shadow/
shadow/data/command/
shadow/data/help/
shadow/data/religion/
shadow/data/skill/
/****************************************************************************
*  Automated Quest code written by Vassago of MOONGATE, moongate.ams.com    
*
*  4000. Copyright (c) 1996 Ryan Addams, All Rights Reserved. Use of this   
*
*  code is allowed provided you add a credit line to the effect of:         
*
*  "Quest Code (c) 1996 Ryan Addams" to your logon screen with the rest     
*
*  of the standard diku/rom credits. If you use this or a modified version  
*
*  of this code, let me know via email: moongate@moongate.ams.com. Further  
*
*  updates will be posted to the rom mailing list. If you'd like to get     
*
*  the latest version of quest.c, please send a request to the above add-   
*
*  ress. Quest Code v2.03. Please do not remove this notice from this file. 
****************************************************************************/

/****************************************************************************
 * Updated Quest Code copyright 1999-2001                                   
 * Markanth : markanth@spaceservices.net                                    
 * Devil's Lament : spaceservices.net port 3778                             
 * Web Page : http://spaceservices.net/~markanth/                           
 * Quest objects are now updated by owner's level, code is more dynamic.    
 * All I ask in return is that you give me credit on your mud somewhere     
 * or email me if you use it.                                               
***************************************************************************/

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "merc.h"
#include "const.h"
DECLARE_DO_FUN( do_restore );
DECLARE_DO_FUN( do_transfer );
DECLARE_SPELL_FUN( spell_identify );


extern int mobile_count;

void do_function args((CHAR_DATA *ch, DO_FUN *do_fun, char *argument));


struct quest_type 
{
    char *name;
    char *who_name;
    int vnum;
    int cost;
};

#define QUEST_ARMS 46
#define QUEST_AURA 234
#define QUEST_LEGGINGS 233
#define QUEST_SWORD 213
#define QUEST_BPLATE 212
#define QUEST_BOOTS 44
#define QUEST_MASK 211
#define QUEST_GLOVES 224
#define QUEST_FLAME 232
#define QUEST_HELM 55
#define QUEST_WINGS 52
#define QUEST_BAG 230
#define QUEST_SHIELD 63
#define QUEST_REGEN 227
#define QUEST_INVIS 43
#define QUEST_DEED 216
#define QUEST_DAGGER 99
#define QUEST_DECANT 45
#define QUEST_TRIVIA    OBJ_VNUM_TRIVIA_PILL
#define OBJ_VNUM_TRIVIA_PILL 215

/*
 * Object vnums for Quest Rewards 
 */
const struct quest_type quest_table[] = {
    {"nohunger", "{YNo Hunger/Thirst (quest buy nohunger){x", 0, 3000},
    {"aura", "{YAura{W of {RSanctuary{x", QUEST_AURA, 2600},
    {"leggings", "{YThe{W leggings of the {RGods{x", QUEST_LEGGINGS, 2600},
    {"sword", "{YSword{W of the {RAncients{x", QUEST_SWORD, 2500},
    {"breastplate", "{YBreastPlate{W of the {RAncients{x", QUEST_BPLATE, 2500},
    {"boots", "{YBoots{W of {RHell{x", QUEST_BOOTS, 2500},
    {"mask", "{YMask{W of the {RAncients{x", QUEST_MASK, 2500},
    {"gloves", "{YGloves{W of {RProtection{x", QUEST_GLOVES, 2500},
    {"flame", "{YFlame{W of the {RAncients{x", QUEST_FLAME, 2500},
    {"helm", "{YHelm{W of {RCaptured Inferno{x", QUEST_HELM, 2300},
    {"wings", "{YWings{W of the {RPegasus{x", QUEST_WINGS, 2000},
    {"bag", "{YBag{W of the {RAncients{x", QUEST_BAG, 1000},
    {"shield", "{YShield{W of {RThought{x", QUEST_SHIELD, 1500},
    {"regeneration", "{YRing{W of {RRegeneration{x", QUEST_REGEN, 700},
    {"invisibility", "{YCloak{W of {RInvisibility{x", QUEST_INVIS, 500},
    {"dagger", "{GDagger of Acidx", QUEST_DAGGER, 1500},
    {"deed", "{WAn {YOfficial {RDeed{x", QUEST_DEED, 500},
    {"decanter", "{CDecanter of Endless Water{x", QUEST_DECANT, 500},
    {"shoulder", "{CPads with diamond spiked tips{x", QUEST_ARMS, 2000},
    {"trivia", "{YTrivia {RPill{x", QUEST_TRIVIA, 100},
    {"frequent", "{YFrequent {RQuestor{x", 1, 1000 },
    {"levelflux", "{YLevelflux{x", 2, 10000 },
    {"train",     "{Y100 {RTrains{x", 3, 2000 },
    {"practice",  "{Y1000 {RPractices{x", 4, 2000 }, 
    {"dblquest", "{YDouble {RQuest{x", 5, 5000 },
    {NULL, 0, 0}
};

/* Quests to find objects */
#define QUEST_OBJQUEST1 71
#define QUEST_OBJQUEST2 78
#define QUEST_OBJQUEST3 82
#define QUEST_OBJQUEST4 92

/*
 * CHANCE function. I use this everywhere in my code, very handy :> 
 */
bool chance(int num)
{
    if (number_range(1, 100) <= num)
	return TRUE;
    else
	return FALSE;
}

/* is object in quest table? */
int is_qobj(OBJ_DATA *obj)
{
    int i;

    if (!obj || !obj->pIndexData)
	return -1;

    for (i = 0; quest_table[i].name != NULL; i++)
    {
	if (obj->pIndexData->vnum == quest_table[i].vnum)
	    return i;
    }
    return -1;
}

int qobj_cost(OBJ_DATA * obj)
{
    int i;

    if (!obj || !obj->pIndexData)
	return 0;

    for (i = 0; quest_table[i].name != NULL; i++)
    {
	if (obj->pIndexData->vnum == quest_table[i].vnum)
	    return quest_table[i].cost;
    }
    return 0;
}

/*
 * * Add or enhance an obj affect. 
 */
void affect_join_obj(OBJ_DATA * obj, AFFECT_DATA * paf)
{
    AFFECT_DATA *paf_old;
    bool found;

    found = FALSE;
    for (paf_old = obj->affected; paf_old != NULL; paf_old = paf_old->next)
    {
	if (paf_old->location == paf->location)
	{
	    paf_old->level = paf->level;
	    paf_old->modifier = paf->modifier;
	    found = TRUE;
	}
    }
    if (!found)
	affect_to_obj(obj, paf);
    return;
}

void add_apply(OBJ_DATA * obj, int loc, int mod, int where, int type, int dur,int vector, int level)
{
    AFFECT_DATA pAf;

    if (obj == NULL)
	return;

    if (!obj->enchanted)
	affect_enchant(obj);

    pAf.location = loc;
    pAf.modifier = mod;
    pAf.where = where;
    pAf.type = type;
    pAf.duration = dur;
    pAf.bitvector = vector;
    pAf.level = level;
    affect_join_obj(obj, &pAf);

    return;
}

/* Updates a quest object.  AFFECTS like sanctuary ect..
   are done in game or on area file. (Anything not level based) */
void update_questobjs(CHAR_DATA * ch, OBJ_DATA * obj)
{
    int bonus,
     pbonus,
     cost;

    if (obj == NULL || obj->pIndexData == NULL)
    {
	bug("update_questobjs: NULL obj", 0);
	return;
    }
    if (ch == NULL)
    {
	bug("update_questobjs: NULL ch", 0);
	return;
    }

    if (!IS_OBJ_STAT(obj, ITEM_QUEST) && is_qobj(obj) == -1)
	return;

    bonus = UMAX(5, ch->level / 10);
    pbonus = UMAX(5, ch->level / 5);
    cost = qobj_cost(obj);

    if (obj->level != ch->level)
	obj->level = ch->level;
    if (obj->condition != -1)
	obj->condition = -1;
    if (obj->cost != cost)
	obj->cost = cost;
    if (!CAN_WEAR(obj, ITEM_NO_SAC))
	SET_BIT(obj->wear_flags, ITEM_NO_SAC);
    if (!IS_OBJ_STAT(obj, ITEM_BURN_PROOF))
	SET_BIT(obj->extra_flags, ITEM_BURN_PROOF);
    if (!IS_OBJ_STAT(obj, ITEM_NODROP))
	SET_BIT(obj->extra_flags, ITEM_NODROP);

	/* Bonuses could get fun here */
    if (obj->pIndexData->vnum == QUEST_BPLATE)
    {
	add_apply(obj, APPLY_DAMROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_HITROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
    } else if (obj->pIndexData->vnum == QUEST_SHIELD)
    {
	add_apply(obj, APPLY_DAMROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_HITROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
    } else if (obj->pIndexData->vnum == QUEST_LEGGINGS)
    {
	add_apply(obj, APPLY_DAMROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_HITROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_HIT, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_MANA, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_MOVE, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
    } else if (obj->pIndexData->vnum == QUEST_MASK)
    {
	add_apply(obj, APPLY_DAMROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_HITROLL, pbonus, TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_HIT, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_MANA, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_MOVE, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
    } else if (obj->pIndexData->vnum == QUEST_WINGS)
    {
	add_apply(obj, APPLY_HIT, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_MANA, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_MOVE, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
    } else if (obj->pIndexData->vnum == QUEST_AURA)
    {
	add_apply(obj, APPLY_HIT, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_MANA, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_MOVE, UMAX(50, ch->level), TO_OBJECT, 0, -1, 0, ch->level);
    } else if (obj->item_type == ITEM_CONTAINER)
    {
    /* weight modifiers */
	obj->weight = -1 * (50 + (ch->level * 1.5));
	obj->value[0] = 1000 + (20 * ch->level);
	obj->value[3] = 1000 + (20 * ch->level);
    }
    if (obj->item_type == ITEM_WEAPON)
    {
    /* weapon values of levelD5 */
	obj->value[1] = UMAX(15, ( ch->level + 20 ) /5);
	obj->value[2] = ch->level < 80 ? 4 : 5;
	add_apply(obj, APPLY_DAMROLL, bonus, TO_OBJECT, 0, -1, 0, ch->level);
	add_apply(obj, APPLY_HITROLL, bonus, TO_OBJECT, 0, -1, 0, ch->level);
    } else if (obj->item_type == ITEM_ARMOR)
    {
    /* AC value of player level */
	obj->value[0] = UMAX(20, ch->level);
	obj->value[1] = UMAX(20, ch->level);
	obj->value[2] = UMAX(20, ch->level);
	obj->value[3] = (5 * UMAX(20, ch->level)) / 6;
    } else if (obj->item_type == ITEM_STAFF)
	obj->value[0] = UMAX(40, ch->level / 3);
    return;
}

/* Usage info on the QUEST commands*/
/* Keep this in line with the do_quest function's keywords */
void quest_usage(CHAR_DATA * ch)
{
    send_to_char("{GQUEST commands: POINTS, INFO, TIME, REQUEST, COMPLETE, LIST,\n\r" 
		   "                BUY, QUIT, SELL, REPLACE, IDENTIFY, CONVERT.\n\r"
                   "                CONVERTALL, REVERT, REVERTALL DBLQUEST{x\n\r",ch );
	if(IS_IMMORTAL(ch))
	send_to_char("{GQUEST RESET (player): resets players quest.{x\n\r", ch);
    send_to_char("{GFor more information, type 'HELP QUEST'.{x\n\r", ch);
    return;
}

/* Obtain additional location information about sought item/mob */
void quest_where(CHAR_DATA * ch, char *what)
{
    char buf[MAX_INPUT_LENGTH];
    ROOM_INDEX_DATA *room;

    if(!ch || IS_NPC(ch))
    return;

    if (ch->pcdata->questloc <= 0)
    {
	bug("QUEST INFO: ch->questloc = %ld", ch->pcdata->questloc);
	return;
    }
    if (ch->in_room == NULL)
	return;

    room = get_room_index(ch->pcdata->questloc);
    if (room->area == NULL)
    {
	bug("QUEST INFO: room(%ld)->area == NULL", ch->pcdata->questloc);
	return;
    }
    if (room->area->name == NULL)
    {
	bug("QUEST INFO: area->name == NULL", 0);
	return;
    }
    sprintf(buf, "{RRumor has it this %s was last seen in the area known as %s,{x\n\r", what, room->area->name);
    send_to_char(buf, ch);
    if (room->name == NULL)
    {
	bug("QUEST INFO: room(%ld)->name == NULL", ch->pcdata->questloc);
	return;
    }
    sprintf(buf, "{Rnear %s.{x\n\r", room->name);
    send_to_char(buf, ch);

}				/* end quest_where() */

OBJ_DATA *has_questobj(CHAR_DATA * ch)
{
    OBJ_DATA *obj;
    OBJ_INDEX_DATA *pObj;

    if(!ch || IS_NPC(ch) || ch->pcdata->questobj <= 0)
    return NULL;

    if ((pObj = get_obj_index(ch->pcdata->questobj)) == NULL)
	return NULL;

    for (obj = ch->carrying; obj != NULL; obj = obj->next_content)
	if (obj != NULL && obj->pIndexData == pObj)
	    return obj;

    return NULL;
}

/*
 * The main quest function 
 */
void do_quest(CHAR_DATA * ch, char *argument)
{
    CHAR_DATA *questman;
    OBJ_DATA *obj = NULL;
    OBJ_INDEX_DATA *questinfoobj;
    MOB_INDEX_DATA *questinfo;
    char buf[MSL];
    char arg1[MIL];
    char arg2[MIL];
    int i = 0;

    if (IS_NPC(ch))
    {
	send_to_char("I'm sorry, you can't quest.\n\r", ch);
	return;
    }

    argument = one_argument(argument, arg1);
    argument = one_argument(argument, arg2);

    if (arg1[0] == '\0')
    {
	quest_usage(ch);
	return;
    }

    if (!strcmp(arg1, "info"))
    {
	MOB_INDEX_DATA *qm_mobindex;

	if (!IS_QUESTOR(ch))
	{
	    send_to_char("You aren't currently on a quest.\n\r", ch);
	    return;
	}
	send_to_char("\n\r", ch);
	if (ch->pcdata->questgiver <= 0)
	{
	    bug("QUEST INFO: quest giver = %d", ch->pcdata->questgiver);
	    send_to_char("{RIt seems your quest master died of old age waiting for you.{x\n\r", ch);
	    end_quest(ch, 10);
	    return;
	}
	qm_mobindex = get_mob_index(ch->pcdata->questgiver);
	if (qm_mobindex == NULL)
	{
	    bug("QUEST INFO: quest giver %d has no MOB_INDEX_DATA!", ch->pcdata->questgiver);
	    send_to_char("{RYour quest master has fallen very ill. Please contact an imm!{x\n\r", ch);
	    end_quest(ch, 10);
	    return;
	}
	if (ch->pcdata->questmob == -1 || has_questobj(ch))	/* killed target mob */
	{
	    send_to_char("{RYour quest is {fALMOST{x{R complete!{x\n\r", ch);
	    sprintf(buf, "{RGet back to %s{R before your time runs out!{x\n\r", (qm_mobindex->short_descr == NULL ? "your quest master" : qm_mobindex->short_descr));
	    send_to_char(buf, ch);
	    return;
	} else if (ch->pcdata->questobj > 0)	/* questing for an object 
*/
	{
	    questinfoobj = get_obj_index(ch->pcdata->questobj);
	    if (questinfoobj != NULL)
	    {
		sprintf(buf, "{RYou recall the quest which the %s{R gave you.{x\n\r", (qm_mobindex->short_descr == NULL ? "your quest master" : qm_mobindex->short_descr));
		send_to_char(buf, ch);
		sprintf(buf, "{RYou are on a quest to recover the fabled %s{R!{x\n\r", questinfoobj->name);
		send_to_char(buf, ch);
		quest_where(ch, "treasure");
		return;
	    }
	    /* quest object not found! */
	    bug("No info for quest object %d", ch->pcdata->questobj);
	    ch->pcdata->questobj = 0;
	    REMOVE_BIT(ch->act, PLR_QUESTOR);
	    /* no RETURN -- fall thru to 'no quest', below */
	} else if (ch->pcdata->questmob > 0)	/* questing for a mob */
	{
	    questinfo = get_mob_index(ch->pcdata->questmob);
	    if (questinfo != NULL)
	    {
		sprintf(buf, "{RYou are on a quest to slay the dreaded %s{R!{x\n\r", questinfo->short_descr);
		send_to_char(buf, ch);
		quest_where(ch, "fiend");
		return;
	    }
	    /* quest mob not found! */
	    bug("No info for quest mob %d", ch->pcdata->questmob);
	    ch->pcdata->questmob = 0;	/* Changed to mob instead of obj - Lotus */
	    REMOVE_BIT(ch->act, PLR_QUESTOR);
	    /* no RETURN -- fall thru to 'no quest', below */
	}
	/* we shouldn't be here */
	bug("QUEST INFO: Questor with no kill, mob or obj", 0);
	return;
    }
    if (!strcmp(arg1, "points"))
    {
	sprintf(buf, "You have %d quest points.\n\r", ch->pcdata->questpoints);
	send_to_char(buf, ch);
	return;
    } else if (!strcmp(arg1, "time"))
    {
	if (!IS_SET(ch->act, PLR_QUESTOR))
	{
	    send_to_char("You aren't currently on a quest.\n\r", ch);
	    if (ch->pcdata->nextquest > 1)
	    {
		sprintf(buf, "There are %d minutes remaining until you can go on another quest.\n\r", ch->pcdata->nextquest);
		send_to_char(buf, ch);
	    } else if (ch->pcdata->nextquest == 1)
	    {
		sprintf(buf, "There is less than a minute remaining until you can go on another quest.\n\r");
		send_to_char(buf, ch);
	    }
	} else if (ch->pcdata->countdown > 0)
	{
	    sprintf(buf, "Time left for current quest: %d\n\r", ch->pcdata->countdown);
	    send_to_char(buf, ch);
	}
	return;
    }

    for (questman = ch->in_room->people; questman != NULL; questman = questman->next_in_room)
    {
	if (!IS_NPC(questman))
	    continue;
	if (questman->spec_fun == spec_lookup("spec_questmaster"))
	    break;
    }

    if (questman == NULL || questman->spec_fun != spec_lookup("spec_questmaster"))
    {
	send_to_char("You can't do that here.\n\r", ch);
	return;
    }

    if (questman->fighting != NULL)
    {
	send_to_char("Wait until the fighting stops.\n\r", ch);
	return;
    }

    if (!strcmp(arg1, "list"))
    {
	act("$n asks $N for a list of quest items.", ch, NULL, questman, TO_ROOM);
	send_to_char("\t{YCurrent Quest Items available for Purchase:{x\n\r", ch);
        for (i = 0; quest_table[i].who_name != NULL; i++)
        {
	    sprintf(buf, "\t%-5dqp ........ %s{x\n\r", quest_table[i].cost, quest_table[i].who_name);
   		send_to_char(buf, ch);
    }
	send_to_char("\tTo buy an item, type 'QUEST BUY <item>'.\n\r", ch);
	send_to_char("\tFor more info on quest items type 'help questitems'\n\r", ch);
	return;
        }

    else if (!strcmp(arg1, "buy"))
    {

	if (arg2[0] == '\0')
	{
	    send_to_char("To buy an item, type 'QUEST BUY <item>'.\n\r", ch);
	    return;
	}

	for (i = 0; quest_table[i].name != NULL; i++)
	{
	    if (is_name(arg2, quest_table[i].name))
	    {
		if (ch->pcdata->questpoints >= quest_table[i].cost)
		{
		    if (quest_table[i].vnum == 0)
		    {
			ch->pcdata->questpoints -= quest_table[i].cost;
			ch->pcdata->condition[COND_FULL] = -1;
			ch->pcdata->condition[COND_THIRST] = -1;
			act("$N calls upon the power of the gods to relieve your mortal burdens.", ch, NULL, questman, TO_CHAR);
			act("$N calls upon the power of the gods to relieve $n's mortal burdens.", ch, NULL, questman, TO_ROOM);
			return;
		    }

                    if (quest_table[i].vnum == 1)
                    {
                        ch->pcdata->questpoints -= quest_table[i].cost;
                        SET_BIT( ch->act, PLR_FREQUENT );
                        send_to_char( "You are now a considered a frequent questor, you will have to wait less time between quests.\n\r", ch ); 
                        act("$N now recognizes $n as a frequent questor!", ch, NULL, questman, TO_ROOM );
                        return;
                    }

                    if ( ch->levelflux < 6 )
                    {
                        if (quest_table[i].vnum == 2 )
                        {
                            ch->pcdata->questpoints -= quest_table[i].cost;
                            ch->levelflux++;
                            printf_to_char( ch, "Your level flux has been raised to %d!\n\r", ch->levelflux );
                            return;
                        }

                   }
              
                   if (quest_table[i].vnum == 3 )
                   {
                       ch->pcdata->questpoints -= quest_table[i].cost;
                       ch->train += 100;
                       send_to_char( "The questmaster gives you 100 trains.\n\r", ch );
                       return;
                   }

                   if (quest_table[i].vnum == 4)
                   {
                       ch->pcdata->questpoints -= quest_table[i].cost;
                       ch->practice += 1000;
                       send_to_char( "The questmaster gives you 1000 practices.\n\r", ch );
                       return;
                   }

                   if (quest_table[i].vnum == 5)
                   {
                       ch->pcdata->questpoints -= quest_table[i].cost;
                       ch->pcdata->dblq = 1;
                       send_to_char( "The questmaster promises to give you double questpoints for every quest you complete from now on.\n\r", ch );
                       return;
                   }

                   else if ((obj = create_object(get_obj_index(quest_table[i].vnum), ch->level)) == NULL)
	           {
		       send_to_char("That object could not be found, contact an immortal.\n\r", ch);
		       return;
		   }

                   else
 	           {
			ch->pcdata->questpoints -= quest_table[i].cost;
			if (!IS_IMMORTAL(ch))
			{
			    sprintf(buf, "Bought a %s (%d) for %d questpoints.", quest_table[i].name, quest_table[i].vnum, quest_table[i].cost);
			    append_file(ch, QUEST_FILE, buf);
			}
		   }

		    if (!IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST))
		    {
			SET_BIT(obj->pIndexData->extra_flags, ITEM_QUEST);
			SET_BIT(obj->extra_flags, ITEM_QUEST);
			SET_BIT(obj->pIndexData->area->area_flags, AREA_CHANGED);
		    }
		    act("$N gives $p to $n.", ch, obj, questman, TO_ROOM);
		    act("$N gives you $p.", ch, obj, questman, TO_CHAR);
		    obj_to_char(obj, ch);
		    save_char_obj(ch);
		    return;
		}
 
                else
		{
		    sprintf(buf, "Sorry, %s, but you need %d quest points for that.", ch->name, quest_table[i].cost);
		    do_mob_tell(ch, questman, buf);
		    return;
		}
	    }
	}

	sprintf(buf, "I don't have that item, %s.", ch->name);
	do_mob_tell(ch, questman, buf);
	return;
    }

    else if (!strcmp(arg1, "sell"))
    {
	if (arg2[0] == '\0')
	{
	    send_to_char("To sell an item, type 'QUEST SELL <item>'.\n\r", ch);
	    return;
	}
	if ((obj = get_obj_carry(ch, arg2, ch)) == NULL)
	{
	    send_to_char("Which item is that?\n\r", ch);
	    return;
	}

	if (!IS_OBJ_STAT(obj, ITEM_QUEST))
	{
	    sprintf(buf, "That is not a quest item, %s.", ch->name);
	    do_mob_tell(ch, questman, buf);
	    return;
	}

	for (i = 0; quest_table[i].name != NULL; i++)
	{
	    if (quest_table[i].vnum <= 0)
		continue;
	    if (quest_table[i].vnum == obj->pIndexData->vnum)
	    {
		ch->pcdata->questpoints += quest_table[i].cost / 3;
		act("$N takes $p from $n.", ch, obj, questman, TO_ROOM);
		sprintf(buf, "$N takes $p from you for %d quest points.", quest_table[i].cost / 3);
		act(buf, ch, obj, questman, TO_CHAR);
		extract_obj(obj);
		save_char_obj(ch);
		return;
	    }
	}
	sprintf(buf, "I only take items I sell, %s.", ch->name);
	do_mob_tell(ch, questman, buf);
	return;
	}
	else if (!strcmp(arg1, "reset"))
	{
		CHAR_DATA *victim;

		if(!IS_IMMORTAL(ch))
		{
			quest_usage(ch);
			return;
		}

		if (IS_NULLSTR(arg2))
		{
		send_to_char("Syntax: quest reset <player>\n\r", ch);
		return;
		}

		if ((victim = get_char_world(ch, arg2)) == NULL)
		{
		send_to_char("They aren't here.\n\r", ch);
		return;
		}

		if (IS_NPC(victim))
		{
		send_to_char("Mobs dont quest.\n\r", ch);
		return;
		}

		end_quest(victim, 0);

		if(victim != ch)
			send_to_char("You clear thier quest.\n\r", ch);
		sprintf(buf, "%s has cleared your quest.\n\r", PERS(ch, victim));
		send_to_char(buf, ch);
		return;
    } else if (!strcmp(arg1, "replace"))
    {
	if (arg2[0] == '\0')
	{
	    send_to_char("To replace an item, type 'QUEST REPLACE <item>'.\n\r", ch);
	    return;
	}
	if ((obj = get_obj_carry(ch, arg2, ch)) == NULL)
	{
	    send_to_char("Which item is that?\n\r", ch);
	    return;
	}

	if (!IS_OBJ_STAT(obj, ITEM_QUEST))
	{
	    sprintf(buf, "That is not a quest item, %s.", ch->name);
	    do_mob_tell(ch, questman, buf);
	    return;
	}
	if (ch->pcdata->questpoints < 75)
	{
	    do_mob_tell(ch, questman, "It costs 75 questpoints to replace an item.");
	    return;
	}
	for (i = 0; quest_table[i].name != NULL; i++)
	{
	    if (quest_table[i].vnum <= 0)
		continue;
	    if (quest_table[i].vnum == obj->pIndexData->vnum)
	    {
		OBJ_DATA *newobj;

		if ((newobj = create_object(get_obj_index(quest_table[i].vnum), ch->level)) == NULL)
		{
		    sprintf(buf, "I could not find a new quest item for you %s.", ch->name);
		    do_mob_tell(ch, questman, buf);
		} else
		{
		    obj_to_char(newobj, ch);
            extract_obj(obj);
            ch->pcdata->questpoints -= 75;
            act("$N takes $p from $n and gives $m a new one.", ch, obj, questman, TO_ROOM);
            act("$N replaces $p with a new one for 75 quest points.", ch, obj, questman, TO_CHAR);
            save_char_obj(ch);
		}
		return;
	    }
	}
	sprintf(buf, "I only replace items I sell, %s.", ch->name);
	do_mob_tell(ch, questman, buf);
	return;
    }

    else if (!strcmp(arg1, "identify"))
    {

	if (IS_NULLSTR(arg2))
	{
	    printf_to_char(ch,"To identify an item, type 'QUEST IDENTIFY <item>'.\n\r");
	    return;
	}

	for (i = 0; quest_table[i].name != NULL; i++)
	{
	    if (is_name(arg2, quest_table[i].name))
	    {
		if (quest_table[i].vnum == 0)
		{
		    send_to_char("That isn't a quest item.\n\r", ch);
		    return;
		} else if ((obj = create_object(get_obj_index(quest_table[i].vnum), ch->level)) == NULL)
		{
		    send_to_char("That object could not be found, contact an immortal.\n\r", ch);
		    return;
		} else
		{
		    if (!IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST))
		    {
			SET_BIT(obj->pIndexData->extra_flags, ITEM_QUEST);
			SET_BIT(obj->extra_flags, ITEM_QUEST);
			SET_BIT(obj->pIndexData->area->area_flags, AREA_CHANGED);
		    }
		    obj_to_char(obj, ch);
		    sprintf(buf, "%s costs %d questpoints.", obj->short_descr, quest_table[i].cost);
			send_to_char(buf, ch);
		    spell_identify(0, ch->level, ch, obj, TAR_OBJ_INV);
		    extract_obj(obj);
		    return;
		}
	    }
	}
	sprintf(buf, "I don't have that item, %s.", ch->name);
	do_mob_tell(ch, questman, buf);
	return;
    }

    else if (!strcmp(arg1, "request"))
    {
	act("$n asks $N for a quest.", ch, NULL, questman, TO_ROOM);
	act("You ask $N for a quest.", ch, NULL, questman, TO_CHAR);
	if (IS_SET(ch->act, PLR_QUESTOR) || ON_GQUEST(ch)) 
	{
	    do_mob_tell(ch, questman, "But you're already on a quest!");
	    return;
	}
	if (ch->pcdata->nextquest > 0)
	{
	    sprintf(buf, "You're very brave, %s, but let someone else have a chance.", ch->name);
	    do_mob_tell(ch, questman, buf);
	    do_mob_tell(ch, questman, "Come back later.");
	    return;
	}

	sprintf(buf, "Thank you, brave %s!", ch->name);
	do_mob_tell(ch, questman, buf);
	ch->pcdata->questmob = 0;
	ch->pcdata->questobj = 0;

	generate_quest(ch, questman);
	return;
    } 
    else if (!strcmp(arg1, "complete"))
    {
	if (ch->pcdata->questgiver != questman->pIndexData->vnum)
	{
	    do_mob_tell(ch, questman, "I never sent you on a quest! Perhaps you're thinking of someone else.");
	    return;
	}

	if (IS_SET(ch->act, PLR_QUESTOR))
	{
	    int reward, points;
            int gold, quest, train, practice, silver;

	    if (ch->pcdata->questmob == -1 && ch->pcdata->countdown > 0)
	    {
		reward = number_range(125, 375);
		points = number_range(25, 70);
		act("$n informs $N $e has completed $s quest.", ch, NULL, questman, TO_ROOM);
		act("You inform $N you have completed $s quest.", ch, NULL, questman, TO_CHAR);

		do_mob_tell(ch, questman, "Congratulations on completing your quest!");

		sprintf(buf, "As a reward, I am giving you %d quest points, and %d gold.", points, reward);
		do_mob_tell(ch, questman, buf);

		if (chance(points/5))
		{
		    send_to_char("You gain an extra {YTrivia {RPoint{x!\n\r", ch);
		    ch->pcdata->trivia += 1;
		}

                if ( ch->pcdata->countdown >= 8 ) 
                {
                    
                    switch ( number_range( 0, 4 ) )
                    {
                        default:
                        quest = number_range( 10, 25 );
                        printf_to_char( ch, "{cAs a reward for completing your quest so fast i'm giving you {C%d {cquestpoints!\n\r", quest ); 
                        ch->pcdata->questpoints += quest;
                        break;
                        
                        case 0:
                        gold = number_range( 80, 200  );
                        printf_to_char( ch, "{yAs a reward for completing your quest so fast i'm giving {Y%d {ygold!{x\n\r", gold );
                        ch->gold += gold;
                        break;
                    
                        case 1:
                        quest = number_range( 10, 25 );
                        printf_to_char( ch, "{cAs a reward for completing your quest so fast i'm giving you {C%d {cquestpoints!\n\r", quest ); 
                        ch->pcdata->questpoints += quest;
                        break;
              
                        case 2:
                        train = number_range( 2, 8 );
                        printf_to_char( ch, "{bAs a reward for completing your quest so fast i'm giving you {B%d {btrains!{x\n\r", train );
                        ch->train += train;
                        break;
                      
                        case 3:
                        practice = number_range( 5, 10 );
                        printf_to_char( ch, "{rAs a reward for completing your quest so fast i'm giving you {R%d {rpractices!{x\n\r", practice );
                        ch->practice += practice;
                        break;
           
                        case 4:
                        silver = number_range( 250, 500 );
                        printf_to_char( ch, "{gAs a reward for completing your quest so fast i'm giving you {G%d {gsilver{x\n\r", silver );
                        ch->silver += silver;
                        break;                         
                    }
                }

                if ( IS_SET( ch->act, PLR_FREQUENT ) )
                end_quest( ch, 5 );
                else
		end_quest(ch, 10);
                if ( ch->pcdata->dblq == 1 )
                reward *= 2;
		ch->gold += reward;
                if ( ch->pcdata->dblq == 1 || double_qp )
                points *= 2;
		ch->pcdata->questpoints += points;
		save_char_obj(ch);
		return;
	    } 
            else if (ch->pcdata->questobj > 0 && ch->pcdata->countdown > 0)
	    {
		if ((obj = has_questobj(ch)) != NULL)
		{
		    reward = number_range(125, 375);
		    points = number_range(25, 75);
		    act("$n informs $N $e has completed $s quest.", ch, NULL, questman, TO_ROOM);
		    act("You inform $N you have completed $s quest.", ch, NULL, questman, TO_CHAR);

		    act("You hand $p to $N.", ch, obj, questman, TO_CHAR);
		    act("$n hands $p to $N.", ch, obj, questman, TO_ROOM);

		    do_mob_tell(ch, questman, "Congratulations on completing your quest!");
		    sprintf(buf, "As a reward, I am giving you %d quest points, and %d gold.", points, reward);
		    do_mob_tell(ch, questman, buf);

                    if ( ch->pcdata->countdown >= 8 ) 
                    {
                        switch ( number_range( 0, 4 ) )
                        {
                            default:
                            quest = number_range( 10, 25 );   
                            printf_to_char( ch, "{cAs a reward for completing your quest so fast i'm giving you {C%d {cquestpoints!\n\r", quest ); 
                            ch->pcdata->questpoints += quest;
                            break;

                            case 0:
                            gold = number_range( 80, 200  );
                            printf_to_char( ch, "{yAs a reward for completing your quest so fast i'm giving {Y%d {ygold!{x\n\r", gold );
                            ch->gold += gold;
                            break;
                    
                            case 1:
                            quest = number_range( 10, 25 );
                            printf_to_char( ch, "{cAs a reward for completing your quest so fast i'm giving you {C%d {cquestpoints!\n\r", quest ); 
                            ch->pcdata->questpoints += quest;
                            break;
              
                            case 2:
                            train = number_range( 2, 8 );
                            printf_to_char( ch, "{bAs a reward for completing your quest so fast i'm giving you {B%d {btrains!{x\n\r", train );
                            ch->train += train;
                            break;
                      
                            case 3:
                            practice = number_range( 5, 10 );
                            printf_to_char( ch, "{rAs a reward for completing your quest so fast i'm giving you {R%d {rpractices!{x\n\r", practice );
                            ch->practice += practice;
                            break;
             
                            case 4:
                            silver = number_range( 250, 500 );
                            printf_to_char( ch, "{gAs a reward for completing your quest so fast i'm giving you {G%d {gsilver{x\n\r", silver );
                            ch->silver += silver;
                            break;                         
                        }
                    }

		    if (chance(points/5))
		    {
			send_to_char("You gain an extra {YTrivia {RPoint{x!\n\r", ch);
			ch->pcdata->trivia += 1;
		    }

                    if ( IS_SET( ch->act, PLR_FREQUENT ) )
                    end_quest(ch, 5 );
                    else
		    end_quest(ch, 10);
                 
                    if ( ch->pcdata->dblq == 1 )
                    reward *= 2;
		    ch->gold += reward;
                    if ( ch->pcdata->dblq == 1 || double_qp )
                    points *= 2;
		    ch->pcdata->questpoints += points;
		    extract_obj(obj);
		    save_char_obj(ch);
		    return;
		} 
                else
		{
		    do_mob_tell(ch, questman, "You haven't completed the quest yet, but there is still time!");
		    return;
		}
		return;
	    } 
            else if ((ch->pcdata->questmob > 0 || ch->pcdata->questobj > 0) && ch->pcdata->countdown > 0)
	    {
		do_mob_tell(ch, questman, "You haven't completed the quest yet, but there is still time!");
		return;
	    }
	}
	if (ch->pcdata->nextquest > 0)
	    sprintf(buf, "But you didn't complete your quest in time!");
	else
	    sprintf(buf, "You have to REQUEST a quest first, %s.", ch->name);
	do_mob_tell(ch, questman, buf);
	return;
    }

    else if (!strcmp(arg1, "quit") || !strcmp(arg1, "fail"))
    {
	act("$n informs $N $e wishes to quit $s quest.", ch, NULL, questman, TO_ROOM);
	act("You inform $N you wish to quit $s quest.", ch, NULL, questman, TO_CHAR);
	if (ch->pcdata->questgiver != questman->pIndexData->vnum)
	{
	    do_mob_tell(ch, questman, "I never sent you on a quest! Perhaps you're thinking of someone else.");
	    return;
	}

	if (IS_SET(ch->act, PLR_QUESTOR))
	{
            if ( IS_SET( ch->act, PLR_FREQUENT ) )
	    end_quest(ch, 10);
            else
            end_quest(ch, 15);
	    do_mob_tell(ch, questman, "Your quest is over, but for your cowardly behavior, you may not quest again for 15 minutes.");
	    return;
	} else
	{
	    send_to_char("You aren't on a quest!", ch);
	    return;
	}
    }
   
    else if (!str_cmp(arg1, "convert"))
    {
        if(ch->pcdata->qps <= 0)
        {
           send_to_char("You don't have any iquest points to convert.\n\r",ch);
           return;
        }

     ch->pcdata->qps--;
     ch->pcdata->questpoints += 100;
     send_to_char("One immortal questpoint converted into 100 atomautic questpoints.\n\r",ch);
     return;
    }
  
    else if (!str_cmp(arg1, "convertall"))
    {
        if ( ch->pcdata->qps <= 0 )
        {
            send_to_char( "I'm sorry you don't have enough immortal questpoints!\n\r", ch );
            return;
        }

        if ( ch->pcdata->qps >= 500 )
        {
            send_to_char( "I'm sorry you have to many questpoints to convert at once! 320 is the max!\n\r", ch );
            return;
        }

        ch->pcdata->questpoints += ( 100 * ch->pcdata->qps );
        ch->pcdata->qps = 0;
        send_to_char( "Ok all of your immortal questpoints have been converted to automatic questpoints!\n\r", ch );
        return;
    }

    else if (!str_cmp(arg1,"revert"))
    {

        if(ch->pcdata->questpoints<100)
        {
  	   send_to_char("You don't have enough aquest points to revert.\n\r",ch);
           return;
        }

        ch->pcdata->qps++;
        ch->pcdata->questpoints -= 100;
        send_to_char("100 aquest points reverted into one iquest point.\n\r",ch);
        return;
     }

     else if (!str_cmp(arg1, "revertall"))
     {
         if ( ch->pcdata->questpoints < 100 )
         {
             send_to_char("You don't have enough automatic questpoints to rever.\n\r", ch );
             return;
         }

         ch->pcdata->qps += ( ch->pcdata->questpoints / 100 );
         ch->pcdata->questpoints = 0;
         send_to_char("Reverting all your questpoints into immortal questpoints.\n\r", ch );
         return;
     }

	quest_usage(ch);
	return;
}

#define MAX_QMOB_COUNT mobile_count

void generate_quest(CHAR_DATA * ch, CHAR_DATA * questman)
{
    CHAR_DATA *victim = NULL;
    ROOM_INDEX_DATA *room = NULL;
    CHAR_DATA *mobs[MAX_QMOB_COUNT];
    size_t mob_count;
    OBJ_DATA *questitem = NULL;
    char buf[MSL];
    int mrange;

    /*
     * * find MAX_QMOB_COUNT quest mobs and store their vnums in mob_buf 
     */

    mob_count = 0;
    for (victim = char_list; victim; victim = victim->next)
    {

	if (!IS_NPC(victim)
	    || !quest_level_diff(ch, victim)
	    || (victim->pIndexData == NULL
		|| victim->in_room == NULL
		|| victim->pIndexData->pShop != NULL)
	    || (IS_EVIL(victim) && IS_EVIL(ch) && chance(50))
	    || (IS_GOOD(victim) && IS_GOOD(ch) && chance(50))
	    || victim->pIndexData->vnum < 100
        || victim->in_room->clan > 0
	    || IS_SET(victim->imm_flags, IMM_WEAPON | IMM_MAGIC)
	    || IS_SET(victim->act, ACT_TRAIN | ACT_PRACTICE | 
        ACT_IS_HEALER | ACT_PET | ACT_PET | ACT_GAIN)
        || IS_SET(victim->affected_by, AFF_CHARM)
		|| IS_SET(victim->in_room->room_flags, ROOM_PET_SHOP)
        || questman->pIndexData == victim->pIndexData
        || (IS_SET(victim->act, ACT_SENTINEL) && 
            IS_SET(victim->in_room->room_flags, ROOM_PRIVATE | ROOM_SOLITARY | ROOM_SAFE)))
	    continue;
	mobs[mob_count++] = victim;
	if (mob_count >= (size_t)MAX_QMOB_COUNT)
	    break;
    }

    if (mob_count == 0) /* not likely but just in case */
    {
	do_mob_tell(ch, questman, "I'm sorry, but I don't have any quests for you at this time.");
	do_mob_tell(ch, questman, "Try again later.");
        if ( IS_SET( ch->act, PLR_FREQUENT ) )
	end_quest(ch, 2);
        else
        end_quest(ch, 4);
	return;
    }

    /*
     * at this point the player is sure to get a quest 
     */
    ch->pcdata->questgiver = questman->pIndexData->vnum;
    mrange = number_range(0, mob_count - 1);
    while ((victim = mobs[mrange]) == NULL)
	mrange = number_range(0, mob_count - 1);
    room = victim->in_room;
    ch->pcdata->questloc = room->vnum;

    /* countdown set here so we can use it for object timers */
    ch->pcdata->countdown = number_range(12, 30);

    /*
     * 20% chance it will send the player on a 'recover item' quest.
     */

    if (chance(20))
    {
	long objvnum = 0;

	switch (number_range(0, 3))
	{
	    case 0:
		objvnum = QUEST_OBJQUEST1;
		break;

	    case 1:
		objvnum = QUEST_OBJQUEST2;
		break;

	    case 2:
		objvnum = QUEST_OBJQUEST3;
		break;

	    case 3:
		objvnum = QUEST_OBJQUEST4;
		break;
	}

	questitem = create_object(get_obj_index(objvnum), ch->level);
	obj_to_room(questitem, room);
	REMOVE_BIT(ch->act, PLR_CANLOOT);
	free_string(questitem->owner);
	questitem->owner = str_dup(ch->name);
	questitem->cost = 0;
	questitem->timer = (4 * ch->pcdata->countdown + 10) / 3;
	ch->pcdata->questobj = questitem->pIndexData->vnum;

	switch (number_range(0, 1))
	{
	    default:
	    case 0:
		sprintf(buf, "Vile pilferers have stolen %s from the royal treasury!", questitem->short_descr);
		do_mob_tell(ch, questman, buf);
		do_mob_tell(ch, questman, "My court wizardess, with her magic mirror, has pinpointed its location.");
		break;
	    case 1:
		sprintf(buf, "A powerful wizard has stolen %s for his personal power!", questitem->short_descr);
		do_mob_tell(ch, questman, buf);
		break;
	}

	if (room->name != NULL)
	{
	    sprintf(buf, "Look for %s somewhere in the vicinity of %s!", questitem->short_descr, room->name);
	    do_mob_tell(ch, questman, buf);

	    sprintf(buf, "That location is in the general area of %s.", room->area->name);
	    do_mob_tell(ch, questman, buf);
	}
    }

    /*
     * Quest to kill a mob 
     */

    else
    {
	switch (number_range(0, 3))
	{
	    default:
	    case 0:
		sprintf(buf, "An enemy of mine, %s, is making vile threats against the crown.", victim->short_descr);
		do_mob_tell(ch, questman, buf);
		do_mob_tell(ch, questman, "This threat must be eliminated!");
		break;

	    case 1:
		sprintf(buf, "Thera's most heinous criminal, %s, has escaped from the dungeon!", victim->short_descr);
		do_mob_tell(ch, questman, buf);
		sprintf(buf, "Since the escape, %s has murdered %d civillians!", victim->short_descr, number_range(2, 20));
		do_mob_tell(ch, questman, buf);
		do_mob_tell(ch, questman, "The penalty for this crime is death, and you are to deliver the sentence!");
		break;

	    case 2:
		sprintf(buf, "The Mayor of Midgaard has recently been attacked by %s.  This is an act of war!", victim->short_descr);
		do_mob_tell(ch, questman, buf);
		sprintf(buf, "%s must be severly dealt with for this injustice.", victim->short_descr);
		do_mob_tell(ch, questman, buf);
		break;

	    case 3:
		sprintf(buf, "%s has been stealing valuables from the citizens of Arkham.", victim->short_descr);
		do_mob_tell(ch, questman, buf);
		sprintf(buf, "Make sure that %s never has the chance to steal again.", victim->short_descr);
		do_mob_tell(ch, questman, buf);
		break;

	}

	if (room->name != NULL)
	{
	    sprintf(buf, "Seek %s out somewhere in the vicinity of %s!", victim->short_descr, room->name);
	    do_mob_tell(ch, questman, buf);

	    sprintf(buf, "That location is in the general area of %s.", room->area->name);
	    do_mob_tell(ch, questman, buf);
	}
	ch->pcdata->questmob = victim->pIndexData->vnum;
    }
    if (ch->pcdata->questmob > 0 || ch->pcdata->questobj > 0)
    {
	SET_BIT(ch->act, PLR_QUESTOR);
	sprintf(buf, "You have %d minutes to complete this quest.", ch->pcdata->countdown);
	do_mob_tell(ch, questman, buf);
	do_mob_tell(ch, questman, "May the gods go with you!");
    } else
	end_quest(ch, 2);
    return;
}

bool quest_level_diff(CHAR_DATA * ch, CHAR_DATA * mob)
{
    int bonus = 20;

    if (IS_IMMORTAL(ch))
	return TRUE;
    else if (ch->level > (mob->level + bonus) || ch->level < (mob->level - bonus))
	return FALSE;
    else
	return TRUE;
}

void quest_update(void)
{
    DESCRIPTOR_DATA *d;
    CHAR_DATA *ch;
    char buf [MSL];

    for (d = descriptor_list; d != NULL; d = d->next)
    {
	if (d->connected == CON_PLAYING && (ch = d->original ? d->original : d->character) != NULL)
	{

	    if (ch->pcdata->nextquest > 0)
	    {
		ch->pcdata->nextquest--;
		if (ch->pcdata->nextquest == 0)
		{
		    send_to_char("You may now quest again.\n\r", ch);
		    return;
		}
	    } else if (IS_SET(ch->act, PLR_QUESTOR))
	    {
		if (--ch->pcdata->countdown <= 0)
		{
                    if ( IS_SET( ch->act, PLR_FREQUENT ) )
		    end_quest(ch, 5);
                    else
                    end_quest(ch, 10 );
		    sprintf(buf, "You have run out of time for your quest!\n\rYou may quest again in %d minutes.\n\r", ch->pcdata->nextquest);
	    	    send_to_char(buf, ch);
		}
		if (ch->pcdata->countdown > 0 && ch->pcdata->countdown < 6)
		{
		    send_to_char("Better hurry, you're almost out of time for your quest!\n\r", ch);
		    return;
		}
	    }
	}
    }
    return;
}

void end_quest(CHAR_DATA * ch, int time)
{
    REMOVE_BIT(ch->act, PLR_QUESTOR);
    ch->pcdata->questgiver = 0;
    ch->pcdata->countdown = 0;
    ch->pcdata->questmob = 0;
    ch->pcdata->questobj = 0;
    ch->pcdata->questloc = 0;
    ch->pcdata->nextquest = time;
}

/* handy dandy, victim must be an NPC */
void do_mob_tell(CHAR_DATA * ch, CHAR_DATA * victim, char *argument)
{
    char buf[MSL];

    if (!victim)
	return;

    sprintf(buf, "{R%s tells you '{W%s{R'{x\n\r", victim->short_descr, argument);
    send_to_char(buf, ch);
    return;
}

void do_tpspend(CHAR_DATA * ch, char *argument)
{
    CHAR_DATA *triviamob;
    char buf[MSL];
    char arg1[MIL];
    char arg2[MIL];
    int sav_trust;

    argument = one_argument(argument, arg1);
    strcpy(arg2, argument);

    if(!ch || IS_NPC(ch))
    return;

    if (arg1[0] == '\0')
    {
	send_to_char("Syntax: Tpspend <item>\n\r", ch);
	send_to_char("        Tpspend list\n\r", ch);
	return;
    }

    if (is_name(arg1, "list"))
    {
	send_to_char("{WTrivia Point Options{x\n\r", ch);
	send_to_char("{Ycorpse retrival{W......{R1tp\n\r", ch);
	send_to_char("{Ytransfer{W.............{R1tp\n\r", ch);
	send_to_char("{Yrestore{W..............{R1tp\n\r", ch);
	send_to_char("{Y5 trains{W.............{R1tp\n\r", ch);
	send_to_char("{Y40 practices{W.........{R1tp\n\r", ch);
	send_to_char("{Y75 questpoints{W.......{R1tp\n\r", ch);
	send_to_char("{Y1 Trivia Pill{W........{R1tp\n\r", ch);
	send_to_char("{WSee HELP TRIVIA for important info before buying.{X\n\r", ch);
	send_to_char("{WFor transfers and corpses, you do not have to be at a trivia shop.\n\r", ch);
	return;
    } else if (is_name(arg1, "corpse"))
    {
	if (ch->pcdata->trivia >= 1)
	{
	    OBJ_DATA *obj;
	    int count = 0;

           for (obj = ch->carrying ; obj != NULL ; obj = obj->next_content) 
	    {
		if (is_name(ch->name, obj->owner) && obj->item_type == ITEM_CORPSE_PC)
		{
                    if(obj->in_room) 
	 	    obj_from_room(obj);
                   
                    else if(obj->carried_by)
                    obj_from_char(obj);
          
                    else if(obj->in_obj)
                    obj_from_obj(obj->in_obj);
	            obj->carried_by = ch;
        	    obj_to_room(obj, ch->in_room);
		    count++;
		}
	    }
	    if (count == 0)
		send_to_char("You have no corpses in the game.\n\r", ch);
	    else
	    {
		ch->pcdata->trivia -= 1;
		if (count == 1)
		{
		    send_to_char("Your corpse appears in the room.\n\r", ch);
		    act("$n's corpse appears in the room.", ch, NULL, NULL, TO_ROOM);
		} else
		{
		    send_to_char("All your corpses appear in the room.\n\r", ch);
		    act("All of $n's corpses appear in the room.", ch, NULL, NULL, TO_ROOM);
		}
	    }
	    return;
	} else
	{
	    send_to_char("You don't have enough trivia points for that.", ch);
	    return;
	}
    } else if (is_name(arg1, "transfer"))
    {
	if (ch->pcdata->trivia >= 1)
	{
	    ROOM_INDEX_DATA *oldroom;

	    if (arg2[0] == '\0')
	    {
		send_to_char("Transfer you where? [recall/room name/character name]\n\r", ch);
		return;
	    } else if (!str_cmp(arg2, "recall"))
		sprintf(arg2, "%d", ROOM_VNUM_TEMPLE);

	    oldroom = ch->in_room;
	    sprintf(buf, "self '%s'", arg2);
	    sav_trust = ch->trust;
	    ch->trust = MAX_LEVEL;
	    do_function(ch, &do_transfer, buf);
	    ch->trust = sav_trust;
	    if (oldroom != ch->in_room)
		ch->pcdata->trivia -= 1;
	    else
		send_to_char("Whoops! You were not charged for that transfer.\n\r", ch);
	    return;
	} else
	{
	    send_to_char("You don't have enough trivia points for that.", ch);
	    return;
	}
    }
    for (triviamob = ch->in_room->people; triviamob != NULL; triviamob = triviamob->next_in_room)
    {
	if (!IS_NPC(triviamob))
	    continue;
	if (triviamob->spec_fun == spec_lookup("spec_triviamob"))
	    break;
    }

    if (triviamob == NULL || triviamob->spec_fun != spec_lookup("spec_triviamob"))
    {
	send_to_char("You can't do that here.\n\r", ch);
	return;
    }

    if (triviamob->fighting != NULL)
    {
	send_to_char("Wait until the fighting stops.\n\r", ch);
	return;
    }

    if (is_name(arg1, "practices pracs practice"))
    {
	if (ch->pcdata->trivia >= 1)
	{
	    ch->pcdata->trivia -= 1;
	    ch->practice += 40;
	    act("$N gives 40 practices to $n.", ch, NULL, triviamob, TO_ROOM);
	    act("$N gives you 40 practices.", ch, NULL, triviamob, TO_CHAR);
	    return;
	} else
	{
	    sprintf(buf, "Sorry, %s, but you don't have enough trivia points for that.", ch->name);
	    do_mob_tell(ch, triviamob, buf);
	    return;
	}
    } else if (is_name(arg1, "trains train"))
    {
	if (ch->pcdata->trivia >= 1)
	{
	    ch->pcdata->trivia -= 1;
	    ch->train += 5;
	    act("$N gives 5 training sessions to $n.", ch, NULL, triviamob, TO_ROOM);
	    act("$N gives you 5 training sessions.", ch, NULL, triviamob, TO_CHAR);
	    return;
	} else
	{
	    sprintf(buf, "Sorry, %s, but you don't have enough trivia points for that.", ch->name);
	    do_mob_tell(ch, triviamob, buf);
	    return;
	}
    } else if (is_name(arg1, "questpoints points"))
    {
	if (ch->pcdata->trivia >= 1)
	{
	    ch->pcdata->trivia -= 1;
	    ch->pcdata->questpoints += 75; /* a trivia point costs 100 quest points 25% conversion */
	    act("$N gives 75 questpoints to $n.", ch, NULL, triviamob, TO_ROOM);
	    act("$N gives you 75 questpoints.", ch, NULL, triviamob, TO_CHAR);
	    return;
	} else
	{
	    sprintf(buf, "Sorry, %s, but you don't have enough trivia points for that.", ch->name);
	    do_mob_tell(ch, triviamob, buf);
	    return;
	}
    } else if (is_name(arg1, "pill"))
    {
	OBJ_DATA *obj = NULL;

	if (ch->pcdata->trivia >= 1)
	{
	    obj = create_object(get_obj_index(OBJ_VNUM_TRIVIA_PILL), 1);
	    if (obj != NULL)
	    {
		act("$N gives $p to $n.", ch, obj, triviamob, TO_ROOM);
		act("$N gives you $p.", ch, obj, triviamob, TO_CHAR);
		obj_to_char(obj, ch);
		ch->pcdata->trivia -= 1;
		return;
	    } else
	    {
		sprintf(buf, "I don't any more trivia pills to give, %s.", ch->name);
		do_mob_tell(ch, triviamob, buf);
	    }
	    return;
	}

	else
	{
	    sprintf(buf, "Sorry, %s, but you don't have enough trivia points for that.", ch->name);
	    do_mob_tell(ch, triviamob, buf);
	    return;
	}
    } else if (is_name(arg1, "restore"))
    {
	if (ch->pcdata->trivia >= 1)
	{
        sav_trust = ch->trust;
        ch->trust = MAX_LEVEL;
        do_function(ch, &do_restore, "all");
        ch->trust = sav_trust;
        ch->pcdata->trivia -= 1;
        return;
	} else
	{
	    sprintf(buf, "Sorry, %s, but you don't have enough trivia points for that.", ch->name);
	    do_mob_tell(ch, triviamob, buf);
	    return;
	}
    } else
	do_tpspend(ch, "list");
}

void do_qpgive(CHAR_DATA * ch, char *argument)
{
    CHAR_DATA *victim;
    char arg[MIL],
     buf[MIL];
    int amount;

    if(!ch || IS_NPC(ch))
    return;

    argument = one_argument(argument, arg);

    if (argument[0] == '\0' || !is_number(arg))
    {
	send_to_char("Syntax: qpgive [amount] [person]\n\r", ch);
	return;
    }

    if ((amount = atoi(arg)) <= 0)
    {
	send_to_char("Give how many questpoints?\n\r", ch);
	return;
    }

    if (amount > ch->pcdata->questpoints)
    {
	send_to_char("You don't have that many questpoints to give.\n\r", ch);
	return;
    }

    if ((victim = get_char_room(ch, NULL, argument)) == NULL)
    {
	send_to_char("That person is not here.\n\r", ch);
	return;
    }

    if(IS_NPC(victim))
    {
       send_to_char("NPC's don't need quest points.\n\r", ch);
       return;
    }

    if(victim == ch)
    {
       printf_to_char( ch, "You give yourself %d questpoints..... don't you feel better?\n\r", amount );
       return;
    }

    ch->pcdata->questpoints -= amount;
    victim->pcdata->questpoints += amount;

    sprintf(buf, "%d", amount);
    act("$n gives you $t questpoints.", ch, buf, victim, TO_VICT);
    act("You give $N $t questpoints.", ch, buf, victim, TO_CHAR);
    act("$n gives $N $t questpoints.", ch, buf, victim, TO_ROOM);

    return;
}

void do_tpgive(CHAR_DATA * ch, char *argument)
{
    CHAR_DATA *victim;
    char arg[MIL],
     buf[MIL];
    int amount;

    if(!ch || IS_NPC(ch))
    return;

    argument = one_argument(argument, arg);

    if (argument[0] == '\0' || !is_number(arg))
    {
	send_to_char("Syntax: tpgive [amount] [person]\n\r", ch);
	return;
    }

    if ((amount = atoi(arg)) <= 0)
    {
	send_to_char("Give how many trivia points?\n\r", ch);
	return;
    }

    if (amount > ch->pcdata->trivia)
    {
	send_to_char("You don't have that many trivia points to give!!\n\r", ch);
	return;
    }

    if ((victim = get_char_room(ch, NULL, argument)) == NULL)
    {
	send_to_char("That person is not here.\n\r", ch);
	return;
    }

    if(IS_NPC(victim))
    {
       send_to_char("NPC's don't need trivia points.\n\r", ch);
       return;
    }

    if(victim == ch)
    {
        printf_to_char( ch, "You give yourself %d trivia points...... don't you feel better?\n\r", amount );
        return;
    }

    ch->pcdata->trivia -= amount;
    victim->pcdata->trivia += amount;

    sprintf(buf, "%d", amount);
    act("$n gives you $t trivia points.", ch, buf, victim, TO_VICT);
    act("You give $N $t trivia points.", ch, buf, victim, TO_CHAR);
    act("$n gives $N $t trivia points.", ch, buf, victim, TO_ROOM);

    return;
}


void do_token ( CHAR_DATA *ch, char *argument )
{

   OBJ_DATA *obj=NULL;   
   char buf[MAX_STRING_LENGTH];
   char arg[MAX_STRING_LENGTH];
   int value;

   one_argument( argument, arg );

   if (IS_NPC(ch))
        return;

   if (arg[0] == '\0')
   {
        send_to_char("How many qp should the token be worth?\n\r", ch);
        return;
   }

   if (ch->pcdata->questpoints <= 0 )
   {
        send_to_char("You have no qp left to spend!\n\r", ch);
        return;
   }

   else  
   { 
        value = atoi(arg);

        if (value <= 0 )
        {
           send_to_char("Tokens may only by made for positive amounts.\n\r", ch);
           return;
        }

        else if (value > ch->pcdata->questpoints )
        {
           send_to_char("You don't have that many quest points.\n\r",ch);
           return;
        }

        else
        {
           obj = create_object(get_obj_index(OBJ_VNUM_TOKEN), 0);
           sprintf( buf, obj->short_descr, value );
           free_string( obj->short_descr );
           obj->short_descr = str_dup( buf );
           sprintf( buf, obj->description, value );
           free_string( obj->description );
           obj->description = str_dup( buf );
           obj->value[0]   =    value;
           ch->pcdata->questpoints -=   value;
           obj_to_char(obj,ch);
           act( "You snap your fingers and produce a quest token!", ch, NULL, NULL,TO_CHAR);
           act( "$n snaps $s fingers and produces a quest token!", ch, NULL, NULL, TO_ROOM);
           return;
        }
   }

   return;
}


void unfinished_quest(CHAR_DATA *ch)
{
    if (IS_QUESTOR(ch))
    {
        bool found = FALSE;

        sprintf(log_buf, "Creating unfinished quest for %s.", ch->name);
        log_string(log_buf);
        wiznet(log_buf, NULL, NULL, 0, 0, 0);
        if (ch->pcdata->questobj > 0)
        {
            OBJ_DATA *obj = NULL;
            ROOM_INDEX_DATA *pRoom = NULL;      
 
                 for(obj = object_list; obj != NULL; obj = obj->next)
                 if(obj->pIndexData->vnum == ch->pcdata->questobj
                    && is_name(ch->name, obj->owner))
                    found = TRUE;

            if(!found)
            {
                if ((obj = create_object(get_obj_index(ch->pcdata->questobj), ch->level)) == NULL)
                end_quest(ch, 0);
                else
                {
                if ((pRoom = get_room_index(ch->pcdata->questloc)) == NULL)
                {
                    pRoom = get_random_room(ch);
                    ch->pcdata->questobj = pRoom->vnum;
                }
                obj_to_room(obj, pRoom);
            }       

            do_quest(ch, "info");
            send_to_char("\n\r", ch);
            }
        } else if (ch->pcdata->questmob > 0)
        {
            CHAR_DATA *mob;

            for(mob = char_list; mob != NULL; mob = mob->next)
                if(IS_NPC(mob) && mob->pIndexData->vnum == ch->pcdata->questmob)
                   break;

            if (!mob)
            end_quest(ch, 5);
            else
            {
            ch->pcdata->questloc = mob->in_room->vnum;
            do_quest(ch, "info");
            send_to_char("\n\r", ch);
            }            

        }
    }
}
       

void update_all_qobjs(CHAR_DATA * ch)
{
    OBJ_DATA *obj;
    AFFECT_DATA *o_paf;

    for (obj = ch->carrying; obj != NULL; obj = obj->next_content)
    {
        if (IS_OBJ_STAT(obj, ITEM_QUEST))
        {
            update_questobjs(ch, obj);

            for (o_paf = obj->pIndexData->affected; o_paf != NULL; o_paf = o_paf->next)
            affect_join_obj(obj, o_paf);
            for (o_paf = obj->affected; o_paf != NULL; o_paf = o_paf->next)
            affect_check(ch, o_paf->where, o_paf->bitvector);
        }
        else
        {         
            for (o_paf = obj->affected; o_paf != NULL; o_paf = o_paf->next)
                affect_check(ch, o_paf->where, o_paf->bitvector);
            if (!obj->enchanted)
            {
                for (o_paf = obj->pIndexData->affected; o_paf != NULL;
                     o_paf = o_paf->next)
                    affect_check(ch, o_paf->where, o_paf->bitvector);
            }
        }
    }
}                    


void do_levelflux( CHAR_DATA *ch, char *argument )
{
    CHAR_DATA *questman;
    ROOM_INDEX_DATA *in_room;
    int cost = 5000;

    in_room = ch->in_room;

    if ( IS_NPC( ch ) )
    {
        send_to_char( "Sorry mobs don't have pets!\n\r", ch );
        return;
    }

    if ( ch->pet == NULL )
    {
        send_to_char( "You don't have a pet get one first!\n\r", ch );
        return;
    }

    if ( ch->pet->in_room->vnum != ch->in_room->vnum )
    {
        send_to_char( "Your pet has to be here with you!\n\r", ch );
        return;
    }

    if ( ch->pet->level < 40 )    
    {
        send_to_char( "Your pet has to be at least level 40 to raise his levelflux!\n\r", ch );
        return;
    }

    for ( questman = ch->in_room->people; questman != NULL; questman = questman->next_in_room )
    {
        if (!IS_NPC(questman)) continue;
        if (questman->spec_fun == spec_lookup( "spec_questmaster" )) break;
    }

    if (questman == NULL || questman->spec_fun != spec_lookup( "spec_questmaster" ))
    {
        send_to_char("You can't do that here.\n\r",ch);
        return;
    }            
  
    if ( questman->fighting != NULL)
    {
        send_to_char("Wait until the fighting stops.\n\r",ch);
        return;
    }
       
    if ( ch->pet->levelflux >= 5 )
    {
        send_to_char( "I'm sorry you can't train your levelflux past 5!\n\r", ch );
        return;
    }

    if ( ch->pcdata->questpoints < cost )
    {
        printf_to_char( ch, "I'm sorry it takes %d questpoints to buy a levelflux.\n\r", cost );
        return;
    }    

    ch->pet->levelflux++;
    ch->pcdata->questpoints -= cost;
    printf_to_char( ch, "Your pets levelflux is now %d.\n\r", ch->pet->levelflux );
    printf_to_char( ch, "The questmaster relieves you of %d questpoints.\n\r", cost );
    return;
}