zim/area/
zim/bin/
zim/clans/plists/
zim/corefiles/
zim/doc/muddy/
zim/gods/
zim/log/
zim/player/
zim/skill_tree/
zim/tmp/
/*-
 * Copyright (c) 1998 fjoe <fjoe@iclub.nsu.ru>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: quest.c 978 2006-12-08 07:38:16Z zsuzsu $
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>

#include "merc.h"
#include "debug.h"
#include "quest.h"
#include "update.h"
#include "waffects.h"

#ifdef SUNOS
#	include <stdarg.h>
#	include "compat/compat.h"
#endif

enum qitem_type {
	TYPE_ITEM,
	TYPE_OTHER
};

typedef struct qitem_data QITEM_DATA;
struct qitem_data {
	char		*name;
	int		price;
	const char	*restrict_class;
	int		vnum;
	bool		(*do_buy)(CHAR_DATA *ch, CHAR_DATA *questor);
	int		*gsn;
};

static void 	quest_tell(CHAR_DATA *ch, CHAR_DATA *questor, const char *fmt, ...);
static CHAR_DATA *questor_lookup(CHAR_DATA *ch);
qtrouble_t 	*qtrouble_lookup(CHAR_DATA *ch, int vnum);
bool 		qtrouble_delete(CHAR_DATA *ch, int vnum);
int		quest_item_price (CHAR_DATA *ch, QITEM_DATA *qitem);

static void quest_points(CHAR_DATA *ch, char *arg);
static void quest_info(CHAR_DATA *ch, char *arg);
static void quest_time(CHAR_DATA *ch, char *arg);
static void quest_list(CHAR_DATA *ch, char *arg);
static void quest_buy(CHAR_DATA *ch, char *arg);
static void quest_return(CHAR_DATA *ch, char *arg);
static void quest_request(CHAR_DATA *ch, char *arg);
static void quest_complete(CHAR_DATA *ch, char *arg);
static void quest_trouble(CHAR_DATA *ch, char *arg);
static void quest_quit(CHAR_DATA *ch, char *arg);

static OBJ_DATA *quest_give_item(CHAR_DATA *ch, CHAR_DATA *questor,
			    int item_vnum);

/*static bool buy_gold(CHAR_DATA *ch, CHAR_DATA *questor);*/
static bool buy_tattoo(CHAR_DATA *ch, CHAR_DATA *questor);
static bool buy_item(CHAR_DATA *ch, CHAR_DATA *questor, int vnum);
static bool buy_con(CHAR_DATA *ch, CHAR_DATA *questor);
static bool buy_death(CHAR_DATA *ch, CHAR_DATA *questor);
static bool buy_katana(CHAR_DATA *ch, CHAR_DATA *questor);
static bool buy_vampire(CHAR_DATA *ch, CHAR_DATA *questor);

bool quest_item_usable (CHAR_DATA *ch, QITEM_DATA *qitem);
bool quest_has_similar (CHAR_DATA *ch, int vnum);
int  quest_item_extract(CHAR_DATA *ch, int vnum);
int  quest_extract_similar (CHAR_DATA *ch, int vnum);
int  quest_item_in_world (CHAR_DATA *ch, int vnum);
int  quest_item_group (int vnum);
QITEM_DATA * quest_item_by_vnum (int vnum);

struct qitem_data qitem_table[] = {
	{ "a flying rug",		25, 	NULL,
	   QUEST_VNUM_RUG,		NULL,	NULL		},

/*	{ "10,000 gold pieces",		250, 	NULL,
	   0,			buy_gold,	NULL		},
*/
	{ "tattoo of your religion",	200, 	NULL,
	   0, 			buy_tattoo,	NULL		},

/*
	{ "Heroic Constitution",	2000, 	NULL,
	   0, buy_con						},
*/

	{ "Decrease number of deaths",	10,	"samurai",
	   0,			buy_death,	NULL		},

	{ "Katana quest",		150,	"samurai",
	   0,			buy_katana,	NULL		},

	{ "Vampire skill",		20,	"vampire",
	   0,			buy_vampire,	NULL		},

/*	{ "Bottomless canteen with cranberry juice", 350, NULL,
	   QUEST_VNUM_CANTEEN, NULL				},
*/
	{ "Quest song",			1000,	NULL,
	   QUEST_VNUM_SONG, 		NULL,	NULL		},

	{ "Quest amulet",		2000,	NULL,
	   QUEST_VNUM_AMULET, 		NULL,	NULL		},

	{ "Quest bracer",		2500,	NULL,
	   QUEST_VNUM_BRACER, 		NULL,	NULL		},

	{ "Quest ring",			3000,	NULL,
	   QUEST_VNUM_RING, 		NULL,	NULL		},

	{ "Quest halberd",		3500,	NULL,
	   QUEST_VNUM_POLEARM,		NULL,	&gsn_polearm	},

	{ "Quest claymore",		3500,	NULL,
	   QUEST_VNUM_BASTARDSWORD,	NULL,	&gsn_bastardsword	},

	{ "Quest battleaxe",		3500,	NULL,
	   QUEST_VNUM_BATTLEAXE,	NULL,	&gsn_axe	},

	{ "Quest warhammer",		3500,	NULL,
	   QUEST_VNUM_WARHAMMER,	NULL,	&gsn_hammer	},

	{ "Quest lance",		3500,	NULL,
	   QUEST_VNUM_LANCE,		NULL,	&gsn_lance	},

	{ "Quest spear",		3500,	NULL,
	   QUEST_VNUM_SPEAR,		NULL,	&gsn_spear	},

	{ "Quest longsword",		3500,	NULL,
	   QUEST_VNUM_LONGSWORD,	NULL,	&gsn_longsword	},

	{ "Quest axe",			3500,	NULL,
	   QUEST_VNUM_AXE,		NULL,	&gsn_axe	},

	{ "Quest hammer",		3500,	NULL,
	   QUEST_VNUM_HAMMER,		NULL,	&gsn_hammer	},

	{ "Quest shortsword",		3500,	NULL,
	   QUEST_VNUM_SHORTSWORD,	NULL,	&gsn_shortsword	},

	{ "Quest stiletto",		3500,	NULL,
	   QUEST_VNUM_STILETTO,		NULL,	&gsn_dagger	},

	{ "Quest dagger",		3500,	NULL,
	   QUEST_VNUM_DAGGER,		NULL,	&gsn_dagger	},

	{ "Quest mace",			3500,	NULL,
	   QUEST_VNUM_MACE,		NULL,	&gsn_mace	},

	{ "Quest flail",		3500,	NULL,
	   QUEST_VNUM_FLAIL,		NULL,	&gsn_flail	},

	{ "Quest whip",			3500,	NULL,
	   QUEST_VNUM_WHIP,		NULL,	&gsn_whip	},

	{ "Quest bow",			3500,	NULL,
	   QUEST_VNUM_BOW,		NULL,	&gsn_bow	},

	{ NULL }
};

#define QUEST_GROUP_WEAPON	0
int quest_item_groups[][18] = { 
				{
				QUEST_VNUM_LANCE,
				QUEST_VNUM_BOW,
				QUEST_VNUM_POLEARM,
				QUEST_VNUM_BASTARDSWORD,
				QUEST_VNUM_BATTLEAXE,
				QUEST_VNUM_WARHAMMER,
				QUEST_VNUM_LONGSWORD,
				QUEST_VNUM_AXE,
				QUEST_VNUM_HAMMER,
				QUEST_VNUM_SHORTSWORD,
				QUEST_VNUM_STILETTO,
				QUEST_VNUM_DAGGER,
				QUEST_VNUM_FLAIL,
				QUEST_VNUM_MACE,
				QUEST_VNUM_SPEAR,
				QUEST_VNUM_STAFF,
				QUEST_VNUM_WHIP,
				0},

				{ 0 }
		};

struct qcmd_data {
	char *name;
	void (*do_fn)(CHAR_DATA *ch, char* arg);
	int min_position;
	int extra;
};
typedef struct qcmd_data Qcmd_t;

Qcmd_t qcmd_table[] = {
	{ "points",	quest_points,	POS_DEAD,	CMD_KEEP_HIDE},
	{ "info",	quest_info,	POS_DEAD,	CMD_KEEP_HIDE},
	{ "time",	quest_time,	POS_DEAD,	CMD_KEEP_HIDE},
	{ "list",	quest_list,	POS_RESTING,	0},
	{ "buy",	quest_buy,	POS_RESTING,	0},
	{ "request",	quest_request,	POS_RESTING,	0},
	{ "complete",	quest_complete,	POS_RESTING,	0},
	{ "return",	quest_return,	POS_RESTING,	0},
	{ "trouble",	quest_trouble,	POS_RESTING,	0},
	{ "quit",	quest_quit,	POS_DEAD,	CMD_KEEP_HIDE},
	{ NULL}
};

/*
 * The main quest function
 */
void do_quest(CHAR_DATA *ch, const char *argument)
{
	char cmd[MAX_INPUT_LENGTH];
	char arg[MAX_INPUT_LENGTH];
	Qcmd_t *qcmd;

	argument = one_argument(argument, cmd, sizeof(cmd));
	argument = one_argument(argument, arg, sizeof(arg));

	if (IS_NPC(ch)) 
		return;

	for (qcmd = qcmd_table; qcmd->name != NULL; qcmd++)
		if (str_prefix(cmd, qcmd->name) == 0) {
			if (ch->position < qcmd->min_position) {
				char_puts("In your dreams, or what?\n", ch);
				return;
			}
			if (!IS_SET(qcmd->extra, CMD_KEEP_HIDE)
			&&  IS_SET(ch->affected_by, AFF_HIDE | AFF_FADE)) { 
				REMOVE_BIT(ch->affected_by,
					   AFF_HIDE | AFF_FADE);
				act_puts("You step out of shadows.",
					 ch, NULL, NULL, TO_CHAR, POS_DEAD);
				act("$n steps out of shadows.",
				    ch, NULL, NULL, TO_ROOM);
			}
			qcmd->do_fn(ch, arg);
			return;
		}
		
	char_puts("QUEST COMMANDS: points info time request complete list buy trouble quit.\n", ch);
	char_puts("For more information, type: help quests.\n", ch);
}

void quest_handle_death(CHAR_DATA *ch, CHAR_DATA *victim)
{
	if (IS_NPC(ch)
	&&  IS_SET(ch->pIndexData->act, ACT_SUMMONED)
	&&  ch->master != NULL)
		ch = ch->master;

	if (victim->hunter) {
		if (victim->hunter == ch || victim->hunter->master == ch) {
			act_puts("You have almost completed your QUEST!\n"
				 "Return to questmaster before your time "
				 "runs out!",
				 ch, NULL, NULL, TO_CHAR, POS_DEAD);
			ch->pcdata->questmob = -1;
		}
		else {
			act_puts("You have completed someone's quest.",
				 ch, NULL, NULL, TO_CHAR, POS_DEAD);

			ch = victim->hunter;
			act_puts("Someone has completed you quest.",
				 ch, NULL, NULL, TO_CHAR, POS_DEAD);
			quest_cancel(ch);
			ch->pcdata->questtime = -number_range(5, 10);
		}
	}
}

void quest_cancel(CHAR_DATA *ch)
{
	CHAR_DATA *fch;

	if (IS_NPC(ch)) {
		bug("quest_cancel: called for NPC", 0);
		return;
	}

	/*
	 * remove mob->hunter
	 */
	for (fch = npc_list; fch; fch = fch->next)
		if (fch->hunter == ch) {
			fch->hunter = NULL;
			break;
		}

	ch->pcdata->questtime = 0;
	ch->pcdata->questgiver = 0;
	ch->pcdata->questmob = 0;
	ch->pcdata->questobj = 0;
	ch->pcdata->questroom = NULL;
}

/*
 * Called from update_handler() by pulse_area
 */
void quest_update(void)
{
	CHAR_DATA *ch, *ch_next;

	for (ch = char_list; ch && !IS_NPC(ch); ch = ch_next) {
		ch_next = ch->next;

		if (ch->pcdata->questtime < 0) {
			if (++ch->pcdata->questtime == 0) {
				char_puts("{*You may now quest again.\n", ch);
				return;
			}
		} else if (IS_ON_QUEST(ch)) {
			if (--ch->pcdata->questtime == 0) {
				char_puts("You have run out of time for your quest!\n", ch);
				quest_cancel(ch);
				ch->pcdata->questtime = -number_range(4, 8);
			} else if (ch->pcdata->questtime < 6) {
				char_puts("Better hurry, you're almost out of time for your quest!\n", ch);
				return;
			}
		}
	}
}

void qtrouble_set(CHAR_DATA *ch, int vnum, int count)
{
	qtrouble_t *qt;

	if ((qt = qtrouble_lookup(ch, vnum)) != NULL)
		qt->count = count;
	else {
		qt = malloc(sizeof(*qt));
		qt->vnum = vnum;
		qt->count = count;
		qt->next = ch->pcdata->qtrouble;
		ch->pcdata->qtrouble = qt;
	}
}

/*
 * local functions
 */

static void quest_tell(CHAR_DATA *ch, CHAR_DATA *questor, const char *fmt, ...)
{
	va_list ap;
	char buf[MAX_STRING_LENGTH];

	va_start(ap, fmt);
	vsnprintf(buf, sizeof(buf), GETMSG(fmt, ch->lang), ap);
	va_end(ap);

	do_tell_raw(questor, ch, buf);
}

static CHAR_DATA* questor_lookup(CHAR_DATA *ch)
{
	CHAR_DATA *vch;
	CHAR_DATA *questor = NULL;

	for (vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room) {
		if (!IS_NPC(vch)) 
			continue;
		if (IS_SET(vch->pIndexData->act, ACT_QUESTOR)) {
			questor = vch;
			break;
		}
	}

	if (questor == NULL) {
		char_puts("You can't do that here.\n", ch);
		return NULL;
	}

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

	return questor;
}

/* 
 * returns whether one was deleted or not
 */
bool qtrouble_delete(CHAR_DATA *ch, int vnum)
{
	qtrouble_t *qt, *doomed;
	bool found = FALSE;

	qt = ch->pcdata->qtrouble;
	while (qt != NULL) {

		if (qt == ch->pcdata->qtrouble
		&& qt->vnum == vnum) {
			ch->pcdata->qtrouble = qt->next;
			free(qt);
			qt = ch->pcdata->qtrouble;
			found = TRUE;
		}
		else if (qt->next && qt->next->vnum == vnum) {
			doomed = qt->next;
			qt->next = qt->next->next;
			free(doomed);
			found = TRUE;
		}

		if (qt)
			qt = qt->next;

	}

	return found;
}

qtrouble_t *qtrouble_lookup(CHAR_DATA *ch, int vnum)
{
	qtrouble_t *qt;

	for (qt = ch->pcdata->qtrouble; qt != NULL; qt = qt->next)
		if (qt->vnum == vnum)
			return qt;

	return NULL;
}

/*
 * quest do functions
 */

static void quest_points(CHAR_DATA *ch, char* arg)
{
	char_printf(ch, "You have {W%d{x quest points.\n",
		    ch->pcdata->questpoints);
}

static void quest_info(CHAR_DATA *ch, char* arg)
{
	if (!IS_ON_QUEST(ch)) {
		char_puts("You aren't currently on a quest.\n", ch);
		return;
	}

	if (ch->pcdata->questmob == -1) {
		char_puts("Your quest is ALMOST complete!\nGet back to questor before your time runs out!\n", ch);
		return;
	}

	if (ch->pcdata->questobj > 0) {
		OBJ_INDEX_DATA *qinfoobj;

		qinfoobj = get_obj_index(ch->pcdata->questobj);
		if (qinfoobj != NULL) {
			char_printf(ch, "You are on a quest to recover the fabled {W%s{x!\n",
				    qinfoobj->name);
			if (ch->pcdata->questroom)
				char_printf(ch, "That location is in general area of {W%s{x for {W%s{x.\n",
					    ch->pcdata->questroom->area->name, 
					    mlstr_mval(ch->pcdata->questroom->name));
		}
		else 
			char_puts("You aren't currently on a quest.\n", ch);
		return;
	}

	if (ch->pcdata->questmob > 0) {
		MOB_INDEX_DATA *questinfo;

		questinfo = get_mob_index(ch->pcdata->questmob);
		if (questinfo != NULL) {
			char_printf(ch, "You are on a quest to slay the dreaded {W%s{x!\n",
				    mlstr_mval(questinfo->short_descr));
			if (ch->pcdata->questroom)
				char_printf(ch, "That location is in general area of {W%s{x for {W%s{x.\n",
					    ch->pcdata->questroom->area->name, 
					    mlstr_mval(ch->pcdata->questroom->name));
		} else 
			char_puts("You aren't currently on a quest.\n", ch);
		return;
	}
}

static void quest_time(CHAR_DATA *ch, char* arg)
{
	if (!IS_ON_QUEST(ch)) {
		char_puts("You aren't currently on a quest.\n", ch);
		if (ch->pcdata->questtime < -1)
			char_printf(ch, "There are {W%d{x minutes remaining until you can go on another quest.\n",
				    -ch->pcdata->questtime);
	    	else if (ch->pcdata->questtime == -1)
			char_puts("There is less than a minute remaining until you can go on another quest.\n", ch);
	}
	else
		char_printf(ch, "Time left for current quest: {W%d{x.\n",
			    ch->pcdata->questtime);
}

static void quest_list(CHAR_DATA *ch, char *arg)
{
	CHAR_DATA *questor;
	QITEM_DATA *qitem;
	class_t *cl;

	if ((questor = questor_lookup(ch)) == NULL
	||  (cl = class_lookup(ch->class)) == NULL)
		return;

	act("$n asks $N for list of quest items.", ch, NULL, questor, TO_ROOM);
	act_puts("You ask $N for list of quest items.",
		 ch, NULL, questor, TO_CHAR, POS_DEAD);

	char_puts("Current Quest Items available for Purchase:\n", ch);
	for (qitem = qitem_table; qitem->name; qitem++) {

		if (!quest_item_usable(ch, qitem))
			continue;

		if (arg[0] != '\0' && !is_name(arg, qitem->name))
			continue;

		char_printf(ch, "{M%5d{mqp{x...........{B%-17s{x",
			quest_item_price(ch, qitem), qitem->name); 

		if (qitem->vnum != 0) {
			if (get_obj_index(qitem->vnum) == NULL) {
				bug("missing quest item: vnum ", qitem->vnum);
				continue;
			}
			char_printf(ch, " {D(level %2d){x",
				    get_obj_index(qitem->vnum)->level);
		}
		char_printf(ch, "\n");

	}
	char_puts("To buy an item, type 'QUEST BUY <item>'.\n", ch);
}

static void quest_buy(CHAR_DATA *ch, char *arg)
{
	CHAR_DATA *questor;
	QITEM_DATA *qitem;
	int price = 0;
	class_t *cl;

	if ((questor = questor_lookup(ch)) == NULL
	||  (cl = class_lookup(ch->class)) == NULL)
		return;

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

	for (qitem = qitem_table; qitem->name; qitem++)
		if (is_name(arg, qitem->name)) {
			bool buy_ok = FALSE;

			if (!quest_item_usable(ch, qitem))
				continue;

			price = quest_item_price(ch, qitem);

			if (ch->pcdata->questpoints < price) {
				quest_tell(ch, questor,
					   "Sorry, {W%s{z, but you don't have "
					   "enough quest points for that.",
					   ch->name);
				return;
			}

			if (qitem->vnum == 0)
				buy_ok = qitem->do_buy(ch, questor);
			else
				buy_ok = buy_item(ch, questor,
						qitem->vnum);

			if (buy_ok) {
				ch->pcdata->questpoints -= price;

				DEBUG(DEBUG_QUEST,
					"%s[%d][%d] quest buy [%d] %s",
					ch->name, ch->level, 
					ch->pcdata->questpoints, 
					qitem->vnum, qitem->name);
				wiznet2(WIZ_QUESTS, IM, 0, ch,
					"$N {mquest buy{x %s.",
					qitem->name);
			}
			return;
		}

	quest_tell(ch, questor, "I do not have that item, %s.", ch->name);
}

static void quest_quit(CHAR_DATA *ch, char* arg)
{
	char buf[MAX_STRING_LENGTH];

        if (!IS_ON_QUEST(ch)) {
                char_puts("You aren't currently on a quest.\n", ch);
		return;
	}
	else
	char_puts("Your quest has been cancelled.\n",ch);
	quest_cancel(ch);
	ch->pcdata->questtime = -number_range(5, 8);

	snprintf(buf, sizeof(buf),
		"$N[%d] {mquest quit{x.",
		ch->level);
	wiznet(buf,
		ch, NULL, WIZ_QUESTS, 0, 0);

}



#define MAX_QMOB_COUNT 512

static void quest_request(CHAR_DATA *ch, char *arg)
{
	int i;
	CHAR_DATA *mobs[MAX_QMOB_COUNT];
	size_t mob_count;
	CHAR_DATA *victim = NULL;
	CHAR_DATA *questor;
	char buf[MAX_STRING_LENGTH];

	if ((questor = questor_lookup(ch)) == NULL)
		return;

	act("$n asks $N for a quest.", ch, NULL, questor, TO_ROOM);
	act_puts("You ask $N for a quest.",
		 ch, NULL, questor, TO_CHAR, POS_DEAD);

	if (IS_ON_QUEST(ch)) {
    		quest_tell(ch, questor, "But you are already on a quest!");
    		return;
	} 

	if (ch->pcdata->questtime < 0) {
		quest_tell(ch, questor,
			   "You're very brave, {W%s{z, but let someone else "
			   "have a chance.", ch->name);
		quest_tell(ch, questor, "Come back later.");
		return;
	}

	if (is_affected(ch, gsn_dishonor)) {
		quest_tell(ch, questor,
			   "Perhaps, {W%s{z, you should be more concerned "
			   "regaining your fallen honor.", ch->name);
		return;
	}

	quest_tell(ch, questor, "Thank you, brave {W%s{z!", ch->name);

	/*
	 * find MAX_QMOB_COUNT quest mobs and store their vnums in mob_buf
	 */
	mob_count = 0;
	for (victim = npc_list; victim; victim = victim->next) {
		int diff = victim->level - ch->level;

		if (!IS_NPC(victim)
		||  (ch->level < 51 && (diff > 4 || diff < -1))
		||  (ch->level > 50 && (diff > (ch->level/12 -1) || diff < 0))
		||  victim->pIndexData->pShop
		||  IS_SET(victim->pIndexData->attr_flags, MOB_ATTR_NOQUEST)
		||  victim->race == ch->race
		||  victim->invis_level
		||  (IS_EVIL(victim) && IS_EVIL(ch))
		||  (IS_NEUTRAL(victim) && IS_NEUTRAL(ch))
		||  (IS_GOOD(victim) && IS_GOOD(ch))
		||  victim->pIndexData->vnum < 200
		||  IS_SET(victim->pIndexData->act,
			   ACT_TRAIN | ACT_PRACTICE | ACT_HEALER |
			   ACT_NOTRACK | ACT_PET)
/*		||  IS_SET(victim->pIndexData->imm_flags, IMM_SUMMON) */
		||  questor->pIndexData == victim->pIndexData
		||  victim->in_room == NULL
		||  (IS_SET(victim->pIndexData->act, ACT_SENTINEL) &&
		     IS_SET(victim->in_room->room_flags,
			    ROOM_PRIVATE | ROOM_SOLITARY))
		||  !str_cmp(victim->in_room->area->name,
			     hometown_name(ch->hometown))
		||  IS_SET(victim->in_room->area->flags,
			   AREA_CLOSED | AREA_NOQUEST))
			continue;

		mobs[mob_count++] = victim;
		if (mob_count >= MAX_QMOB_COUNT)
			break;
	}

	if (mob_count == 0) {
		quest_tell(ch, questor, "I'm sorry, but i don't have any quests for you at this time.");
		quest_tell(ch, questor, "Try again later.");
		ch->pcdata->questtime = -5;
		return;
	}

	victim = mobs[number_range(0, mob_count-1)];
	ch->pcdata->questroom = victim->in_room;

	if (chance(40 - (ch->level /4))) { /* Quest to find an obj */
		OBJ_DATA *eyed;
		int obj_vnum;

		if (IS_GOOD(ch))
			i = 0;
		else if (IS_EVIL(ch))
			i = 2;
		else
			i = 1;

		obj_vnum = number_range(QUEST_OBJ_FIRST, QUEST_OBJ_LAST);
		eyed = create_obj(get_obj_index(obj_vnum), 0);
		eyed->level = ch->level;
		eyed->owner = mlstr_dup(ch->short_descr);
		eyed->ed = ed_new2(eyed->pIndexData->ed, ch->name);
		eyed->cost = 0;
		eyed->timer = 30;

		obj_to_room(eyed, victim->in_room);
		ch->pcdata->questobj = eyed->pIndexData->vnum;

		quest_tell(ch, questor,
			   "Vile pilferers have stolen {W%s{z "
			   "from the royal treasury!",
			   mlstr_mval(eyed->short_descr));
		quest_tell(ch, questor,
			   "My court wizardess, with her magic mirror, "
			   "has pinpointed its location.");
		quest_tell(ch, questor,
			   "Look in the general area of {W%s{z for {W%s{z!",
			   victim->in_room->area->name,
			   mlstr_mval(victim->in_room->name));

		snprintf(buf, sizeof(buf),
			"$N[%d] {mon quest{x to {W%s{x in {W%s{x to find %s.",
			ch->level,
			victim->in_room->area->name,
			mlstr_mval(victim->in_room->name),
			mlstr_mval(eyed->short_descr));
		wiznet(buf,
			ch, NULL, WIZ_QUESTS, 0, 0);
	}
	else {	/* Quest to kill a mob */
		if (IS_GOOD(ch)) {
			quest_tell(ch, questor,
				   "Rune's most heinous criminal, {W%s{z, "
				   "has escaped from the dungeon.",
				   mlstr_mval(victim->short_descr));
			quest_tell(ch, questor,
				   "Since the escape, {W%s{z has murdered {W%d{z civilians!",
				   mlstr_mval(victim->short_descr),
				   number_range(2, 20));
			quest_tell(ch, questor, "The penalty for this crime is death, and you are to deliver the sentence!");
		}
		else {
			quest_tell(ch, questor,
				   "An enemy of mine, {W%s{z, "
				   "is making vile threats against the crown.",
				   mlstr_mval(victim->short_descr));
			quest_tell(ch, questor,
				   "This threat must be eliminated!");
		}

		quest_tell(ch, questor,
			   "Seek {W%s{z out in the vicinity of {W%s{z!",
			   mlstr_mval(victim->short_descr),
			   mlstr_mval(victim->in_room->name));
		quest_tell(ch, questor,
			   "That location is in general area of {W%s{z.",
			   victim->in_room->area->name);

		ch->pcdata->questmob = victim->pIndexData->vnum;
		victim->hunter = ch;

		snprintf(buf, sizeof(buf),
			"$N[%d] {mon quest{x to {W%s{x in {W%s{x to kill %s.",
			ch->level,
			victim->in_room->area->name,
			mlstr_mval(victim->in_room->name),
			mlstr_mval(victim->short_descr));
		wiznet(buf,
			ch, NULL, WIZ_QUESTS, 0, 0);
	}

	ch->pcdata->questgiver = questor->pIndexData->vnum;
	ch->pcdata->questtime = number_range(10, 20) + ch->level/10;
	quest_tell(ch, questor,
		   "You have {W%d{z minutes to complete this quest.", 
		   ch->pcdata->questtime);
	quest_tell(ch, questor, "May the gods go with you!");
}

static void quest_complete(CHAR_DATA *ch, char *arg)
{
	bool complete = FALSE;
	char buf[MAX_INPUT_LENGTH];
	CHAR_DATA *questor;
	OBJ_DATA *obj;
	OBJ_DATA *obj_next;
	WORLD_AFFECT_DATA *waf;

	int gold_reward = 0;
	int qp_reward = 0;
	int exp_reward = 0;
	
	if ((questor = questor_lookup(ch)) == NULL)
		return;

	act("$n informs $N $e has completed $s quest.",
	    ch, NULL, questor, TO_ROOM);
	act_puts("You inform $N you have completed your quest.",
		 ch, NULL, questor, TO_CHAR, POS_DEAD);

	if (!IS_ON_QUEST(ch)) {
		quest_tell(ch, questor, "You have to REQUEST a quest first, {W%s{z.",
			   ch->name); 
		return;
	}

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

	if (ch->pcdata->questobj > 0)
		for (obj = ch->carrying; obj; obj = obj_next) {
			obj_next = obj->next_content;

			if (obj->pIndexData->vnum == ch->pcdata->questobj
			&&  IS_OWNER(ch, obj)) {
				act_puts("You hand {W$p{x to $N.",
					 ch, obj, questor, TO_CHAR, POS_DEAD);
				act("$n hands {W$p{x to $N.",
				    ch, obj, questor, TO_ROOM);
				extract_obj(obj, 0);

				complete = TRUE;
				break;
			}
		}
	else if (ch->pcdata->questmob == -1) {
		complete = TRUE;
	}

	if (!complete) {
		quest_tell(ch, questor,
			   "You haven't completed the quest yet, but there is "
			   "still time!");
		return;
	}

	/* quest rewards */
	if (ch->pcdata->questobj > 0) {
		qp_reward = number_range((ch->level - ch->level / 10 +1),
					 (ch->level + ch->level / 50 +1));
		qp_reward = UMAX(1, qp_reward);
		gold_reward = UMAX(1, number_range(1, ch->level/10));
		exp_reward = number_range(75, 125);
	}
	/* mob quest */
	else {
		qp_reward = number_range((ch->level - ch->level / 10 +1),
					 (ch->level + ch->level / 10 +1));
		qp_reward = UMAX(1, qp_reward);
		gold_reward = UMAX(2, number_range(1, ch->level/8));
	}

	if ((waf = ch_waffected(ch, WAFF_QP)) != NULL) {
		if (number_percent() <= waf->chance)
			qp_reward = qp_reward * waf->modifier / 100;
	}

	/*ch->gold += gold_reward;*/
	ch->pcdata->questpoints += qp_reward;

	quest_tell(ch, questor, "Congratulations on completing your quest!");
	ch->pcdata->questcount++;
	if (exp_reward > 0 && ch->level < HERO) {
		quest_tell(ch, questor,
			   "As a reward,"
			   " I am giving you {Y%d{z quest points,"
			   " {Y%d{z experience points."
			   /*" and {Y%d{z gold to cover expenses."*/,
			   qp_reward, exp_reward/*, gold_reward*/);
		gain_exp(ch, exp_reward);
	}
	else
		quest_tell(ch, questor,
			   "As a reward,"
			   " I am giving you {Y%d{z quest points."
			   /*" and {Y%d{z gold to cover expenses."*/,
			   qp_reward/*, gold_reward*/);

	snprintf(buf, sizeof(buf),
		"$N[%d] {mrewarded{x {M%d{xqp by $t.",
		ch->level,
		qp_reward);
	wiznet(buf,
		ch, mlstr_mval(questor->short_descr), WIZ_QUESTS, 0, 0);

	quest_cancel(ch);
	ch->pcdata->questtime = -number_range(6, 9);
}

/*
 * give questus an item
 *    if this is a quest item he will give them
 *    a percentage of the original cost.
 *
 *    Also, you can return IMM quest items to Questus
 *    and he will give you the cost in quest points.
 *
 * by Zsuzsu
 */
static void quest_return(CHAR_DATA *ch, char *arg)
{
	CHAR_DATA *questor;
	QITEM_DATA *qitem;
	qtrouble_t *qt = NULL;
	OBJ_DATA *obj;
	int qp_reward = 0;
	char buf[MAX_STRING_LENGTH];

	if ((questor = questor_lookup(ch)) == NULL)
		return;

	if (arg[0] == '\0') {
		char_puts("To turn in an item, type 'QUEST RETURN <item>'.\n", ch);
		return;
	}	

        if ((obj = get_obj_carry(ch, arg)) == NULL) {
		char_puts("You do not have that item.\n", ch);
		return;
	}

	/* non-quest reward */

	if (!IS_SET(obj->pIndexData->extra_flags, ITEM_QUEST)) {
		quest_tell(ch, questor,
			   "It's nice of you to offer,"
			   " but no thank you.");
		return;
	}

	qt = qtrouble_lookup(ch, obj->pIndexData->vnum);
	qitem = quest_item_by_vnum(obj->pIndexData->vnum);

	/* non-quest item */
	if (!qitem) {
		qp_reward = UMIN(obj->cost, obj->pIndexData->cost);

		if (qp_reward > 300) {
			quest_tell(ch, questor,
				   "I'm sorry, but that item is so valuable"
				   " only an immortal can reward you for it.");
			return;
		}

		qp_reward = UMAX(1, qp_reward * ch->level / 100);

		DEBUG(DEBUG_QUEST,
			"quest return: %s[%d](%d) rewarded %d for [%d] %s",
			ch->name, ch->level, 
			ch->pcdata->questpoints, 
			qp_reward,
			obj->pIndexData->vnum, obj->name);

		act_puts("You hand {W$p{x to $N.",
			 ch, obj, questor, TO_CHAR, POS_DEAD);

		act_puts("$n hands {W$p{x to $N.",
		    ch, obj, questor, TO_ROOM, POS_RESTING);

		quest_tell(ch, questor,
			"Oh, {W%s{z, won't the immortals be pleased!",
			ch->name);

		quest_tell(ch, questor,
			   "As a reward,"
			   " I am giving you {Y%d{z quest points.",
			   qp_reward);

		ch->pcdata->questpoints += qp_reward;

		snprintf(buf, sizeof(buf),
			"$N[%d] {mrewarded{x {M%d{xqp for returning $p.",
			ch->level,
			qp_reward);
		wiznet(buf,
			ch, obj, WIZ_QUESTS, 0, 0);

		extract_obj(obj, 0);

		return;
	}


	/* item not in the world */
	if (quest_item_in_world(ch, qitem->vnum) <= 0) {
		quest_tell(ch, questor,
			   "If you had one, I might take it.");
			return;
	}

	/* now delete it from the record */
	qtrouble_delete(ch, obj->pIndexData->vnum);
	quest_item_extract(ch, obj->pIndexData->vnum);

	qp_reward = quest_item_price(ch, qitem) / 2;

	DEBUG(DEBUG_QUEST,
		"quest return: %s[%d](%d) reinbursed %d for [%d] %s",
		ch->name, ch->level, 
		ch->pcdata->questpoints, 
		qp_reward,
		obj->pIndexData->vnum, qitem->name);

	ch->pcdata->questpoints += qp_reward;

	quest_tell(ch, questor,
		"Thank you, {W%s{z."
		"  I'll reinburse you, {Y%d{z quest points.",
		ch->name, qp_reward);
}


/*
 * recover lost items
 *
 * rewritten by Zsusu
 */
static void quest_trouble(CHAR_DATA *ch, char *arg)
{
	CHAR_DATA *questor;
	QITEM_DATA *qitem;
	OBJ_DATA *reward;
	qtrouble_t *qt = NULL;
	class_t *cl;
	OBJ_INDEX_DATA *pObjIndex;

	if ((questor = questor_lookup(ch)) == NULL
	||  (cl = class_lookup(ch->class)) == NULL)
		return;

	if (arg[0] == '\0') {
		char_puts("To correct a quest award's trouble,"
			  " type: 'quest trouble <award>'.\n", ch);
		return;
	}

	/* which item are we talking about */
	for (qitem = qitem_table; qitem->name; qitem++) {

		if (!qitem->vnum) 
			continue;

		/* if they already have one -- even though
		 * they no longer can */
		qt = qtrouble_lookup(ch, qitem->vnum);

		if (is_name(arg, qitem->name)) {
			if (!quest_item_usable(ch, qitem)) {
				if (qt)
					break;
				else
					continue;
			}
			break;
		}
	}
	if (!qitem || !qitem->name) {
		quest_tell(ch, questor,
			   "{W%s{z, I'm not really sure what you're trying to do.",
			   ch->name);
		return;
	}

	if (!qt)
		qt = qtrouble_lookup(ch, qitem->vnum);

	/* ch has never bought this item */
	if (!qt) {
		quest_tell(ch, questor,
			"Sorry, {W%s{z, but you haven't bought "
			"that quest award, yet.\n",
			ch->name);    
		return;
	}

	/* item still in the world, don't trouble to get
	 * it back */
	if (quest_item_in_world(ch, qitem->vnum) > 0) {
		quest_tell(ch, questor,
			   "Maybe you should look for it"
			   " before you try to trouble it.");
			return;
	}

	/* look up object */
	pObjIndex = get_obj_index(qitem->vnum);

	/* check if they've exceeded their trouble max
	 * or, if item doesn't have the "quest" flag
	 * refuse to trouble the item */
	if ((TROUBLE_MAX != -1 && qt->count > TROUBLE_MAX)
	||  !IS_SET(pObjIndex->extra_flags, ITEM_QUEST)) {

		quest_tell(ch, questor,
			   "This item is beyond the trouble option.");
		return;
	}

#if 0
	/* this can be used to give people items back that they
	 * have lost.  It should probable come with a cost */

	/* look for item in the world and extract */
	was_in_world = quest_item_extract(ch, qt->vnum);

	debug_printf("%s troubled %s in world: [%d] %s.",
		ch->name, 
		(was_in_world) ? "item" : "item not",
		qitem->vnum,
		qitem->name);
#endif

	reward = quest_give_item(ch, questor, qitem->vnum);

	if (!reward)
		return;

	DEBUG(DEBUG_QUEST,
		"%s[%d][%d] quest trouble [%d/%d] %s",
		ch->name, ch->level, 
		ch->pcdata->questpoints, 
		qitem->vnum, qt->count, qitem->name);

	quest_tell(ch, questor,
		   "This is the {Y%i{x time that I am giving "
		   "that award back.",
		   qt->count);

	qt->count++;


	if (TROUBLE_MAX != -1 && qt->count > TROUBLE_MAX) 
		quest_tell(ch, questor,
			   "And I won't give you that again, "
			   "with trouble option.\n");
}

/*
 * generate the quest object and give it to the char
 *
 * rewritten by Zsuzsu
 */
static OBJ_DATA *quest_give_item(CHAR_DATA *ch, CHAR_DATA *questor,
			    int item_vnum)
{
	OBJ_DATA *reward;
	OBJ_INDEX_DATA *pObjIndex = get_obj_index(item_vnum);
	AFFECT_DATA af;
	char buf[MAX_STRING_LENGTH];

	/* ok, give him requested item */
	reward = create_obj(pObjIndex, 0);
	
	if (get_wear_level(ch, reward) < reward->level) {
		quest_tell(ch, questor,
			   "This item is too powerful for you.\n");
		extract_obj(reward, 0);
		return NULL;
	}

	/* if it's a quest item brand it with the owner's name */
	if (IS_SET(pObjIndex->extra_flags, ITEM_QUEST)) {
		reward->owner = mlstr_dup(ch->short_descr);
		mlstr_free(reward->short_descr);
		reward->short_descr =
			mlstr_printf(reward->pIndexData->short_descr,
				     IS_GOOD(ch) ?	"{Choly{x" :
				     IS_NEUTRAL(ch) ?	"{bblue{w-{ggreen{x" : 
							"{revil{x", 
				     ch->name);

		if (IS_EVIL(ch)) {
			SET_BIT(reward->extra_flags, ITEM_EVIL);
		}

		if (IS_SET(pObjIndex->item_type, ITEM_WEAPON)) {
			af.where	= TO_WEAPON;
			af.type		= 0;
			af.level	= 91;
			af.duration	= -1;
			af.location	= 0;
			af.modifier	= 0;
			af.bitvector	= IS_GOOD(ch) ? WEAPON_HOLY :
					  IS_EVIL(ch) ? WEAPON_VAMPIRIC :
							WEAPON_FLAMING;

			/*
			if (IS_EVIL(ch)
			&& HAS_SKILL(ch, gsn_spellbane))
				af.bitvector = WEAPON_TAINTED;
			*/

			affect_to_obj(reward, &af);
		}
	}

	obj_to_char(reward, ch);

	act("$N gives {W$p{x to $n.", ch, reward, questor, TO_ROOM);
	act_puts("$N gives you {W$p{x.",
		 ch, reward, questor, TO_CHAR, POS_DEAD);

	snprintf(buf, sizeof(buf),
		"$N[%d] {mquest bought{x $p.",
		ch->level);
	wiznet(buf,
		ch, reward, WIZ_QUESTS, 0, 0);

	return reward;
}

/*
 * buy item
 *
 * rewritten by Zsuzsu
 */
static bool buy_item(CHAR_DATA *ch, CHAR_DATA *questor, int item_vnum)
{
	qtrouble_t *qt = NULL;
	OBJ_DATA *reward = NULL;
	OBJ_INDEX_DATA *pObjIndex = get_obj_index(item_vnum);

	qt = qtrouble_lookup(ch, item_vnum);

	/* check if they bought the item 
	 * and still have troubles left */
	if (qt && (TROUBLE_MAX == -1 || qt->count <= TROUBLE_MAX)) {
		quest_tell(ch, questor,
			   "You have already bought this item.");
		return FALSE;
	}

	/* don't let people buy two quest items of the same type (weapon)*/
	if (quest_has_similar(ch, item_vnum)) {
		quest_tell(ch, questor,
			   "You already have a similar item.");
			return FALSE;
	}

	/* if the item is in the world, don't sell them a new one */
	if (quest_item_in_world(ch, item_vnum)) {
		quest_tell(ch, questor,
			   "Maybe you should look for it"
			   " before you try to buy another.");
			return FALSE;
	}

	reward = quest_give_item(ch, questor, item_vnum);

	if (reward) {
		if (qt)
			qt->count = 1;

		else if (IS_SET(pObjIndex->extra_flags, ITEM_QUEST)) {
			qt = malloc(sizeof(*qt));
			qt->vnum = item_vnum;
			qt->count = 1;
			qt->next = ch->pcdata->qtrouble;
			ch->pcdata->qtrouble = qt;
		}
	}


	return reward != NULL;
}

/*
static bool buy_gold(CHAR_DATA *ch, CHAR_DATA *questor)
{
	ch->pcdata->bank_g += 10000;
	act("$N gives 10,000 gold pieces to $n.", 
		ch, NULL, questor, TO_ROOM);
	act("$N transfers {Y10,000{x gold pieces to your bank account.\n",
		ch, NULL, questor, TO_CHAR);
	return TRUE;
}
*/

static bool buy_tattoo(CHAR_DATA *ch, CHAR_DATA *questor)
{
	OBJ_DATA *tattoo;
	char buf[MAX_STRING_LENGTH];

	if (!ch->religion || ch->religion == RELIGION_ATHEIST) {
		char_puts("You must worship a god to have a tattoo.\n", ch);
		return FALSE;
	}

	tattoo = get_eq_char(ch, WEAR_TATTOO);
	if (tattoo != NULL) {
		char_puts("But you have already your tattoo!\n", ch);
		return FALSE;
	}

	tattoo = create_obj(get_obj_index(religion_table[ch->religion].vnum), 0);

	obj_to_char(tattoo, ch);
	equip_char(ch, tattoo, WEAR_TATTOO);
	act("$N tattoos $n with {W$p{x!", ch, tattoo, questor, TO_ROOM);
	act_puts("$N tattoos you with {W$p{x!",
		 ch, tattoo, questor, TO_CHAR, POS_DEAD);

	snprintf(buf, sizeof(buf),
		"$N[%d] {mquest bought{x $p.",
		ch->level);
	wiznet(buf,
		ch, tattoo, WIZ_QUESTS, 0, 0);

	return TRUE;
}

/* buy consitution points
 * 	only for those truely in desperate straits
 *
 * by Zsuzsu
 */
static bool buy_con(CHAR_DATA *ch, CHAR_DATA *questor)
{
	CHAR_DATA *vch;
	char buf[MAX_STRING_LENGTH];

	if (ch->level < LEVEL_HERO) {
		quest_tell(ch, questor,
		"Sorry, but, {W%s{z, but you'll have to earn"
		" Hero status before you can do that.", 
			ch->name);
		return FALSE;
	}
	if (ch->perm_stat[STAT_CON] >= get_max_train(ch,STAT_CON)) {
		quest_tell(ch, questor, 
			"You look healthy enough to me, {W%s{z.",
			ch->name);
		return FALSE;
	}

	if (ch->train > 0 || ch->practice > 9) {
		quest_tell(ch, questor, 
			"You don't look that bad off."
			" Perhaps you should visit the trainer before coming to me.",
			ch->name);
		return FALSE;
	}

	act("$n looks up to the sky and mumbles in conversation before"
		" turning $s gaze back to $N.  With a wink, a pulse of "
		" {mviolet{x light spreads over $N into the corners of the room.",
		questor, NULL, ch, TO_NOTVICT);
	act_puts("$n looks up to the sky and mumbles in conversation before"
		" turning $s gaze back to you.  With a wink, a pulse of"
		" {mviolet{x light spreads over you and into the corners of the room.",
		questor, NULL, ch, TO_VICT, POS_DEAD);

	ch->perm_stat[STAT_CON] += 1;
	act_puts("Your constitution returns!",
		 ch, NULL, NULL, TO_CHAR, POS_DEAD);

	/* just a little something for everyone in the room */
	for (vch = ch->in_room->people; vch; vch = vch->next_in_room) {
		affect_strip(vch,gsn_plague);
		affect_strip(vch,gsn_poison);
		affect_strip(vch,gsn_blindness);
		affect_strip(vch,gsn_curse);

		if (vch->hit < vch->max_hit)
			vch->hit        = vch->max_hit;

		act_puts("The {mviolet{x light restores you.",
			 vch, NULL, NULL, TO_CHAR, POS_DEAD);
	}

	snprintf(buf, sizeof(buf),
		"$N[%d] {mquest bought{x {Wconstitution{x[%d].",
		ch->level,
		ch->perm_stat[STAT_CON]);
	wiznet(buf,
		ch, NULL, WIZ_QUESTS, 0, 0);


	return TRUE;
}

static bool buy_death(CHAR_DATA *ch, CHAR_DATA *questor)
{
	char buf[MAX_STRING_LENGTH];

	if (ch->pcdata->death < 1) {
		quest_tell(ch, questor, 
			   "Sorry, {W%s{z, but you haven't got any deaths yet.",
			   ch->name);
		return FALSE;
	}

	ch->pcdata->death -= 1;

	snprintf(buf, sizeof(buf),
		"$N[%d] {mquest bought{x a {Ddeath{x[%d].",
		ch->level,
		ch->pcdata->death);
	wiznet(buf,
		ch, NULL, WIZ_QUESTS, 0, 0);

	return TRUE;
}

static bool buy_katana(CHAR_DATA *ch, CHAR_DATA *questor)
{
	AFFECT_DATA af;
	OBJ_DATA *katana;

	if ((katana = get_obj_list(ch, "katana", ch->carrying)) == NULL) {
		quest_tell(ch, questor, "Sorry, {W%s{z, but you don't have your katana with you.",
			   ch->name);
		return FALSE;
	}

	af.where	= TO_WEAPON;
	af.type 	= gsn_katana;
	af.level	= 100;
	af.duration	= -1;
	af.modifier	= 0;
	af.bitvector	= WEAPON_KATANA_QUEST;
	af.location	= APPLY_NONE;
	affect_to_obj(katana, &af);
	quest_tell(ch, questor, "As you wield it, you will feel that its power will increase continuosly.");
	return TRUE;
}

static bool buy_vampire(CHAR_DATA *ch, CHAR_DATA *questor)
{
	set_skill(ch, gsn_vampire, 100);
	act("$N tells a creepy secret to $n.", ch, NULL, questor, TO_ROOM);
	act_puts("$N lets you in on the secret of the undead.",
		 ch, NULL, questor, TO_CHAR, POS_DEAD);
	act("Lightning flashes in the sky.", ch, NULL, NULL, TO_ALL);
	return TRUE;
}

/* look to see if the character already has a similar item */
bool quest_has_similar (CHAR_DATA *ch, int vnum) {
	qtrouble_t *qt		= NULL;
	bool has_similar	= FALSE;
	int quest_group = -1;
	int j = 0;

	quest_group = quest_item_group(vnum);

	if (quest_group == -1)
		return FALSE;

	j = 0;
	while (!has_similar && quest_item_groups[quest_group][j]) {
		/* if not the same item
		 * and there exists a trouble record
		 * and troubles are infinite
		 * or the trouble count is lower than the MAX */
		if (quest_item_groups[quest_group][j] != vnum
		&& (qt = qtrouble_lookup(ch, quest_item_groups[quest_group][j])) != NULL
		&& (TROUBLE_MAX == -1 || qt->count <= TROUBLE_MAX))
			has_similar = TRUE;

		/* look to see if they have an item that's out of troubles
		 * but still in the world */
		else if (quest_item_groups[quest_group][j] != vnum
		&& quest_item_in_world(ch, quest_item_groups[quest_group][j]))
			has_similar = TRUE;
		j++;
	}

	return has_similar;
}

/* extract all similar items */
int quest_extract_similar (CHAR_DATA *ch, int vnum) {
	int quest_group = -1;
	int j = 0;
	int count = 0;

	quest_group = quest_item_group(vnum);

	if (quest_group == -1)
		return FALSE;

	j = 0;
	while (quest_item_groups[quest_group][j]) {
		count += quest_item_extract(ch, quest_item_groups[quest_group][j]);
		j++;
	}
	return count;
}

/*
 * find out which group the item is in to check for similarity
 */
int quest_item_group (int vnum) {
	int quest_group = -1;
	int i = 0;
	int j = 0;

	/* find out which group our vnum is in */
	while (quest_group == -1 && quest_item_groups[i][0]) {
		while (quest_item_groups[i][j]) {
			if (quest_item_groups[i][j] == vnum) {
				quest_group = i;
				break;
			}
			j++;
		}
		i++;
	}
	return quest_group;
}

/*
 * find your quest item in the world and extract it
 * so there are no duplicates
 *
 * returns the number of them extracted
 */
int quest_item_extract(CHAR_DATA *ch, int vnum) {
	OBJ_DATA *obj;
	OBJ_DATA *obj_next;
	int count = 0;

	if (vnum <= 0) return 0;

	for (obj = object_list; obj != NULL; obj = obj_next) {
		obj_next = obj->next;
		if (obj->pIndexData->vnum == vnum 
		&&  IS_OWNER(ch, obj)) {
			extract_obj(obj, 0);
			count++;
			break;
		}
	}
	return count;
}

/*
 * return the number of this type of quest item in 
 * the world that belongs to this player
 */
int quest_item_in_world (CHAR_DATA *ch, int vnum) {
	OBJ_DATA *obj;
	OBJ_DATA *obj_next;
	int count = 0;

	if (vnum <= 0) return 0;

	for (obj = object_list; obj != NULL; obj = obj_next) {
		obj_next = obj->next;
		if (obj->pIndexData->vnum == vnum 
		&&  IS_OWNER(ch, obj)) {
			count++;
			break;
		}
	}
	return count;
}

bool is_quest_item (OBJ_INDEX_DATA *pObj)
{
	switch (pObj->vnum) {
		case QUEST_VNUM_RING:
		case QUEST_VNUM_BRACER:
		case QUEST_VNUM_AMULET:
		case QUEST_VNUM_SONG:

		case QUEST_VNUM_LANCE:
		case QUEST_VNUM_BOW:
		case QUEST_VNUM_POLEARM:
		case QUEST_VNUM_BASTARDSWORD:
		case QUEST_VNUM_BATTLEAXE:
		case QUEST_VNUM_WARHAMMER:
		case QUEST_VNUM_LONGSWORD:
		case QUEST_VNUM_AXE:
		case QUEST_VNUM_HAMMER:
		case QUEST_VNUM_SHORTSWORD:
		case QUEST_VNUM_STILETTO:
		case QUEST_VNUM_DAGGER:
		case QUEST_VNUM_FLAIL:
		case QUEST_VNUM_MACE:
		case QUEST_VNUM_SPEAR:
		case QUEST_VNUM_STAFF:
		case QUEST_VNUM_WHIP:
			return TRUE;
	}
	return FALSE;
}

bool quest_item_usable (CHAR_DATA *ch, QITEM_DATA *qitem) {
	class_t *cl = class_lookup(ch->class);
	int group = -1;

	if (!qitem || !cl)
		return FALSE;

	/* if the item is restricted to a class */
	if (qitem->restrict_class != NULL) {
		if (is_name(cl->name, qitem->restrict_class))
			return TRUE;
		else
			return FALSE;
	}

	if (!qitem->vnum)
		return TRUE;

	if (IS_NEWBIE(ch) && qitem->vnum == QUEST_VNUM_RUG)
		return FALSE;

	group = quest_item_group(qitem->vnum);

	if (ch->class == CLASS_SAMURAI
	&& group == QUEST_GROUP_WEAPON)
		return FALSE;

	if (ch->class == CLASS_CLERIC
	&& qitem->vnum ==  QUEST_VNUM_WARHAMMER)
		return FALSE;

	if ((ch->race == RACE_DWARF
	|| ch->race == RACE_DUERGAR
	|| ch->size < SIZE_MEDIUM)
	&& (qitem->vnum == QUEST_VNUM_POLEARM
	|| qitem->vnum == QUEST_VNUM_BASTARDSWORD))
		return FALSE;

	if (!qitem->gsn)
		return TRUE;

	if (get_skill(ch, *(qitem->gsn)) <= 0)
		return FALSE;

	return TRUE;
}

QITEM_DATA * quest_item_by_vnum (int vnum)
{
	QITEM_DATA *qitem;
	
	for (qitem = qitem_table; qitem->name; qitem++)
		if (qitem->vnum == vnum)
			return qitem;

	return NULL;
}

/*
 * set dynamic prices for progressive quest rewards
 */
int quest_item_price (CHAR_DATA *ch, QITEM_DATA *qitem)
{
	int price = qitem->price;

		switch (qitem->vnum) {
			case QUEST_VNUM_RUG:
				price = qitem->price * URANGE(10, ch->level, LEVEL_HERO);
				break;
		}

		if (qitem->vnum != 0 && IS_NEWBIE(ch))
			price = price * QUEST_NEWBIE_DISCOUNT;

		/* samurai deaths */
		if (qitem->do_buy == buy_death)
			price = price * URANGE(1, ch->level, LEVEL_HERO);


	return price;
}

bool is_quest_complete (CHAR_DATA *ch)
{
	OBJ_DATA *obj, *obj_next;
	bool complete = FALSE;

	if (IS_NPC(ch))
		return FALSE;

	if (!IS_ON_QUEST(ch))
		return FALSE;

	if (ch->pcdata->questobj > 0)
		for (obj = ch->carrying; obj; obj = obj_next) {
			obj_next = obj->next_content;

			if (obj->pIndexData->vnum == ch->pcdata->questobj
			&&  IS_OWNER(ch, obj)) {
				complete = TRUE;
				break;
			}
		}
	else if (ch->pcdata->questmob == -1) {
		complete = TRUE;
	}

	return complete;
}