/***************************************************************************
 *  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.                                               *
 *                                                                         *
 *  Dystopia Mud improvements copyright (C) 2000, 2001 by Brian Graversen  *
 *                                                                         *
 *  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.                                                  *
 ***************************************************************************/

/***************************************************************************
 *  Dystopian Questcode copyright (C) 2001 by Brian Graversen, users must  *
 *  follow the DIKU, Merc and Godwars license as well as the license       *
 *  distributed with Dystopia                                              *
 ***************************************************************************/

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

int  get_rand_mob              args (( void ));
int  get_rand_hard_mob         args (( void ));
int  get_rand_item             args (( void ));
int  load_special_item         args (( CHAR_DATA *ch ));
int  get_lbound                args (( void ));
int  get_ubound                args (( int bound ));
void give_token                args (( CHAR_DATA *questmaster, CHAR_DATA *ch, int value ));

QUEST_DATA *quest_free;

DECLARE_QUEST_FUN( questspec_generic		);
DECLARE_QUEST_FUN( questspec_newbie		);
DECLARE_QUEST_FUN( questspec_special_item	);
DECLARE_QUEST_FUN( questspec_rand_mob		);
DECLARE_QUEST_FUN( questspec_mob_and_item	);
DECLARE_QUEST_FUN( questspec_hard_mob		);
DECLARE_QUEST_FUN( questspec_mass_kill		);
DECLARE_QUEST_FUN( questspec_pk                 );

const struct quest_type quest_table [] =
{
  { "questspec_generic",         questspec_generic          },
  { "questspec_special_item",    questspec_special_item     },
  { "questspec_rand_mob",        questspec_rand_mob         },
  { "questspec_mob_and_item",    questspec_mob_and_item     },
  { "questspec_hard_mob",        questspec_hard_mob         },
  { "questspec_mass_kill",       questspec_mass_kill        },
  { "questspec_pk",              questspec_pk               },
  { "questspec_newbie",          questspec_newbie           },

  /* end of table */
  { "", 0 }
};

/*
 * Used for show_char_to_char_0()
 */
bool is_quest_target(CHAR_DATA *ch, CHAR_DATA *victim)
{
  QUEST_DATA *quest;

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

  for (quest = ch->pcdata->quests; quest; quest = quest->next)
  {
    switch(quest->type)
    {
      case QT_MOB:
        if (victim->pIndexData->vnum == quest->vnums[0])
          return TRUE;
        break;
      case QT_MOB_AND_OBJ:
        if (victim->pIndexData->vnum == quest->vnums[0])
          return TRUE;
        break;
      case QT_MASS_KILL:
        if (victim->pIndexData->vnum >= quest->vnums[0] &&
            victim->pIndexData->vnum <= quest->vnums[1])
          return TRUE;
        break;
      default: continue;
    }
  }
  return FALSE;
}

/* 
 * This is a temp function for creating the database of quest
 * monsters, that needs to be slain. Please don't run it
 * while the mud is operationel - it takes quite a while
 * to execute.
 */
void do_createbase(CHAR_DATA *ch, char *argument)
{
  AREA_DATA *area;
  DESCRIPTOR_DATA *d;
  ROOM_INDEX_DATA *pRoom;
  MOB_INDEX_DATA *pMobIndex;
  RESET_DATA *pReset;
  FILE *fp;
  char arg[MAX_INPUT_LENGTH];
  char buf[MAX_STRING_LENGTH];
  int vnum, vnum2, iMin, iMax, col = 0;
  bool found = FALSE;

  if ((d = ch->desc) == NULL)
  {
    send_to_char("Huh?\n\r", ch);
    return;
  }

  one_argument(argument, arg);

  /* put in what ever levels you want here */
  if (!str_cmp(arg, "big"))
  {
    iMin = 800;
    iMax = 2500;
  }
  else if (!str_cmp(arg, "small"))
  {
    iMin = 100;
    iMax = 400;
  }
  else
  {
    send_to_char("Big or Small moblist ??\n\r", ch);
    return;
  }

  if ((fp = fopen("../txt/questlist.txt", "w")) == NULL)
  {
    send_to_char("Unable to create questlist.txt - terminating attempt\n\r", ch);
    return;
  }

  for (area = area_first; area && !found; area = area->next)
  {
    if (area == area_first) write_to_descriptor(d, "\n\r", 0);
    sprintf(buf, "Processing %s....", area->name);
    write_to_descriptor(d, buf, 0);

    fprintf(fp, "/* %s */\n", area->name);
    found = FALSE; // reset
    for (vnum = area->lvnum; vnum <= area->uvnum; vnum++)
    {
      if ((pMobIndex = get_mob_index(vnum)) != NULL)
      {
        if (pMobIndex->level < iMin || pMobIndex->level > iMax) continue;

        for (vnum2 = area->lvnum; vnum2 <= area->uvnum; vnum2++)
        {
          if ((pRoom = get_room_index(vnum2)) != NULL)
          {
            for (pReset = pRoom->reset_first; pReset; pReset = pReset->next)
            {
              switch (pReset->command)
              {
                default: break;
                case 'M':
                  if ((pMobIndex = get_mob_index(pReset->arg1)) != NULL)
                  {
                    if (pMobIndex->vnum == vnum) found = TRUE;
                  }
              }
            }
          }
        }
        if (found)
        {
          found = FALSE; // reset for later use
          fprintf(fp, " %6d,", vnum);
          if (!(++col % 6)) fprintf(fp, "\n");
        }
      }
    }
    if (col % 6) fprintf(fp, "\n");
    if (area->next) write_to_descriptor(d, "Ok!\n\r", 0);
    else write_to_descriptor(d, "Ok!", 0);
  }
  fclose(fp);
  return;
}

int get_lbound()
{
  int vnum, count = 0;

  static const int mob_table[] = {
  14003, 31108, 1105, 1122, 1602, 1702, 1710, 2007,
  2101, 2201, 2307, 2315, 2801, 2904, 3406, 3601, 4150,
  4700, 5000, 5006, 2612, 6303, 6507, 6601, 7200, 7801,
  8000, 8308, 8904, 9203, 9401, 11006, 7705, 93000, 80001,

  /* END */
    -1
  };

  /* counting the size of the table */
  for (count = 0; mob_table[count] != -1; count++) ;

  vnum = number_range(0, count-1);
  return mob_table[vnum];
}

int get_ubound(int bound)
{
  int vnum;

  switch(bound)
  {
    case 14003 : vnum = 14011; break;
    case 31108 : vnum = 31118; break;
    case 1105 : vnum = 1109; break;
    case 1122 : vnum = 1125; break;
    case 1602 : vnum = 1605; break;
    case 1702 : vnum = 1708; break;
    case 1710 : vnum = 1716; break;
    case 2007 : vnum = 2013; break;
    case 2101 : vnum = 2108; break;
    case 2201 : vnum = 2205; break;
    case 2307 : vnum = 2311; break;
    case 2315 : vnum = 2321; break;
    case 2801 : vnum = 2808; break;
    case 2904 : vnum = 2913; break;
    case 3406 : vnum = 3408; break;
    case 3601 : vnum = 3604; break;
    case 4150 : vnum = 4154; break;
    case 4700 : vnum = 4708; break;
    case 5000 : vnum = 5003; break;
    case 5006 : vnum = 5008; break;
    case 2612 : vnum = 2616; break;
    case 6303 : vnum = 6310; break;
    case 6507 : vnum = 6510; break;
    case 6601 : vnum = 6606; break;
    case 7200 : vnum = 7202; break;
    case 7801 : vnum = 7808; break;
    case 8000 : vnum = 8005; break;
    case 8308 : vnum = 8312; break;
    case 8904 : vnum = 8908; break;
    case 9203 : vnum = 9207; break;
    case 9401 : vnum = 9407; break;
    case 11006 : vnum = 11008; break;
    case 7705 : vnum = 7709; break;
    case 93000 : vnum = 93004; break;
    case 80001 : vnum = 80005; break;
    default:
      vnum = bound;
      bug("Bad Quest Vnum : %d", bound);
      break;
  }
  return vnum;
}

/*
 * Returns the vnum of a random object that actually pops on the mud.
 * It's a bit ugly, but it works :)
 */
int get_rand_item()
{
  OBJ_INDEX_DATA *pObjIndex;
  ROOM_INDEX_DATA *pRoom;
  RESET_DATA *pReset;
  AREA_DATA *area;
  int vnum, i;
  bool found = FALSE;

  while (!found)
  {
    vnum = number_range(101, 32000);
    if ((pObjIndex = get_obj_index(vnum)) != NULL)
    {
      if (!CAN_WEAR(pObjIndex, ITEM_TAKE)) continue;
      if (pObjIndex->item_type == ITEM_MONEY) continue;
      if ((area = pObjIndex->area) != NULL)
      {
        for (i = area->lvnum; i <= area->uvnum; i++)
        {
          if ((pRoom = get_room_index(i)) != NULL)
          {
            for (pReset = pRoom->reset_first; pReset; pReset = pReset->next)
            {
              switch (pReset->command)
              {
                default: break;
                case 'O':
                  if ((pObjIndex = get_obj_index(pReset->arg1)) != NULL)
                  {
                    if (pObjIndex->vnum == vnum) found = TRUE;
                  }
              }
            }
          }
        }
      }
    }
  }
  return vnum;
}

int load_special_item(CHAR_DATA *ch)
{
  OBJ_DATA *obj;
  OBJ_INDEX_DATA *pObjIndex;
  int vnum;

  vnum = number_range(99501, 99505);
  if ((pObjIndex = get_obj_index(vnum)) == NULL)
  {
    bug("Load_special_item : %d", vnum);
    return 103;  // Just to be on the safe side
  }
  obj = create_object(pObjIndex, 50);
  obj->ownerid = ch->pcdata->playerid;
  SET_BIT(obj->extra_flags, ITEM_NOLOCATE);
  obj->timer = 25; // so it will eventually decay...
  obj_to_room(obj, get_rand_room());
  return vnum;
}

int get_rand_mob()
{
  int vnum, count = 0;

  static const int questmob_table[] = {
   1494,
   1654,   1669,
   6101,
   1551,
/* THE END */
  -1
  };

  /* counting the size of the table */
  for (count = 0; questmob_table[count] != -1; count++) ;

  vnum = number_range(0, count-1);
  return questmob_table[vnum];
}

int get_rand_hard_mob()
{
  int vnum, count = 0;
  
  static const int questmob_table[] = {
   6199,
/* THE END */
  -1 
  };

  /* counting the size of the table */
  for (count = 0; questmob_table[count] != -1; count++) ;
  
  vnum = number_range(0, count-1);
  return questmob_table[vnum];
}

void do_showquest(CHAR_DATA *ch, char *argument)
{
  QUEST_DATA *quest;
  OBJ_INDEX_DATA *pObjIndex;
  MOB_INDEX_DATA *pMobIndex1;
  MOB_INDEX_DATA *pMobIndex2;
  MOB_INDEX_DATA *pMobIndex3;
  CHAR_DATA *gch;
  CHAR_DATA *ich;
  char buf[MAX_STRING_LENGTH];
  bool found = FALSE;
  bool found2 = FALSE;

  if (IS_NPC(ch)) return;
  for (quest = ch->pcdata->quests; quest; quest = quest->next)
  {
    found = TRUE;
    switch(quest->type)
    {
      default:
        sprintf(buf, "Do_showquests: %s has bad quest type %d.", ch->name, quest->type);
        bug(buf, 0);
        break;
      case QT_MOB:
        if ((pMobIndex1 = get_mob_index(quest->vnums[0])) == NULL && quest->vnums[0] != -1)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->vnums[0]);
          bug(buf, 0);
          break;
        }
        if ((pMobIndex2 = get_mob_index(quest->giver)) == NULL)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->giver);
          bug(buf, 0);
          break; 
        }
        sprintf(buf, "You have been charged by #G%s#n to complete the following quest\n\r",
          pMobIndex2->short_descr);
        send_to_char(buf, ch);
        if (quest->vnums[0] != -1)
        {
          sprintf(buf, " * Find and slay #R%s#n.\n\r", pMobIndex1->short_descr);
          send_to_char(buf, ch);
          sprintf(buf, " * You have #C%d#n hours to complete the quest.\n\r", quest->time);
          send_to_char(buf, ch);
        }
        else
        {
          send_to_char(" * This quest has been #Ccompleted#n.\n\r", ch);
          sprintf(buf, " * You have #C%d#n hours to return to the questgiver.\n\r", quest->time);
          send_to_char(buf, ch);
        }
        break;
      case QT_PK:
        if ((pMobIndex2 = get_mob_index(quest->giver)) == NULL)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->giver);
          bug(buf, 0);
          break;
        }
        for (gch = char_list; gch && !found2; gch = gch->next)
        {
          if (IS_NPC(gch)) continue;
          if (gch->pcdata->playerid == quest->vnums[0])
          {
            ich = gch;
            found2 = TRUE;
          }
        }
        sprintf(buf, "You have been charged by #G%s#n to complete the following quest\n\r",
          pMobIndex2->short_descr);
        send_to_char(buf, ch);
        if (quest->vnums[0] != -1)
        {
          sprintf(buf, " * Hunt and slay #R%s#n.\n\r",
            found2 ? ich->name : "someone");
          send_to_char(buf, ch);
          sprintf(buf, " * You have #C%d#n hours to complete the quest.\n\r", quest->time);
          send_to_char(buf, ch);
        }
        else
        {
          send_to_char(" * This quest has been #Ccompleted#n.\n\r", ch);
          sprintf(buf, " * You have #C%d#n hours to return to the questgiver.\n\r", quest->time);
          send_to_char(buf, ch);
        }
        break;
      case QT_OBJ:
        if ((pObjIndex = get_obj_index(quest->vnums[0])) == NULL)
        {
          sprintf(buf, "Do_showquests: %s has bad quest item %d.", ch->name, quest->vnums[0]);
          bug(buf, 0);
          break;
        }
        if ((pMobIndex2 = get_mob_index(quest->giver)) == NULL)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->giver);
          bug(buf, 0);
          break;
        }
        sprintf(buf, "You have been charged by #G%s#n to complete the following quest\n\r",
          pMobIndex2->short_descr);
        send_to_char(buf, ch);
        sprintf(buf, " * Find and return #R%s#n.\n\r", pObjIndex->short_descr);
        send_to_char(buf, ch);
        sprintf(buf, " * You have #C%d#n hours to complete the quest.\n\r", quest->time);
        send_to_char(buf, ch);
        break;
      case QT_MOB_AND_OBJ:
        if ((pMobIndex1 = get_mob_index(quest->vnums[0])) == NULL && quest->vnums[0] != -1)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->vnums[0]); 
          bug(buf, 0);
          break;
        }
        if ((pObjIndex = get_obj_index(quest->vnums[1])) == NULL)
        {
          sprintf(buf, "Do_showquests: %s has bad quest item %d.", ch->name, quest->vnums[1]);
          bug(buf, 0);
          break;
        }
        if ((pMobIndex2 = get_mob_index(quest->giver)) == NULL)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->giver);
          bug(buf, 0);
          break;
        }
        sprintf(buf, "You have been charged by #G%s#n to complete the following quest\n\r",
          pMobIndex2->short_descr);
        send_to_char(buf, ch);
        if (quest->vnums[0] != -1)
        {
          sprintf(buf, " * Find and slay #R%s#n.\n\r", pMobIndex1->short_descr);
          send_to_char(buf, ch);
        }
        sprintf(buf, " * Find and return #R%s#n.\n\r", pObjIndex->short_descr);
        send_to_char(buf, ch);
        sprintf(buf, " * You have #C%d#n hours to complete the quest.\n\r", quest->time);
        send_to_char(buf, ch);
        break;
      case QT_MASS_KILL:
        if ((pMobIndex1 = get_mob_index(quest->vnums[0])) == NULL && quest->vnums[0] != -1)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->vnums[0]);
          bug(buf, 0);
          break;
        }
        if ((pMobIndex3 = get_mob_index(quest->vnums[1])) == NULL)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->vnums[1]);
          bug(buf, 0);
          break;
        }
        if ((pMobIndex2 = get_mob_index(quest->giver)) == NULL)
        {
          sprintf(buf, "Do_showquests: %s has bad quest mob %d.", ch->name, quest->giver);
          bug(buf, 0);
          break;
        }
        sprintf(buf, "You have been charged by #G%s#n to complete the following quest\n\r",
          pMobIndex2->short_descr);
        send_to_char(buf, ch);
        if (quest->vnums[3] >= quest->vnums[2])
        {
          send_to_char(" * This quest has been #Ccompleted#n.\n\r", ch);
          sprintf(buf, " * You have #C%d#n hours to return to the questgiver.\n\r", quest->time);
          send_to_char(buf, ch);
        }
        else
        {
          sprintf(buf, " * Find and slay #C%d#n monsters ranging from\n\r", quest->vnums[2] - quest->vnums[3]);
          send_to_char(buf, ch);
          sprintf(buf, "   #R%s#n to #R%s#n.\n\r", pMobIndex1->short_descr, pMobIndex3->short_descr);
          send_to_char(buf, ch);
          sprintf(buf, " * You have #C%d#n hours to complete the quest.\n\r", quest->time);
          send_to_char(buf, ch);
        }
        break;
    }
  }
  if (!found) send_to_char("You are currently undertaking no quests.\n\r", ch);
  return;
}

void quest_to_char(CHAR_DATA *ch, QUEST_DATA *quest)
{
  QUEST_DATA *quest_new;

  if (IS_NPC(ch))
  {
    bug("Quest_to_char: on npc", 0);
    return;
  }

  if (quest_free == NULL)
    quest_new = alloc_perm(sizeof(*quest_new));
  else
  {
    quest_new  = quest_free;
    quest_free = quest_free->next;
  }

 *quest_new          = *quest;
  quest_new->next    = ch->pcdata->quests;
  ch->pcdata->quests = quest_new;

  return;
}

/*
 * Remove an affect from a char.
 */
void quest_from_char(CHAR_DATA *ch, QUEST_DATA *quest)
{
  if (IS_NPC(ch))
  {
    bug("Quest_from_char: on npc", 0);
    return;
  }
  if (ch->pcdata->quests == NULL)
  {
    bug("Quest_from_char: No quest", 0);
    return;
  }
  if (quest == ch->pcdata->quests)
    ch->pcdata->quests = quest->next;
  else
  {
    QUEST_DATA *prev;
   
    for (prev = ch->pcdata->quests; prev; prev = prev->next)
    {
      if (prev->next == quest)
      {
        prev->next = quest->next;
        break;
      }
    }
    if (prev == NULL)
    {
      bug("Quest_from_char: No quest", 0);
      return;
    }
  }
  quest->next = quest_free;
  quest_free  = quest;
  return;
}

QUEST_FUN *quest_lookup(const char *name)
{
  int cmd;

  for (cmd = 0; *quest_table[cmd].quest_name; cmd++)
    if (!str_cmp(name, quest_table[cmd].quest_name))
      return quest_table[cmd].quest_fun;
  return 0;
}

char *quest_string(QUEST_FUN *fun)
{
  int cmd;

  for (cmd = 0; *quest_table[cmd].quest_fun; cmd++)
    if (fun == quest_table[cmd].quest_fun)
      return quest_table[cmd].quest_name;

  return 0;
}

void give_token(CHAR_DATA *questmaster, CHAR_DATA *ch, int value)
{
  OBJ_DATA *obj;
  char buf[MAX_STRING_LENGTH];

  value *= ccenter[CCENTER_QPS_LEVEL];
  value /= 100;
  if (IS_SET(ch->pcdata->tempflag, TEMP_EDGE)) value *= 1.1;
  if (ch->pcdata->time_tick > 49)
  {
    value *= 100 + ch->pcdata->time_tick/10;
    value /= 100;
  }

  obj = create_object(get_obj_index(OBJ_VNUM_PROTOPLASM), 50);
  obj->value[0] = value;
  obj->level = value; 
  obj->cost = 1000;
  obj->item_type = ITEM_QUEST;
  obj_to_char(obj, ch);
  free_string(obj->name);
  obj->name = str_dup("quest token");
  free_string(obj->short_descr);
  sprintf(buf, "a %d point quest token", value);
  obj->short_descr = str_dup(buf);
  free_string(obj->description);
  sprintf(buf, "A %d point quest token lies on the floor.", value);
  obj->description = str_dup(buf);
  act("You recieve $p from $N.", ch, obj, questmaster, TO_CHAR);
  act("$n recieves $p from $N.", ch, obj, questmaster, TO_ROOM);
  ch->pcdata->questsrun++;
  ch->pcdata->questtotal += value;
  do_autosave(ch, "");
}

void do_qcomplete(CHAR_DATA *ch, char *argument)  
{
  CHAR_DATA *questmaster;
  QUEST_DATA *quest;
  OBJ_DATA *obj;
  char arg[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  bool found = FALSE;

  if (IS_NPC(ch)) return;
  argument = one_argument(argument, arg);
  one_argument(argument, arg2);
  if ((questmaster = get_char_room(ch, arg)) == NULL)
  {
    send_to_char("You cannot seem to find that questmaster.\n\r", ch);
    return;
  }
  if (!IS_NPC(questmaster))   
  {
    send_to_char("Players cannot give quests.\n\r", ch);
    return;
  }
  if (questmaster->quest_fun != 0)
  {
    for (quest = ch->pcdata->quests; quest && !found; quest = quest->next)
    {
      if (quest->giver != questmaster->pIndexData->vnum) continue;
      found = TRUE;

      /* Let's check if the quest is actually completed */
      switch (quest->type)
      {
        default:
          bug("Quest_complete: Bad Quest Type", 0);
          return;
        case QT_MOB:
          if (quest->vnums[0] != -1)
          {
            send_to_char("You have not completed that quest yet.\n\r", ch);
            return;
          }
          break;
        case QT_PK:
          if (quest->vnums[0] != -1)
          {
            send_to_char("You have not completed that quest yet.\n\r", ch);
            return;
          }
          break;
        case QT_OBJ:
          if ((obj = get_obj_carry(ch, arg2)) == NULL)
          {
            send_to_char("What object do you wish to return?\n\r", ch);
            return;
          }
          if (obj->pIndexData->vnum != quest->vnums[0])
          {
            send_to_char("That is not the object of the quest.\n\r", ch);
            return;
          }
          extract_obj(obj);
          break;
        case QT_MOB_AND_OBJ:
          if (quest->vnums[0] != -1)
          {
            send_to_char("You have not completed that quest yet.\n\r", ch);
            return;
          }
          if ((obj = get_obj_carry(ch, arg2)) == NULL)
          {
            send_to_char("What object do you wish to return?\n\r", ch);
            return;   
          }
          if (obj->pIndexData->vnum != quest->vnums[1])
          {
            send_to_char("That is not the object of the quest.\n\r", ch);
            return;
          }
          extract_obj(obj);
          break;
        case QT_MASS_KILL:
          if (quest->vnums[2] > quest->vnums[3])
          {
            send_to_char("You have not completed that quest yet.\n\r", ch);
            return;
          }
          break;
      }
      (*questmaster->quest_fun)(questmaster, ch, "complete");
      quest_from_char(ch, quest);
    }
    if (!found) send_to_char("You are not questing for this questmaster.\n\r", ch);
  }
  else
    send_to_char("Doesn't seem like that's a questmaster.\n\r", ch);
  return;
}

void do_qgain(CHAR_DATA *ch, char *argument)
{
  CHAR_DATA *questmaster;
  QUEST_DATA *quest;
  char arg[MAX_INPUT_LENGTH];

  if (IS_NPC(ch)) return;
  one_argument(argument, arg);
  if ((questmaster = get_char_room(ch, arg)) == NULL)
  {
    send_to_char("You cannot seem to find that questmaster.\n\r", ch);
    return;
  }
  if (!IS_NPC(questmaster))
  {
    send_to_char("Players cannot give quests.\n\r", ch);
    return;
  }
  if (questmaster->quest_fun != 0)
  {
    for (quest = ch->pcdata->quests; quest; quest = quest->next)
    {
      if (quest->giver == questmaster->pIndexData->vnum)
      {
        send_to_char("You have already been given a quest from this questmaster.\n\r", ch);
        return;
      }
    }
    /* ROCK THEM! */
    (*questmaster->quest_fun)(questmaster, ch, "gain");  
  }
  else
    send_to_char("Doesn't seem like that's a questmaster.\n\r", ch);
}

void questspec_generic(CHAR_DATA *questmaster, CHAR_DATA *ch, char *argument)
{
  if (!str_cmp(argument, "gain"))
  {
    QUEST_DATA new_quest;

    do_say(questmaster, "Will you smurf this smurf for me?");
    new_quest.type      = QT_MOB;
    new_quest.time      = 20;
    new_quest.giver     = questmaster->pIndexData->vnum;
    new_quest.vnums[0]  = number_range(103, 109);
    quest_to_char(ch, &new_quest);
    return;
  }
  else if (!str_cmp(argument, "complete"))
  {
    int value = 20;

    give_token(questmaster, ch, value);
    do_say(questmaster, "That's the spirit, thanks for solving my quest.");
    return;
  }
}

void questspec_newbie(CHAR_DATA *questmaster, CHAR_DATA *ch, char *argument)
{
  if (!str_cmp(argument, "gain"))
  {
    QUEST_DATA new_quest;

    do_say(questmaster, "There you go, type 'showquest' to see the quest.");
    new_quest.type      = QT_MOB;
    new_quest.time      = 20;
    new_quest.giver     = questmaster->pIndexData->vnum;
    new_quest.vnums[0]  = number_range(957, 960);
    quest_to_char(ch, &new_quest);
    return;
  }
  else if (!str_cmp(argument, "complete"))
  {
    int value = 5;

    give_token(questmaster, ch, value);
    do_say(questmaster, "You can spend the questpoints at the shop on floor 3.");
    return;
  }
}

void questspec_rand_mob(CHAR_DATA *questmaster, CHAR_DATA *ch, char *argument)
{
  if (!str_cmp(argument, "gain"))
  {
    QUEST_DATA new_quest;

    do_say(questmaster, "I charge you to find and kill this monster!");
    new_quest.type      = QT_MOB;
    new_quest.time      = 15;
    new_quest.giver     = questmaster->pIndexData->vnum;
    new_quest.vnums[0]  = get_rand_mob();
    quest_to_char(ch, &new_quest);
    return;
  } 
  else if (!str_cmp(argument, "complete"))
  {
    int value = 30;

    give_token(questmaster, ch, value);
    do_say(questmaster, "Thanks for solving my quest, come back again if you want.");
    return;
  }
}

void questspec_hard_mob(CHAR_DATA *questmaster, CHAR_DATA *ch, char *argument)
{
  if (!str_cmp(argument, "gain"))
  {
    QUEST_DATA new_quest;

    do_say(questmaster, "I charge you to find and kill this horrible monster!");
    new_quest.type      = QT_MOB;
    new_quest.time      = 25;
    new_quest.giver     = questmaster->pIndexData->vnum;
    new_quest.vnums[0]  = get_rand_hard_mob();        
    quest_to_char(ch, &new_quest);
    return;
  }
  else if (!str_cmp(argument, "complete"))
  {
    int value = 60;

    give_token(questmaster, ch, value);
    do_say(questmaster, "Thanks for solving my quest, come back again if you want.");
    return;
  } 
}  

void questspec_special_item(CHAR_DATA *questmaster, CHAR_DATA *ch, char *argument)
{
  if (!str_cmp(argument, "gain"))
  {
    QUEST_DATA new_quest;

    do_say(questmaster, "I charge you to retrive this ancient artifact of mine!");
    new_quest.type      = QT_OBJ;   
    new_quest.time      = 25;
    new_quest.giver     = questmaster->pIndexData->vnum;
    new_quest.vnums[0]  = load_special_item(ch);
    quest_to_char(ch, &new_quest);
    return;
  }
  else if (!str_cmp(argument, "complete"))
  {
    int value = 120;

    give_token(questmaster, ch, value);
    do_say(questmaster, "Thanks for solving my quest, come back again if you want.");
    return;
  }
}

void questspec_mob_and_item(CHAR_DATA *questmaster, CHAR_DATA *ch, char *argument)
{
  if (!str_cmp(argument, "gain"))
  {
    QUEST_DATA new_quest;   

    do_say(questmaster, "Please slay this monster and retrive this item for me!");
    new_quest.type      = QT_MOB_AND_OBJ;
    new_quest.time      = 25;
    new_quest.giver     = questmaster->pIndexData->vnum;
    new_quest.vnums[0]  = get_rand_mob();
    new_quest.vnums[1]  = get_rand_item();
    quest_to_char(ch, &new_quest);
    return;
  }
  else if (!str_cmp(argument, "complete"))
  {
    int value = 90;

    give_token(questmaster, ch, value);
    do_say(questmaster, "Thanks for solving my quest, come back again if you want.");
    return;
  }
}

void questspec_mass_kill(CHAR_DATA *questmaster, CHAR_DATA *ch, char *argument)
{
  if (!str_cmp(argument, "gain"))
  {
    QUEST_DATA new_quest;
    int bound;

    bound = get_lbound();

    do_say(questmaster, "Please slay these monsters.");
    new_quest.type      = QT_MASS_KILL;
    new_quest.time      = 30;
    new_quest.giver     = questmaster->pIndexData->vnum;
    new_quest.vnums[0]  = bound;
    new_quest.vnums[1]  = get_ubound(bound);
    new_quest.vnums[2]  = number_range(8, 16);   // amount needed to be killed
    new_quest.vnums[3]  = 0;                     // counter
    quest_to_char(ch, &new_quest);
    return;
  }
  else if (!str_cmp(argument, "complete"))
  {
    int value = 75;

    give_token(questmaster, ch, value);
    do_say(questmaster, "Thanks for solving my quest, come back again if you want.");
    return;
  }
}  

void questspec_pk(CHAR_DATA *questmaster, CHAR_DATA *ch, char *argument)
{
  if (!str_cmp(argument, "gain"))
  {
    QUEST_DATA new_quest;
    DESCRIPTOR_DATA *d;
    CHAR_DATA *gch;
    bool found = FALSE;

    for (d = descriptor_list; d && !found; d = d->next)
    {
      if (d->connected == CON_PLAYING && d->lookup_status == STATUS_DONE)
      {
        if ((gch = d->character) == NULL) continue;
        if (fair_fight(ch, gch) && fair_fight(gch, ch)) found = TRUE;
      }
    }
    if (!found)
    {
      do_say(questmaster, "Sorry, I have no quest for you at this time.");
      return;
    }

    do_say(questmaster, "I charge you to find and destroy this player!");
    new_quest.type      = QT_PK;
    new_quest.time      = 8;
    new_quest.giver     = questmaster->pIndexData->vnum;
    new_quest.vnums[0]  = gch->pcdata->playerid;
    quest_to_char(ch, &new_quest);
    return;
  }
  else if (!str_cmp(argument, "complete"))
  {
    int value = 200;

    give_token(questmaster, ch, value);
    do_say(questmaster, "Thanks for solving my quest, come back again if you want.");
    return;
  }
}
