Rot/deity/
Rot/player/
Rot/src/utils/pfiles/
Rot/src/utils/www/
/***************************************************************************
*   Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*   Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                          *
*   Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
*   Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                          *
*   In order to use any part of this Merc Diku Mud, you must comply with   *
*   both the original Diku license in 'license.doc' as well the Merc       *
*   license in 'license.txt'.  In particular, you may not remove either of *
*   these copyright notices.                                               *
*                                                                          *
*   Much time and thought has gone into this software and you are          *
*   benefitting.  We hope that you share your changes too.  What goes      *
*   around, comes around.                                                  *
***************************************************************************/
/***************************************************************************
*       ROM 2.4 is copyright 1993-1995 Russ Taylor                         *
*       ROM has been brought to you by the ROM consortium                  *
*           Russ Taylor (rtaylor@pacinfo.com)                              *
*           Gabrielle Taylor (gtaylor@pacinfo.com)                         *
*           Brian Moore (rom@rom.efn.org)                                  *
*       By using this code, you have agreed to follow the terms of the     *
*       ROM license, in the file 'rom.license'                             *
***************************************************************************/
/***************************************************************************
*       ROT 2.0 is copyright 1996-1999 by Russ Walsh                       *
*       By using this code, you have agreed to follow the terms of the     *
*       ROT license, in the file 'rot.license'                             *
***************************************************************************/

#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "merc.h"
#include "tables.h"

/* command procedures needed */
DECLARE_DO_FUN(	do_give		);
DECLARE_DO_FUN( do_grats	);

/* tables for level quest object names */
char *	const	adj_table	[] =
{
    "light ",
    "heavy ",
    "emaculate ",
    "dark ",
    "red ",
    "worn ",
    "old ",
    "golden ",
    "silver ",
    "tarnished ",
    "obsidian ",
    "black ",
    "ancient ",
    "smelly ",
    "stone ",
    "weathered ",
    "enchanted "
};

char *	const	qot_table	[] =
{
    " coin",
    " tablet",
    " parchment",
    " sword",
    " feather",
    " knife",
    " amulet",
    " ring",
    " bracelet",
    " necklace",
    " disc",
    " staff",
    " idol",
    " figurine",
    " scroll",
    " flame"
};

char *	const	pho_table	[] =
{
    "ob",
    "in",
    "ta",
    "il",
    "at",
    "as",
    "sa",
    "ba",
    "ha",
    "ab",
    "it",
    "no",
    "al",
    "am",
    "ma",
    "da",
    "ra",
    "ro",
    "or",
    "ar",
    "eg"
};

/*
 * Local functions.
 */
char *	find_quest_mob		args( ( CHAR_DATA *ch, bool lquest ) );
char *	make_lquest_obj		args( ( CHAR_DATA *ch ) );
bool	is_suitable_qmob	args( ( CHAR_DATA *ch,
				    CHAR_DATA *victim, bool lquest ) );

void do_aquest(CHAR_DATA *ch, char *argument)
{
    char buf[MAX_STRING_LENGTH];
    CHAR_DATA *questmaster;
    MOB_INDEX_DATA *pMobIndex;
    char *which_mob;
    char mob_vnum[MAX_INPUT_LENGTH];

    if (IS_NPC(ch))
	return;

    buf[0] = '\0';
    /* find a questmaster */
    for ( questmaster = ch->in_room->people; 
	  questmaster != NULL; 
	  questmaster = questmaster->next_in_room)
	if (IS_NPC(questmaster) && IS_SET(questmaster->act,ACT_QUESTMASTER))
	    break;

    if (questmaster == NULL || !can_see(ch,questmaster))
    {
	send_to_char("You can't do that here.\n\r",ch);
	return;
    }

    if (ch->spirit)
    {
	send_to_char( "Spirits cannot autoquest.\n\r", ch);
	return;
    }

    if ((ch->pcdata->tier == 0) && (ch->level < 10))
    {
	send_to_char( "You must be at least level 10 to autoquest.\n\r", ch);
	return;
    }

    if ((ch->can_aquest == 1) && (ch->level < LEVEL_HERO)
    && !IS_SET(ch->act, PLR_LQUEST) && (!ch->pcdata->is_aquest))
    {
	send_to_char( "You may not autoquest again till next level.\n\r", ch);
	return;
    }

    if (!can_see(questmaster,ch))
    {
	act("$N tells you '{aI must see you to judge your worthiness to quest{x'."
	    ,ch,NULL,questmaster,TO_CHAR);
	return;
    }

    if (IS_SET(ch->act, PLR_LQUEST))
    {
	int level_vnum;
	char *object_name;

	level_vnum = ((ch->level/10) + 56);
	if (ch->can_aquest == 2)
	{
	    bool found = FALSE;
	    OBJ_DATA *object;
	    OBJ_INDEX_DATA *pObjIndex;

	    for ( object = ch->carrying; object != NULL; object = object->next_content )
	    {
		if (IS_OBJ_STAT(object,ITEM_LQUEST)
		&& (object->pIndexData->vnum == level_vnum))
		    found = TRUE;
	    }
	    if (found)
	    {
		act("$N tells you '{aWell done, my child, you may now advance to the next level{x'."
		,ch,NULL,questmaster,TO_CHAR);
		ch->can_aquest = 0;
		ch->pcdata->is_aquest = FALSE;
		pObjIndex = get_obj_index( OBJ_VNUM_QUESTPILL );
		object = create_object( pObjIndex, 0 );
		obj_to_char( object, ch );
		act( "$n gives $p to $N.", questmaster, object, ch, TO_NOTVICT );
		act( "$n gives you $p.",   questmaster, object, ch, TO_VICT    );
		ch->exp += 1;
		REMOVE_BIT(ch->act, PLR_LQUEST);
		ch->level += 1;
		change_level( ch );
		if (ch->level == LEVEL_HERO)
		{
		    OBJ_DATA *obj_next;

		    sprintf(buf, "Please welcome the realms newest Hero -> %s",
			capitalize(ch->name) );
		    do_grats(questmaster, buf);
		    buf[0] = '\0';
		    for ( object = ch->carrying; object != NULL; object = obj_next )
		    {
			obj_next = object->next_content;
			if (IS_OBJ_STAT(object,ITEM_LQUEST))
			{
			    extract_obj( object );
			}
		    }
		    for ( object = ch->carrying; object != NULL; object = obj_next )
		    {
			obj_next = object->next_content;
			if (object->pIndexData->vnum == OBJ_VNUM_QPOUCH)
			{
			    extract_obj( object );
			    break;
			}
		    }
		    act("$N takes your quest pouch from you.",ch,NULL,questmaster,TO_CHAR);
		    act("$N tells you '{aI can't yet give you an award for reaching Hero, because Russ hasn't finished my code. (so blame HIM, not ME!){x'.",ch,NULL,questmaster,TO_CHAR);
		}
		return;
	    }
	    sprintf(buf, "$N tells you '{aYour level quest is not yet complete,\n\rplease see me when you have the %s{x'.",
		str_dup(ch->pcdata->lquest_obj));
	    act(buf,ch,NULL,questmaster,TO_CHAR);
	    buf[0] = '\0';
	    return;
	}


	ch->can_aquest = 2;
	act("$N tells you:",ch,NULL,questmaster,TO_CHAR);

	send_to_char("  {aGood day to you, my child, the following task will allow you to{x\n\r", ch);
	send_to_char("  {aadvance to the next level.{x\n\r\n\r", ch);

	object_name = make_lquest_obj(ch);
	which_mob = find_quest_mob(ch, TRUE);
	which_mob = one_argument(which_mob, mob_vnum);
	if ((pMobIndex = get_mob_index(atoi(mob_vnum))) == NULL )
	{
	    sprintf(buf, "  {aI can't seem to find a suitable enemy for your level.{x\n\r\n\r");
	    send_to_char(buf, ch);
	    buf[0] = '\0';
	    return;
	}

	ch->pcdata->is_aquest = FALSE;
	ch->pcdata->lquest_obj = str_dup(object_name);
	ch->pcdata->lquest_mob = str_dup(pMobIndex->short_descr);
	ch->pcdata->lquest_are = str_dup(which_mob);
	ch->pcdata->quest_mob = pMobIndex->vnum;
	sprintf(buf, "  {aYou must find the {W%s{a and bring it back to me.{x\n\r",
		str_dup(ch->pcdata->lquest_obj));
	send_to_char(buf, ch);
	sprintf(buf, "  {aI have heard rumors that it was last seen in {W%s{a,{x\n\r",
		str_dup(ch->pcdata->lquest_are));
	send_to_char(buf, ch);
	sprintf(buf, "  {aand that {W%s{a was bragging of it's powers.{x\n\r\n\r",
		str_dup(ch->pcdata->lquest_mob));
	send_to_char(buf, ch);
	buf[0] = '\0';
	return;
    }

    if (ch->pcdata->is_aquest)
    {
	bool found = FALSE;
	OBJ_DATA *object;
	OBJ_INDEX_DATA *pObjIndex;

	for ( object = ch->carrying; object != NULL; object = object->next_content )
	{
	    if ((object->pIndexData->vnum == ch->pcdata->quest_obj)
	    && (object->got_from == ch->pcdata->quest_mob))
		found = TRUE;
	}
	if (found)
	{
	    act("$N tells you '{aWell done, my child, here is your reward{x'."
	    ,ch,NULL,questmaster,TO_CHAR);
	    ch->pcdata->is_aquest = FALSE;
	    pObjIndex = get_obj_index( OBJ_VNUM_QUESTPILL );
	    object = create_object( pObjIndex, 0 );
	    obj_to_char( object, ch );
	    act( "$n gives $p to $N.", questmaster, object, ch, TO_NOTVICT );
	    act( "$n gives you $p.",   questmaster, object, ch, TO_VICT    );
	    return;
	}
	act("$N tells you '{aYour quest is not yet complete{x'.",ch,NULL,questmaster,TO_CHAR);
	return;
    }

    act("$N tells you:",ch,NULL,questmaster,TO_CHAR);

    send_to_char("  {aGood day to you, my child, I require that you complete the{x\n\r", ch);
    send_to_char("  {afollowing task for a quest point.{x\n\r\n\r", ch);

    which_mob = find_quest_mob(ch, FALSE);
    which_mob = one_argument(which_mob, mob_vnum);
    if ((pMobIndex = get_mob_index(atoi(mob_vnum))) == NULL )
    {
	sprintf(buf, "  {aI can't seem to find a suitable enemy for your level.{x\n\r\n\r");
	send_to_char(buf, ch);
	buf[0] = '\0';
	return;
    }

    {
	OBJ_INDEX_DATA *pObjIndex;

	if ((pObjIndex = get_obj_index(ch->pcdata->quest_obj)) == NULL )
	{
	    sprintf(buf, "  {aI can't seem to find a suitable enemy for your level.{x\n\r\n\r");
	    send_to_char(buf, ch);
	    buf[0] = '\0';
	    return;
	}
	ch->can_aquest = 1;
	ch->pcdata->is_aquest = TRUE;
	ch->pcdata->quest_mob = pMobIndex->vnum;
	ch->pcdata->lquest_obj = str_dup(pObjIndex->short_descr);
	ch->pcdata->lquest_mob = str_dup(pMobIndex->short_descr);
	ch->pcdata->lquest_are = str_dup(which_mob);
	sprintf(buf, "  {aYou must retrieve {W%s{a from {W%s{a.{x\n\r",
	    str_dup(pObjIndex->short_descr), str_dup(pMobIndex->short_descr));
	send_to_char(buf, ch);
	sprintf(buf, "  {aThis creature makes it's home in {W%s{a.{x\n\r\n\r",
	    str_dup(which_mob));
	send_to_char(buf, ch);
	buf[0] = '\0';
    }
    return;

}

/* Find a nice, suitable mob to kill. */

char *find_quest_mob( CHAR_DATA *ch, bool lquest )
{
    MOB_INDEX_DATA *pMobIndex;
    int min_lev;
    int max_lev;
    int timeout;

    /*	if a level quest, go as high as 10 levels above the player and
	2 levels below the player.  If an autoquest, go as high as
	6 above and as low as 6 below.
    */
    if (!lquest)
    {
	min_lev = ch->level - 6;
	max_lev = ch->level + 6;
    } else {
	min_lev = ch->level - 2;
	max_lev = ch->level + 10;
    }

    /* First, let's just choose 500 mobs at random */

    for (timeout = 0; timeout < 500; timeout++)
    {
	CHAR_DATA *victim;

	while ((pMobIndex = get_mob_index(number_range(1, 32767))) == NULL)
	{ }

	if ( pMobIndex->level < min_lev )
	    continue;
	if ( pMobIndex->level > max_lev )
	    continue;

	/* No shopkeepers */
	if ( pMobIndex->pShop != NULL )
	    continue;

	for ( victim = char_list; victim != NULL; victim = victim->next )
	{
	    if ( victim->in_room != NULL
	    &&   is_name( pMobIndex->player_name, victim->name ) )
	    {
		if (!is_suitable_qmob(ch, victim, lquest))
		    continue;
		break;
	    }
	}
	if (victim == NULL)
	    continue;
	else
	{
	    char rt[MAX_STRING_LENGTH];

	    sprintf(rt, "%d %s", victim->pIndexData->vnum,
		str_dup(victim->in_room->area->name));
	    return str_dup(rt);
	}
    }

    /* No suitable mobs found, try a different method.
       This time, we start at a random number and work in
       both directions till we find a suitable mob.
    */

    {
	int vnumlo;
	int vnumhi;

	vnumlo = number_range(1, 32767);
	vnumhi = vnumlo;
	for ( ; vnumlo >= 0 && vnumhi <= 32766; vnumlo--, vnumhi++)
	{
	    CHAR_DATA *victim;
	    if (vnumlo < 0) {vnumlo = 0;}
	    if (vnumhi > 32766) {vnumhi = 32766;}
	    if ((pMobIndex = get_mob_index(vnumlo)) != NULL)
	    {
	      for ( ; ; )
	      {
		if ( pMobIndex->level < min_lev )
		    break;
		if ( pMobIndex->level > max_lev )
		    break;

		/* No shopkeepers */
		if ( pMobIndex->pShop != NULL )
		    break;

		for (victim = char_list; victim != NULL; victim = victim->next)
		{
		    if ( victim->in_room != NULL
		    &&   is_name( pMobIndex->player_name, victim->name ) )
		    {
			if (!is_suitable_qmob(ch, victim, lquest))
			    continue;
			break;
		    }
		}
		if (victim == NULL)
		    break;
		else
		{
		    char rt[MAX_STRING_LENGTH];

		    sprintf(rt, "%d %s", victim->pIndexData->vnum,
			str_dup(victim->in_room->area->name));
		    return str_dup(rt);
		}
	      }
	    }
	    if ((pMobIndex = get_mob_index(vnumhi)) != NULL)
	    {
	      for ( ; ; )
	      {
		if ( pMobIndex->level < min_lev )
		    break;
		if ( pMobIndex->level > max_lev )
		    break;

		/* No shopkeepers */
		if ( pMobIndex->pShop != NULL )
		    break;

		for (victim = char_list; victim != NULL; victim = victim->next)
		{
		    if ( victim->in_room != NULL
		    &&   is_name( pMobIndex->player_name, victim->name ) )
		    {
			if (!is_suitable_qmob(ch, victim, lquest))
			    continue;
			break;
		    }
		}
		if (victim == NULL)
		    break;
		else
		{
		    char rt[MAX_STRING_LENGTH];

		    sprintf(rt, "%d %s", victim->pIndexData->vnum,
			str_dup(victim->in_room->area->name));
		    return str_dup(rt);
		}
	      }
	    }
	}
    }
    /* Still no suitable mobs found, give up */

    return "0 0";
}


/* Check to see if victim is a suitable quest mob.
   No PCs, Pets, Trainers/Guildmasters, Healers, Bankers,
   Priests, Questmasters, Clan Guards, Charmed Mobs,
   or mobs in safe rooms, school, imm area, midgaard,
   or clan rooms.
*/
bool is_suitable_qmob(CHAR_DATA *ch, CHAR_DATA *victim, bool lquest)
{
    if (!IS_NPC(victim))
	return FALSE;
    if (IS_SET(victim->in_room->room_flags,ROOM_SAFE))
	return FALSE;
    if (!can_see(ch, victim))
	return FALSE;
    if (IS_SET(victim->act,ACT_TRAIN)
    ||  IS_SET(victim->act,ACT_PRACTICE)
    ||  IS_SET(victim->act,ACT_IS_HEALER)
    ||  IS_SET(victim->act,ACT_PET)
    ||  IS_SET(victim->act,ACT_QUESTMASTER)
    ||  IS_SET(victim->act,ACT_IS_BANKER)
    ||  IS_SET(victim->act,ACT_IS_SATAN)
    ||  IS_SET(victim->act,ACT_IS_PRIEST))
	return FALSE;
    if (IS_SET(victim->in_room->room_flags,ROOM_CLAN_ENT))
	return FALSE;
    if (IS_AFFECTED(victim,AFF_CHARM))
	return FALSE;
    if (victim->in_room->area->min_vnum == ROOM_VNUM_SCHOOL)
	return FALSE;
    if (victim->in_room->area->min_vnum == ROOM_VNUM_CHAT)
	return FALSE;
    if (victim->in_room->area->min_vnum == ROOM_VNUM_MIDGAARD)
	return FALSE;
    if (victim->in_room->area->min_vnum == ROOM_VNUM_CLANS)
	return FALSE;

    /*	Since the quest object is created in the players
	inventory in a level quest, the mob does not
	have to leave a corpse, nor does it have to be
	carrying anything.  But in an autoquest,
	a no_body mob is unacceptable, and it must have
	a suitable object for the quest.
    */
    if (!lquest)
    {
	OBJ_DATA *obj;

	if (IS_SET(victim->act,ACT_NO_BODY))
	    return FALSE;
	if (victim->carrying == NULL)
	    return FALSE;
	for ( obj = victim->carrying; obj != NULL; obj = obj->next_content )
	{
	    if (!can_see_obj(ch, obj))
		continue;
	    if ((obj->item_type != ITEM_LIGHT)
	    &&  (obj->item_type != ITEM_WAND)
	    &&  (obj->item_type != ITEM_STAFF)
	    &&  (obj->item_type != ITEM_WEAPON)
	    &&  (obj->item_type != ITEM_TREASURE)
	    &&  (obj->item_type != ITEM_ARMOR)
	    &&  (obj->item_type != ITEM_CLOTHING)
	    &&  (obj->item_type != ITEM_TRASH)
	    &&  (obj->item_type != ITEM_CONTAINER)
	    &&  (obj->item_type != ITEM_DRINK_CON)
	    &&  (obj->item_type != ITEM_WARP_STONE)
	    &&  (obj->item_type != ITEM_JEWELRY)
	    &&  (obj->item_type != ITEM_DEMON_STONE))
		continue;

	    if ((IS_OBJ_STAT(obj, ITEM_NODROP))
	    ||  (IS_OBJ_STAT(obj, ITEM_NOREMOVE))
	    ||  (IS_OBJ_STAT(obj, ITEM_INVENTORY))
	    ||  (IS_OBJ_STAT(obj, ITEM_ROT_DEATH))
	    ||  (IS_OBJ_STAT(obj, ITEM_VIS_DEATH))
	    ||  (IS_OBJ_STAT(obj, ITEM_MELT_DROP))
	    ||  (IS_OBJ_STAT(obj, ITEM_NOUNCURSE))
	    ||  (IS_OBJ_STAT(obj, ITEM_QUEST))
	    ||  (IS_OBJ_STAT(obj, ITEM_FORCED))
	    ||  (IS_OBJ_STAT(obj, ITEM_QUESTPOINT))
	    ||  (IS_OBJ_STAT(obj, ITEM_LQUEST)))
		continue;

	    ch->pcdata->quest_obj = obj->pIndexData->vnum;
	    return TRUE;
	}
	return FALSE;
    }

    return TRUE;
}


/* Make an object name for a level quest. */

char *make_lquest_obj( CHAR_DATA *ch )
{
    char lquest_obj[MAX_STRING_LENGTH];
    char lobj_adj[100];
    char lobj_pho[100];
    char lobj_typ[100];
    int phonum = 0;
    int phochoice = 0;
    int pholast = -1;

    lquest_obj[0] = '\0';
    if (number_range(0, 100) < 26)
    {
	sprintf(lobj_adj, "%s", adj_table[number_range(0, 16)]);
    } else {
	lobj_adj[0] = '\0';
    }
    phonum = number_range(3, 5);
    lobj_pho[0] = '\0';
    for ( ; phonum > 0; phonum--)
    {
	while ((phochoice = number_range(0, 20)) == pholast) { }
	strcat(lobj_pho, pho_table[phochoice]);
	pholast = phochoice;
    }
    sprintf(lobj_typ, "%s", qot_table[number_range(0, 15)]);
    sprintf(lquest_obj, "%s%s%s", lobj_adj, lobj_pho, lobj_typ);
    return str_dup(lquest_obj);
}