/* ************************************************************************
*  file: spec_procs.c , Special module.                   Part of DIKUMUD *
*  Usage: Procedures handling special procedures for object/room/mobile   *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
************************************************************************* */

#include "os.h"

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "spells.h"
#include "limits.h"
#include "prototypes.h"

/*   external vars  */

extern struct room_data *world;
extern struct char_data *character_list;
extern struct descriptor_data *descriptor_list;
extern struct index_data *obj_index;
extern struct time_info_data time_info;


/* extern procedures */

void hit (struct char_data *ch, struct char_data *victim, int type);
void gain_exp (struct char_data *ch, int gain);
char *str_dup (char *source);

void cast_burning_hands (int level, struct char_data *ch, char *arg,
  int type, struct char_data *victim, struct obj_data *tar_obj);
void cast_chill_touch (int level, struct char_data *ch, char *arg, int type,
  struct char_data *victim, struct obj_data *tar_obj);
void cast_colour_spray (int level, struct char_data *ch, char *arg, int type,
  struct char_data *victim, struct obj_data *tar_obj);
void cast_energy_drain (int level, struct char_data *ch, char *arg, int type,
  struct char_data *victim, struct obj_data *tar_obj);
void cast_fireball (int level, struct char_data *ch, char *arg, int type,
  struct char_data *victim, struct obj_data *tar_obj);
void cast_magic_missile (int level, struct char_data *ch, char *arg,
  int type, struct char_data *victim, struct obj_data *tar_obj);
void cast_blindness (int level, struct char_data *ch, char *arg, int type,
  struct char_data *tar_ch, struct obj_data *tar_obj);
void cast_curse (int level, struct char_data *ch, char *arg, int type,
  struct char_data *tar_ch, struct obj_data *tar_obj);
void cast_sleep (int level, struct char_data *ch, char *arg, int type,
  struct char_data *tar_ch, struct obj_data *tar_obj);
/* Dragon breath .. */
void cast_fire_breath (int level, struct char_data *ch, char *arg, int type,
  struct char_data *tar_ch, struct obj_data *tar_obj);
void cast_frost_breath (int level, struct char_data *ch, char *arg, int type,
  struct char_data *tar_ch, struct obj_data *tar_obj);
void cast_acid_breath (int level, struct char_data *ch, char *arg, int type,
  struct char_data *tar_ch, struct obj_data *tar_obj);
void cast_gas_breath (int level, struct char_data *ch, char *arg, int type,
  struct char_data *tar_ch, struct obj_data *tar_obj);
void cast_lightning_breath (int level, struct char_data *ch, char *arg,
  int type, struct char_data *tar_ch, struct obj_data *tar_obj);


/* Data declarations */

struct social_type {
  char *cmd;
  int next_line;
};


/* ********************************************************************
*  Special procedures for rooms                                       *
******************************************************************** */

char *how_good (int percent)
{
  static char buf[256];

  if (percent == 0)
    strcpy (buf, " (not learned)");
  else if (percent <= 10)
    strcpy (buf, " (awful)");
  else if (percent <= 20)
    strcpy (buf, " (bad)");
  else if (percent <= 40)
    strcpy (buf, " (poor)");
  else if (percent <= 55)
    strcpy (buf, " (average)");
  else if (percent <= 70)
    strcpy (buf, " (fair)");
  else if (percent <= 80)
    strcpy (buf, " (good)");
  else if (percent <= 85)
    strcpy (buf, " (very good)");
  else
    strcpy (buf, " (Superb)");

  return (buf);
}

int guild (struct char_data *ch, int cmd, char *arg)
{

  char arg1[MAX_STRING_LENGTH];
  char buf[MAX_STRING_LENGTH];
  int number, i, percent;

  extern char *spells[];
  extern struct spell_info_type spell_info[MAX_SPL_LIST];
  extern struct int_app_type int_app[26];

  static char *w_skills[] = {
    "kick",                     /* No. 50 */
    "bash",
    "rescue",
    "\n"
  };

  static char *t_skills[] = {
    "sneak",                    /* No. 45 */
    "hide",
    "steal",
    "backstab",
    "pick",
    "\n"
  };

  if ((cmd != 164) && (cmd != 170))
    return (FALSE);

  for (; *arg == ' '; arg++);

  switch (GET_CLASS (ch)) {
  case CLASS_MAGIC_USER:{
      if (!*arg) {
        sprintf (buf, "You have got %d practice sessions left.\n\r",
          ch->specials.spells_to_learn);
        send_to_char (buf, ch);
        send_to_char ("You can practise any of these spells:\n\r", ch);
        for (i = 0; *spells[i] != '\n'; i++)
          if (spell_info[i + 1].spell_pointer &&
            (spell_info[i + 1].min_level_magic <= GET_LEVEL (ch))) {
            send_to_char (spells[i], ch);
            send_to_char (how_good (ch->skills[i + 1].learned), ch);
            send_to_char ("\n\r", ch);
          }
        return (TRUE);
      }

      number = old_search_block (arg, 0, strlen (arg), spells, FALSE);
      if (number == -1) {
        send_to_char ("You do not know of this spell...\n\r", ch);
        return (TRUE);
      }
      if (GET_LEVEL (ch) < spell_info[number].min_level_magic) {
        send_to_char ("You do not know of this spell...\n\r", ch);
        return (TRUE);
      }
      if (ch->specials.spells_to_learn <= 0) {
        send_to_char ("You do not seem to be able to practice now.\n\r", ch);
        return (TRUE);
      }
      if (ch->skills[number].learned >= 95) {
        send_to_char ("You are already learned in this area.\n\r", ch);
        return (TRUE);
      }

      send_to_char ("You Practice for a while...\n\r", ch);
      ch->specials.spells_to_learn--;

      percent =
        ch->skills[number].learned + MAX (25, int_app[GET_INT (ch)].learn);
      ch->skills[number].learned = MIN (95, percent);

      if (ch->skills[number].learned >= 95) {
        send_to_char ("You are now learned in this area.\n\r", ch);
        return (TRUE);
      }

    }
    break;

  case CLASS_THIEF:{
      if (!*arg) {
        sprintf (buf, "You have got %d practice sessions left.\n\r",
          ch->specials.spells_to_learn);
        send_to_char (buf, ch);
        send_to_char ("You can practise any of these skills:\n\r", ch);
        for (i = 0; *t_skills[i] != '\n'; i++) {
          send_to_char (t_skills[i], ch);
          send_to_char (how_good (ch->skills[i + 45].learned), ch);
          send_to_char ("\n\r", ch);
        }
        return (TRUE);
      }
      number = search_block (arg, t_skills, FALSE);
      if (number == -1) {
        send_to_char ("You do not know of this spell...\n\r", ch);
        return (TRUE);
      }
      if (ch->specials.spells_to_learn <= 0) {
        send_to_char ("You do not seem to be able to practice now.\n\r", ch);
        return (TRUE);
      }
      if (ch->skills[number + SKILL_SNEAK].learned >= 85) {
        send_to_char ("You are already learned in this area.\n\r", ch);
        return (TRUE);
      }
      send_to_char ("You Practice for a while...\n\r", ch);
      ch->specials.spells_to_learn--;

      percent = ch->skills[number + SKILL_SNEAK].learned +
        MIN (int_app[GET_INT (ch)].learn, 12);
      ch->skills[number + SKILL_SNEAK].learned = MIN (85, percent);

      if (ch->skills[number + SKILL_SNEAK].learned >= 85) {
        send_to_char ("You are now learned in this area.\n\r", ch);
        return (TRUE);
      }

    }
    break;

  case CLASS_CLERIC:{
      if (!*arg) {
        sprintf (buf, "You have got %d practice sessions left.\n\r",
          ch->specials.spells_to_learn);
        send_to_char (buf, ch);
        send_to_char ("You can practise any of these spells:\n\r", ch);
        for (i = 0; *spells[i] != '\n'; i++)
          if (spell_info[i + 1].spell_pointer &&
            (spell_info[i + 1].min_level_cleric <= GET_LEVEL (ch))) {
            send_to_char (spells[i], ch);
            send_to_char (how_good (ch->skills[i + 1].learned), ch);
            send_to_char ("\n\r", ch);
          }
        return (TRUE);
      }
      number = old_search_block (arg, 0, strlen (arg), spells, FALSE);
      if (number == -1) {
        send_to_char ("You do not know of this spell...\n\r", ch);
        return (TRUE);
      }
      if (GET_LEVEL (ch) < spell_info[number].min_level_cleric) {
        send_to_char ("You do not know of this spell...\n\r", ch);
        return (TRUE);
      }
      if (ch->specials.spells_to_learn <= 0) {
        send_to_char ("You do not seem to be able to practice now.\n\r", ch);
        return (TRUE);
      }
      if (ch->skills[number].learned >= 95) {
        send_to_char ("You are already learned in this area.\n\r", ch);
        return (TRUE);
      }
      send_to_char ("You Practice for a while...\n\r", ch);
      ch->specials.spells_to_learn--;

      percent =
        ch->skills[number].learned + MAX (25, int_app[GET_INT (ch)].learn);
      ch->skills[number].learned = MIN (95, percent);

      if (ch->skills[number].learned >= 95) {
        send_to_char ("You are now learned in this area.\n\r", ch);
        return (TRUE);
      }
    }
    break;

  case CLASS_WARRIOR:{
      if (!*arg) {
        sprintf (buf, "You have got %d practice sessions left.\n\r",
          ch->specials.spells_to_learn);
        send_to_char (buf, ch);
        send_to_char ("You can practise any of these skills:\n\r", ch);
        for (i = 0; *w_skills[i] != '\n'; i++) {
          send_to_char (w_skills[i], ch);
          send_to_char (how_good (ch->skills[i + SKILL_KICK].learned), ch);
          send_to_char ("\n\r", ch);
        }
        return (TRUE);
      }
      number = search_block (arg, w_skills, FALSE);
      if (number == -1) {
        send_to_char ("You do not have ability to practise this skill!\n\r",
          ch);
        return (TRUE);
      }
      if (ch->specials.spells_to_learn <= 0) {
        send_to_char ("You do not seem to be able to practice now.\n\r", ch);
        return (TRUE);
      }
      if (ch->skills[number + SKILL_KICK].learned >= 80) {
        send_to_char ("You are already learned in this area.\n\r", ch);
        return (TRUE);
      }
      send_to_char ("You Practice for a while...\n\r", ch);
      ch->specials.spells_to_learn--;

      percent = ch->skills[number + SKILL_KICK].learned +
        MIN (12, int_app[GET_INT (ch)].learn);
      ch->skills[number + SKILL_KICK].learned = MIN (80, percent);

      if (ch->skills[number + SKILL_KICK].learned >= 80) {
        send_to_char ("You are now learned in this area.\n\r", ch);
        return (TRUE);
      }
    }
    break;
  }
  return FALSE;
}



int dump (struct char_data *ch, int cmd, char *arg)
{
  struct obj_data *k;
  char buf[100];
  struct char_data *tmp_char;
  int value = 0;

  void do_drop (struct char_data *ch, char *argument, int cmd);
  char *fname (char *namelist);

  for (k = world[ch->in_room].contents; k; k = world[ch->in_room].contents) {
    sprintf (buf, "The %s vanish in a puff of smoke.\n\r", fname (k->name));
    for (tmp_char = world[ch->in_room].people; tmp_char;
      tmp_char = tmp_char->next_in_room)
      if (CAN_SEE_OBJ (tmp_char, k))
        send_to_char (buf, tmp_char);
    extract_obj (k);
  }

  if (cmd != 60)
    return (FALSE);

  do_drop (ch, arg, cmd);

  value = 0;

  for (k = world[ch->in_room].contents; k; k = world[ch->in_room].contents) {
    sprintf (buf, "The %s vanish in a puff of smoke.\n\r", fname (k->name));
    for (tmp_char = world[ch->in_room].people; tmp_char;
      tmp_char = tmp_char->next_in_room)
      if (CAN_SEE_OBJ (tmp_char, k))
        send_to_char (buf, tmp_char);
    value += MAX (1, MIN (50, k->obj_flags.cost / 10));

    extract_obj (k);
  }

  if (value) {
    act ("You are awarded for outstanding performance.", FALSE, ch, 0, 0,
      TO_CHAR);
    act ("$n has been awarded for being a good citizen.", TRUE, ch, 0, 0,
      TO_ROOM);

    if (GET_LEVEL (ch) < 3)
      gain_exp (ch, value);
    else
      GET_GOLD (ch) += value;
  }
  return TRUE;
}

int mayor (struct char_data *ch, int cmd, char *arg)
{
  static char open_path[] =
    "W3a3003b33000c111d0d111Oe333333Oe22c222112212111a1S.";

  static char close_path[] =
    "W3a3003b33000c111d0d111CE333333CE22c222112212111a1S.";

/*
  const struct social_type open_path[] = {
   {"G",0}
  };

  static void *thingy = 0;
  static int cur_line = 0;

  for (i=0; i < 1; i++)
  {
    if (*(open_path[cur_line].cmd) == '!') {
      i++;
      exec_social(ch, (open_path[cur_line].cmd)+1,
        open_path[cur_line].next_line, &cur_line, &thingy);
  } else {
      exec_social(ch, open_path[cur_line].cmd,
        open_path[cur_line].next_line, &cur_line, &thingy);
  }
*/
  static char *path;
  static int index;
  static bool move = FALSE;

  void do_move (struct char_data *ch, char *argument, int cmd);
  void do_open (struct char_data *ch, char *argument, int cmd);
  void do_lock (struct char_data *ch, char *argument, int cmd);
  void do_unlock (struct char_data *ch, char *argument, int cmd);
  void do_close (struct char_data *ch, char *argument, int cmd);


  if (!move) {
    if (time_info.hours == 6) {
      move = TRUE;
      path = open_path;
      index = 0;
    } else if (time_info.hours == 20) {
      move = TRUE;
      path = close_path;
      index = 0;
    }
  }

  if (cmd || !move || (GET_POS (ch) < POSITION_SLEEPING) ||
    (GET_POS (ch) == POSITION_FIGHTING))
    return FALSE;

  switch (path[index]) {
  case '0':
  case '1':
  case '2':
  case '3':
    do_move (ch, "", path[index] - '0' + 1);
    break;

  case 'W':
    GET_POS (ch) = POSITION_STANDING;
    act ("$n awakens and groans loudly.", FALSE, ch, 0, 0, TO_ROOM);
    break;

  case 'S':
    GET_POS (ch) = POSITION_SLEEPING;
    act ("$n lies down and instantly falls asleep.", FALSE, ch, 0, 0,
      TO_ROOM);
    break;

  case 'a':
    act ("$n says 'Hello Honey!'", FALSE, ch, 0, 0, TO_ROOM);
    act ("$n smirks.", FALSE, ch, 0, 0, TO_ROOM);
    break;

  case 'b':
    act ("$n says 'What a view! I must get something done about that dump!'",
      FALSE, ch, 0, 0, TO_ROOM);
    break;

  case 'c':
    act
      ("$n says 'Vandals! Youngsters nowadays have no respect for anything!'",
      FALSE, ch, 0, 0, TO_ROOM);
    break;

  case 'd':
    act ("$n says 'Good day, citizens!'", FALSE, ch, 0, 0, TO_ROOM);
    break;

  case 'e':
    act ("$n says 'I hereby declare the bazaar open!'", FALSE, ch, 0, 0,
      TO_ROOM);
    break;

  case 'E':
    act ("$n says 'I hereby declare Midgaard closed!'", FALSE, ch, 0, 0,
      TO_ROOM);
    break;

  case 'O':
    do_unlock (ch, "gate", 0);
    do_open (ch, "gate", 0);
    break;

  case 'C':
    do_close (ch, "gate", 0);
    do_lock (ch, "gate", 0);
    break;

  case '.':
    move = FALSE;
    break;

  }

  index++;
  return FALSE;
}

/* ********************************************************************
*  General special procedures for mobiles                                      *
******************************************************************** */

/* SOCIAL GENERAL PROCEDURES

If first letter of the command is '!' this will mean that the following
command will be executed immediately.

"G",n      : Sets next line to n
"g",n      : Sets next line relative to n, fx. line+=n
"m<dir>",n : move to <dir>, <dir> is 0,1,2,3,4 or 5
"w",n      : Wake up and set standing (if possible)
"c<txt>",n : Look for a person named <txt> in the room
"o<txt>",n : Look for an object named <txt> in the room
"r<int>",n : Test if the npc in room number <int>?
"s",n      : Go to sleep, return false if can't go sleep
"e<txt>",n : echo <txt> to the room, can use $o/$p/$N depending on
             contents of the **thing
"E<txt>",n : Send <txt> to person pointed to by thing
"B<txt>",n : Send <txt> to room, except to thing
"?<num>",n : <num> in [1..99]. A random chance of <num>% success rate.
             Will as usual advance one line upon sucess, and change
             relative n lines upon failure.
"O<txt>",n : Open <txt> if in sight.
"C<txt>",n : Close <txt> if in sight.
"L<txt>",n : Lock <txt> if in sight.
"U<txt>",n : Unlock <txt> if in sight.    */

/* Execute a social command.                                        */
void exec_social (struct char_data *npc, char *cmd, int next_line,
  int *cur_line, void **thing)
{
  bool ok;

  void do_move (struct char_data *ch, char *argument, int cmd);
  void do_open (struct char_data *ch, char *argument, int cmd);
  void do_lock (struct char_data *ch, char *argument, int cmd);
  void do_unlock (struct char_data *ch, char *argument, int cmd);
  void do_close (struct char_data *ch, char *argument, int cmd);

  if (GET_POS (npc) == POSITION_FIGHTING)
    return;

  ok = TRUE;

  switch (*cmd) {

  case 'G':
    *cur_line = next_line;
    return;

  case 'g':
    *cur_line += next_line;
    return;

  case 'e':
    act (cmd + 1, FALSE, npc, *thing, *thing, TO_ROOM);
    break;

  case 'E':
    act (cmd + 1, FALSE, npc, 0, *thing, TO_VICT);
    break;

  case 'B':
    act (cmd + 1, FALSE, npc, 0, *thing, TO_NOTVICT);
    break;

  case 'm':
    do_move (npc, "", *(cmd + 1) - '0' + 1);
    break;

  case 'w':
    if (GET_POS (npc) != POSITION_SLEEPING)
      ok = FALSE;
    else
      GET_POS (npc) = POSITION_STANDING;
    break;

  case 's':
    if (GET_POS (npc) <= POSITION_SLEEPING)
      ok = FALSE;
    else
      GET_POS (npc) = POSITION_SLEEPING;
    break;

  case 'c':                    /* Find char in room */
    *thing = get_char_room_vis (npc, cmd + 1);
    ok = (*thing != 0);
    break;

  case 'o':                    /* Find object in room */
    *thing = get_obj_in_list_vis (npc, cmd + 1, world[npc->in_room].contents);
    ok = (*thing != 0);
    break;

  case 'r':                    /* Test if in a certain room */
    ok = (npc->in_room == atoi (cmd + 1));
    break;

  case 'O':                    /* Open something */
    do_open (npc, cmd + 1, 0);
    break;

  case 'C':                    /* Close something */
    do_close (npc, cmd + 1, 0);
    break;

  case 'L':                    /* Lock something  */
    do_lock (npc, cmd + 1, 0);
    break;

  case 'U':                    /* UnLock something  */
    do_unlock (npc, cmd + 1, 0);
    break;

  case '?':                    /* Test a random number */
    if (atoi (cmd + 1) <= number (1, 100))
      ok = FALSE;
    break;

  default:
    break;
  }                             /* End Switch */

  if (ok)
    (*cur_line)++;
  else
    (*cur_line) += next_line;
}



void npc_steal (struct char_data *ch, struct char_data *victim)
{
  int gold;

  if (IS_NPC (victim))
    return;
  if (GET_LEVEL (victim) > 20)
    return;

  if (AWAKE (victim) && (number (0, GET_LEVEL (ch)) == 0)) {
    act ("You discover that $n has $s hands in your wallet.", FALSE, ch, 0,
      victim, TO_VICT);
    act ("$n tries to steal gold from $N.", TRUE, ch, 0, victim, TO_NOTVICT);
  } else {
    /* Steal some gold coins */
    gold = (int) ((GET_GOLD (victim) * number (1, 10)) / 100);
    if (gold > 0) {
      GET_GOLD (ch) += gold;
      GET_GOLD (victim) -= gold;
    }
  }
}


int snake (struct char_data *ch, int cmd, char *arg)
{
  void cast_poison (int level, struct char_data *ch, char *arg, int type,
    struct char_data *tar_ch, struct obj_data *tar_obj);

  if (cmd)
    return FALSE;

  if (GET_POS (ch) != POSITION_FIGHTING)
    return FALSE;

  if (ch->specials.fighting &&
    (ch->specials.fighting->in_room == ch->in_room) &&
    (number (0, 32 - GET_LEVEL (ch)) == 0)) {
    act ("$n bites $N!", 1, ch, 0, ch->specials.fighting, TO_NOTVICT);
    act ("$n bites you!", 1, ch, 0, ch->specials.fighting, TO_VICT);
    cast_poison (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL,
      ch->specials.fighting, 0);
    return TRUE;
  }
  return FALSE;
}

int thief (struct char_data *ch, int cmd, char *arg)
{
  struct char_data *cons;

  if (cmd)
    return FALSE;

  if (GET_POS (ch) != POSITION_STANDING)
    return FALSE;

  for (cons = world[ch->in_room].people; cons; cons = cons->next_in_room)
    if ((!IS_NPC (cons)) && (GET_LEVEL (cons) < 21) && (number (1, 5) == 1))
      npc_steal (ch, cons);

  return TRUE;
}

int magic_user (struct char_data *ch, int cmd, char *arg)
{
  struct char_data *vict;

  if (cmd)
    return FALSE;

  if (GET_POS (ch) != POSITION_FIGHTING)
    return FALSE;

  if (!ch->specials.fighting)
    return FALSE;


  /* Find a dude to do evil things upon ! */

  for (vict = world[ch->in_room].people; vict; vict = vict->next_in_room)
    if (vict->specials.fighting == ch && number (0, 2) == 0)
      break;

  if (!vict)
    return FALSE;


  if ((vict != ch->specials.fighting) && (GET_LEVEL (ch) > 13)
    && (number (0, 7) == 0)) {
    act ("$n utters the words 'dilan oso'.", 1, ch, 0, 0, TO_ROOM);
    cast_sleep (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    return TRUE;
  }

  if ((GET_LEVEL (ch) > 12) && (number (0, 6) == 0)) {
    act ("$n utters the words 'gharia miwi'.", 1, ch, 0, 0, TO_ROOM);
    cast_curse (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    return TRUE;
  }

  if ((GET_LEVEL (ch) > 7) && (number (0, 5) == 0)) {
    act ("$n utters the words 'koholian dia'.", 1, ch, 0, 0, TO_ROOM);
    cast_blindness (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    return TRUE;
  }

  if ((GET_LEVEL (ch) > 12) && (number (0, 8) == 0) && IS_EVIL (ch)) {
    act ("$n utters the words 'ib er dranker'.", 1, ch, 0, 0, TO_ROOM);
    cast_energy_drain (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    return TRUE;
  }

  switch (GET_LEVEL (ch)) {
  case 1:
  case 2:
  case 3:
  case 4:
    act ("$n utters the words 'hahili duvini'.", 1, ch, 0, 0, TO_ROOM);
    cast_magic_missile (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    break;
  case 5:
  case 6:
  case 7:
  case 8:
    act ("$n utters the words 'grynt oef'.", 1, ch, 0, 0, TO_ROOM);
    cast_burning_hands (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    break;
  case 9:
  case 10:
    act ("$n utters the words 'sjulk divi'.", 1, ch, 0, 0, TO_ROOM);
    cast_lightning_bolt (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    break;
  case 11:
  case 12:
  case 13:
  case 14:
    act ("$n utters the words 'nasson hof'.", 1, ch, 0, 0, TO_ROOM);
    cast_colour_spray (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    break;
  default:
    act ("$n utters the words 'tuborg'.", 1, ch, 0, 0, TO_ROOM);
    cast_fireball (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);
    break;
  }
  return TRUE;
}

int bat_red (struct char_data *ch, int cmd, char *arg)
{
  struct char_data *vict;

  if (cmd)
    return FALSE;

  if (GET_POS (ch) != POSITION_FIGHTING)
    return FALSE;

  if (!ch->specials.fighting)
    return FALSE;

  /* Find a dude to do evil things upon ! */

  for (vict = world[ch->in_room].people; vict; vict = vict->next_in_room)
    if (vict->specials.fighting == ch && number (0, 2) == 0)
      break;

  if (!vict)
    return FALSE;

  act ("$n breathes fire.", 1, ch, 0, 0, TO_ROOM);
  cast_fire_breath (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);

  return TRUE;
}

int bat_white (struct char_data *ch, int cmd, char *arg)
{
  struct char_data *vict;

  if (cmd)
    return FALSE;

  if (GET_POS (ch) != POSITION_FIGHTING)
    return FALSE;

  if (!ch->specials.fighting)
    return FALSE;

  /* Find a dude to do evil things upon ! */

  for (vict = world[ch->in_room].people; vict; vict = vict->next_in_room)
    if (vict->specials.fighting == ch && number (0, 2) == 0)
      break;

  if (!vict)
    return FALSE;

  act ("$n breathes frost.", 1, ch, 0, 0, TO_ROOM);
  cast_frost_breath (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);

  return TRUE;
}

int bat_black (struct char_data *ch, int cmd, char *arg)
{
  struct char_data *vict;

  if (cmd)
    return FALSE;

  if (GET_POS (ch) != POSITION_FIGHTING)
    return FALSE;

  if (!ch->specials.fighting)
    return FALSE;

  /* Find a dude to do evil things upon ! */

  for (vict = world[ch->in_room].people; vict; vict = vict->next_in_room)
    if (vict->specials.fighting == ch && number (0, 2) == 0)
      break;

  if (!vict)
    return FALSE;

  act ("$n breathes acid.", 1, ch, 0, 0, TO_ROOM);
  cast_acid_breath (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);

  return TRUE;
}

int bat_blue (struct char_data *ch, int cmd, char *arg)
{
  struct char_data *vict;

  if (cmd)
    return FALSE;

  if (GET_POS (ch) != POSITION_FIGHTING)
    return FALSE;

  if (!ch->specials.fighting)
    return FALSE;

  /* Find a dude to do evil things upon ! */

  for (vict = world[ch->in_room].people; vict; vict = vict->next_in_room)
    if (vict->specials.fighting == ch && number (0, 2) == 0)
      break;

  if (!vict)
    return FALSE;

  act ("$n breathes lightning.", 1, ch, 0, 0, TO_ROOM);
  cast_lightning_breath (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, vict, 0);

  return TRUE;
}

int bat_green (struct char_data *ch, int cmd, char *arg)
{
  if (cmd)
    return FALSE;

  if (GET_POS (ch) != POSITION_FIGHTING)
    return FALSE;

  if (!ch->specials.fighting)
    return FALSE;

  if (number (0, 3) != 0)
    return FALSE;

  act ("$n breathes gas.", 1, ch, 0, 0, TO_ROOM);
  cast_gas_breath (GET_LEVEL (ch), ch, "", SPELL_TYPE_SPELL, 0, 0);

  return TRUE;
}

/* ********************************************************************
*  Special procedures for mobiles                                      *
******************************************************************** */

int guild_guard (struct char_data *ch, int cmd, char *arg)
{
  char buf[256], buf2[256];

  if (cmd > 6 || cmd < 1)
    return FALSE;

  strcpy (buf, "The guard humiliates you, and block your way.\n\r");
  strcpy (buf2, "The guard humiliates $n, and blocks $s way.");

  if ((ch->in_room == real_room (3017)) && (cmd == 3)) {
    if (GET_CLASS (ch) != CLASS_MAGIC_USER) {
      act (buf2, FALSE, ch, 0, 0, TO_ROOM);
      send_to_char (buf, ch);
      return TRUE;
    }
  } else if ((ch->in_room == real_room (3004)) && (cmd == 1)) {
    if (GET_CLASS (ch) != CLASS_CLERIC) {
      act (buf2, FALSE, ch, 0, 0, TO_ROOM);
      send_to_char (buf, ch);
      return TRUE;
    }
  } else if ((ch->in_room == real_room (3027)) && (cmd == 2)) {
    if (GET_CLASS (ch) != CLASS_THIEF) {
      act (buf2, FALSE, ch, 0, 0, TO_ROOM);
      send_to_char (buf, ch);
      return TRUE;
    }
  } else if ((ch->in_room == real_room (3021)) && (cmd == 2)) {
    if (GET_CLASS (ch) != CLASS_WARRIOR) {
      act (buf2, FALSE, ch, 0, 0, TO_ROOM);
      send_to_char (buf, ch);
      return TRUE;
    }

  }

  return FALSE;

}

int puff (struct char_data *ch, int cmd, char *arg)
{
  void do_say (struct char_data *ch, char *argument, int cmd);

  if (cmd)
    return (0);

  switch (number (0, 60)) {
  case 0:
    do_say (ch, "My god! It's full of stars!", 0);
    return (1);
  case 1:
    do_say (ch, "How'd all those fish get up here?", 0);
    return (1);
  case 2:
    do_say (ch, "I'm a very female dragon.", 0);
    return (1);
  case 3:
    do_say (ch, "I've got a peaceful, easy feeling.", 0);
    return (1);
  default:
    return (0);
  }
}

int fido (struct char_data *ch, int cmd, char *arg)
{

  struct obj_data *i, *temp, *next_obj;

  if (cmd || !AWAKE (ch))
    return (FALSE);

  for (i = world[ch->in_room].contents; i; i = i->next_content) {
    if (GET_ITEM_TYPE (i) == ITEM_CONTAINER && i->obj_flags.value[3]) {
      act ("$n savagely devour a corpse.", FALSE, ch, 0, 0, TO_ROOM);
      for (temp = i->contains; temp; temp = next_obj) {
        next_obj = temp->next_content;
        obj_from_obj (temp);
        obj_to_room (temp, ch->in_room);
      }
      extract_obj (i);
      return (TRUE);
    }
  }
  return (FALSE);
}

int janitor (struct char_data *ch, int cmd, char *arg)
{
  struct obj_data *i, *temp, *next_obj;

  if (cmd || !AWAKE (ch))
    return (FALSE);

  for (i = world[ch->in_room].contents; i; i = i->next_content) {
    if (IS_SET (i->obj_flags.wear_flags, ITEM_TAKE) &&
      ((i->obj_flags.type_flag == ITEM_DRINKCON) ||
        (i->obj_flags.cost <= 10))) {
      act ("$n picks up some trash.", FALSE, ch, 0, 0, TO_ROOM);

      obj_from_room (i);
      obj_to_char (i, ch);
      return (TRUE);
    }
  }
  return (FALSE);
}

int cityguard (struct char_data *ch, int cmd, char *arg)
{
  struct char_data *tch, *evil;
  int max_evil;

  if (cmd || !AWAKE (ch) || (GET_POS (ch) == POSITION_FIGHTING))
    return (FALSE);

  max_evil = 300;
  evil = 0;

  for (tch = world[ch->in_room].people; tch; tch = tch->next_in_room) {
    if (tch->specials.fighting) {
      if ((GET_ALIGNMENT (tch) < max_evil) &&
        (IS_NPC (tch) || IS_NPC (tch->specials.fighting))) {
        max_evil = GET_ALIGNMENT (tch);
        evil = tch;
      }
    }
  }

  if (evil && !IS_EVIL (evil->specials.fighting)) {
    act
      ("$n screams 'PROTECT THE INNOCENT!  BANZAI!!! CHARGE!!! ARARARAGGGHH!'",
      FALSE, ch, 0, 0, TO_ROOM);
    hit (ch, evil, TYPE_UNDEFINED);
    return (TRUE);
  }

  return (FALSE);
}


int pet_shops (struct char_data *ch, int cmd, char *arg)
{
  char buf[MAX_STRING_LENGTH], pet_name[256];
  int pet_room;
  struct char_data *pet;

  pet_room = ch->in_room + 1;

  if (cmd == 59) {              /* List */
    send_to_char ("Available pets are:\n\r", ch);
    for (pet = world[pet_room].people; pet; pet = pet->next_in_room) {
      sprintf (buf, "%8d - %s\n\r", 3 * GET_EXP (pet),
        pet->player.short_descr);
      send_to_char (buf, ch);
    }
    return (TRUE);
  } else if (cmd == 56) {       /* Buy */

    arg = one_argument (arg, buf);
    arg = one_argument (arg, pet_name);
    /* Pet_Name is for later use when I feel like it */

    if (!(pet = get_char_room (buf, pet_room))) {
      send_to_char ("There is no such pet!\n\r", ch);
      return (TRUE);
    }

    if (GET_GOLD (ch) < (GET_EXP (pet) * 3)) {
      send_to_char ("You don't have enough gold!\n\r", ch);
      return (TRUE);
    }

    GET_GOLD (ch) -= GET_EXP (pet) * 3;

    pet = read_mobile (pet->nr, REAL);
    GET_EXP (pet) = 0;
    SET_BIT (pet->specials.affected_by, AFF_CHARM);

    if (*pet_name) {
      sprintf (buf, "%s %s", pet->player.name, pet_name);
      free (pet->player.name);
      pet->player.name = str_dup (buf);

      sprintf (buf,
        "%sA small sign on a chain around the neck says 'My Name is %s'\n\r",
        pet->player.description, pet_name);
      free (pet->player.description);
      pet->player.description = str_dup (buf);
    }

    char_to_room (pet, ch->in_room);
    add_follower (pet, ch);

    /* Be certain that pet's can't get/carry/use/weild/wear items */
    IS_CARRYING_W (pet) = 1000;
    IS_CARRYING_N (pet) = 100;

    send_to_char ("May you enjoy your pet.\n\r", ch);
    act ("$n bought $N as a pet.", FALSE, ch, 0, pet, TO_ROOM);

    return (TRUE);
  }

  /* All commands except list and buy */
  return (FALSE);
}


/* Idea of the LockSmith is functionally similar to the Pet Shop */
/* The problem here is that each key must somehow be associated  */
/* with a certain player. My idea is that the players name will  */
/* appear as the another Extra description keyword, prefixed     */
/* by the words 'item_for_' and followed by the player name.     */
/* The (keys) must all be stored in a room which is (virtually)  */
/* adjacent to the room of the lock smith.                       */

int pray_for_items (struct char_data *ch, int cmd, char *arg)
{
  char buf[256];
  int key_room, gold;
  bool found;
  struct obj_data *tmp_obj, *obj;
  struct extra_descr_data *ext;

  if (cmd != 176)               /* You must pray to get the stuff */
    return FALSE;

  key_room = 1 + ch->in_room;

  strcpy (buf, "item_for_");
  strcat (buf, GET_NAME (ch));

  gold = 0;
  found = FALSE;

  for (tmp_obj = world[key_room].contents; tmp_obj;
    tmp_obj = tmp_obj->next_content)
    for (ext = tmp_obj->ex_description; ext; ext = ext->next)
      if (str_cmp (buf, ext->keyword) == 0) {
        if (gold == 0) {
          gold = 1;
          act ("$n kneels and at the altar and chants a prayer to Odin.",
            FALSE, ch, 0, 0, TO_ROOM);
          act ("You notice a faint light in Odin's eye.",
            FALSE, ch, 0, 0, TO_CHAR);
        }
        obj = read_object (tmp_obj->item_number, REAL);
        obj_to_room (obj, ch->in_room);
        act ("$p slowly fades into existence.", FALSE, ch, obj, 0, TO_ROOM);
        act ("$p slowly fades into existence.", FALSE, ch, obj, 0, TO_CHAR);
        gold += obj->obj_flags.cost;
        found = TRUE;
      }

  if (found) {
    GET_GOLD (ch) -= gold;
    GET_GOLD (ch) = MAX (0, GET_GOLD (ch));
    return TRUE;
  }

  return FALSE;
}

int worm_ritual (struct char_data *ch, int cmd, char *arg)
{
  struct obj_data *scroll, *herbs, *blood;
  struct char_data *tmpch;
  char buf[MAX_INPUT_LENGTH];
  bool found, equipped = FALSE;
  int room;
  static int scroll_nr = -1, herbs_nr = -1, blood_nr = -1, to_room = -1;

  if (scroll_nr < 1) {
    scroll_nr = real_object (5012);
    herbs_nr = real_object (5002);
    blood_nr = real_object (5003);
    to_room = real_room (5040);
  }

  if (cmd != 207)
    return FALSE;

  arg = one_argument (arg, buf);

  if (!(scroll = get_obj_in_list_vis (ch, buf, ch->carrying))) {
    scroll = ch->equipment[HOLD];
    equipped = TRUE;
  }

  /* which scroll */
  found = (scroll && (scroll->item_number == scroll_nr));

  if (!found)
    return FALSE;

  act ("$n recites $p.", TRUE, ch, scroll, 0, TO_ROOM);
  act ("You recite $p which dissolves.", FALSE, ch, scroll, 0, TO_CHAR);

  room = ch->in_room;
  blood = get_obj_in_list_num (blood_nr, world[room].contents);
  herbs = get_obj_in_list_num (herbs_nr, world[room].contents);

  if (!blood || !herbs || (blood->obj_flags.value[1] < 2))
    return TRUE;

  act ("$p dissolves into thin air.", TRUE, ch, herbs, 0, TO_ROOM);
  act ("$p dissolves into thin air.", TRUE, ch, herbs, 0, TO_CHAR);
  act ("$p is emptied from blood.", FALSE, ch, blood, 0, TO_ROOM);
  act ("$p is emptied from blood.", FALSE, ch, blood, 0, TO_CHAR);

  obj_from_room (herbs);
  extract_obj (herbs);          /* herbs dissapear */
  blood->obj_flags.value[1] = 0;        /* empty for blood */
  blood->obj_flags.weight -= 2; /* correct weight  */

  if (equipped)
    unequip_char (ch, HOLD);

  extract_obj (scroll);

  act ("You feel yanked downwards.....", FALSE, ch, 0, 0, TO_ROOM);
  act ("You feel yanked downwards.....", FALSE, ch, 0, 0, TO_CHAR);

  /* Move'em */
  tmpch = world[room].people;
  while (world[room].people) {
    char_from_room (tmpch);
    char_to_room (tmpch, to_room);
    tmpch = world[room].people;
  }
  return TRUE;
}

/* ********************************************************************
*  Special procedures for objects                                     *
******************************************************************** */



#define CHAL_ACT \
"You are torn out of reality!\n\r\
You roll and tumble through endless voids for what seems like eternity...\n\r\
\n\r\
After a time, a new reality comes into focus... you are elsewhere.\n\r"


int chalice (struct char_data *ch, int cmd, char *arg)
{
  /* 222 is the normal chalice, 223 is chalice-on-altar */

  struct obj_data *chalice;
  char buf1[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH];
  static int chl = -1, achl = -1;

  if (chl < 1) {
    chl = real_object (222);
    achl = real_object (223);
  }

  switch (cmd) {
  case 10:                     /* get */
    if (!(chalice = get_obj_in_list_num (chl, world[ch->in_room].contents))
      && CAN_SEE_OBJ (ch, chalice))
      if (!(chalice = get_obj_in_list_num (achl,
            world[ch->in_room].contents)) && CAN_SEE_OBJ (ch, chalice))
        return (0);

    /* we found a chalice.. now try to get us */
    do_get (ch, arg, cmd);
    /* if got the altar one, switch her */
    if (chalice == get_obj_in_list_num (achl, ch->carrying)) {
      extract_obj (chalice);
      chalice = read_object (chl, VIRTUAL);
      obj_to_char (chalice, ch);
    }
    return (1);
    break;
  case 67:                     /* put */
    if (!(chalice = get_obj_in_list_num (chl, ch->carrying)))
      return (0);

    argument_interpreter (arg, buf1, buf2);
    if (!str_cmp (buf1, "chalice") && !str_cmp (buf2, "altar")) {
      extract_obj (chalice);
      chalice = read_object (achl, VIRTUAL);
      obj_to_room (chalice, ch->in_room);
      send_to_char ("Ok.\n\r", ch);
    }
    return (1);
    break;
  case 176:                    /* pray */
    if (!(chalice = get_obj_in_list_num (achl, world[ch->in_room].contents)))
      return (0);

    do_action (ch, arg, cmd);   /* pray */
    send_to_char (CHAL_ACT, ch);
    extract_obj (chalice);
    act ("$n is torn out of existence!", TRUE, ch, 0, 0, TO_ROOM);
    char_from_room (ch);
    char_to_room (ch, real_room (2500));        /* before the fiery gates */
    do_look (ch, "", 15);
    return (1);
    break;
  default:
    return (0);
    break;
  }
  return (0);
}

int kings_hall (struct char_data *ch, int cmd, char *arg)
{
  if (cmd != 176)
    return (0);

  do_action (ch, arg, 176);

  send_to_char ("You feel as if some mighty force has been offended.\n\r",
    ch);
  send_to_char (CHAL_ACT, ch);
  act ("$n is struck by an intense beam of light and vanishes.",
    TRUE, ch, 0, 0, TO_ROOM);
  char_from_room (ch);
  char_to_room (ch, real_room (1420));  /* behind the altar */
  do_look (ch, "", 15);
  return (1);
}