/* ************************************************************************
*  file: spec_generic.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 <stdio.h>
#include <strings.h>
#include <ctype.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"

/*   external vars  */

extern struct room_data *world;
extern int top_of_world;
extern struct zone_data *zone_table;
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 struct title_type titles[4][28];

/* extern procedures */

void hit(struct char_data *ch, struct char_data *victim, int type);
void gain_exp(struct char_data *ch, int gain);
char *strdup(char *source);
struct char_data *get_char_room_vis(struct char_data *ch,char *name);
void do_say(struct char_data *ch, char *argument, int cmd);
void do_tell(struct char_data *ch, char *argument, int cmd);

/* Data declarations */

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

#define LAW_ENFORCER(x) (((x)->nr==real_mobile(3081) || \
		(x)->nr==real_mobile(3082) || (x)->nr==real_mobile(12581) \
		|| (x)->nr==real_mobile(12582)))


/* ********************************************************************
*  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);
}
/* NOT a room proc, folks... */
int guild(struct char_data *ch, struct char_data *pl, 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++);

	/* Keep people from practicing at the wrong guild -Sman */
	if(ISNPC(ch) && GET_CLASS(pl) != GET_NPCCLASS(ch) &&
	(!(pl->specials.act&PLR_ISMULTIWA) || !(ch->specials.act&ACT_HAS_WA)) &&
	(!(pl->specials.act&PLR_ISMULTIMU) || !(ch->specials.act&ACT_HAS_MU)) &&
	(!(pl->specials.act&PLR_ISMULTITH) || !(ch->specials.act&ACT_HAS_TH)) &&
	(!(pl->specials.act&PLR_ISMULTICL) || !(ch->specials.act&ACT_HAS_CL)) &&
					*arg && ch!=pl) {
		if(CAN_SEE(ch,pl)) {
			sprintf(buf,"%s Hey! This isn't your guild!",GET_NAME(pl));
			do_tell(ch,buf,0);
		} else
			do_say(ch,"Whoever you are, this isn't your guild!",0);
		return(TRUE);
	}

	/* This is setup to go off of the ACT_HAS_xx bits of the guildmaster */
	/* -- be sure the guildmaster has only one set, for general sanity   */

	switch (IS_NPC(ch) ? GET_NPCCLASS(ch) : 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, pl);
				send_to_char("You can practise any of these spells:\n\r", pl);
				for(i=0; *spells[i] != '\n'; i++) {
					if (spell_info[i+1].spell_pointer &&
					    (spell_info[i+1].min_level_magic <= GET_LEVEL(pl))) {
						send_to_char(spells[i], pl);
						send_to_char(how_good(pl->skills[i+1].learned), pl);
						send_to_char("\n\r", pl);
					}
				}
				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", pl);
				return(TRUE);
			}
			if (GET_LEVEL(pl) < spell_info[number].min_level_magic) {
				send_to_char("You do not know of this spell...\n\r", pl);
				return(TRUE);
			}
			if (pl->specials.spells_to_learn <= 0) {
				send_to_char("You do not seem to be able to practice now.\n\r", pl);
				return(TRUE);
			}
			if (pl->skills[number].learned >= 95) {
				send_to_char("You are already learned in this area.\n\r", pl);
				return(TRUE);
			}

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

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

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

		} break;

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

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

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

		} break;

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

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

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

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

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

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


/* A room proc */
int dump(int room, struct char_data *ch, int cmd, char *arg) 
{
	struct obj_data *k;
	char buf[MAX_INPUT_LENGTH];
	struct char_data *tmp_char;
	int value=0;

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

	if(!ch)
		return(FALSE);

	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 vanishes 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;
	}
}

int mayor(struct char_data *ch, struct char_data *pl, 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(IS_TRUSTED(victim)) return;
	if(world[ch->in_room].room_flags & SAFE) 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 poison(struct char_data *ch, struct char_data *pl, int cmd, char *arg)
{
	void cast_poison( byte 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, struct char_data *pl, 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) && !IS_TRUSTED(cons) && (number(1,5)==1))
			npc_steal(ch,cons); 

	return TRUE;
}

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

	void cast_burning_hands( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_chill_touch( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_colour_spray( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_energy_drain( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_fireball( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_magic_missile( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_blindness( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *tar_ch, struct obj_data *tar_obj );
	void cast_curse( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *tar_ch, struct obj_data *tar_obj );
	void cast_sleep( byte 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) return FALSE;


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

	for (vict = world[ch->in_room].people; vict; vict = vict->next_in_room )
		if (vict->specials.fighting==ch)
			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;

}


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

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

	if (cmd>6 || cmd<1)
		return(witness(ch,pl,cmd,arg));
	if (IS_TRUSTED(ch)){
		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(pl) != CLASS_MAGIC_USER)&&
		    (!IS_SET(pl->specials.act,PLR_ISMULTIMU))) {
			act(buf2, FALSE, pl, 0, 0, TO_ROOM);
			send_to_char(buf, pl);
			return TRUE;
		}
	} else if ((ch->in_room == real_room(3004)) && (cmd == 1)) {
		if ((GET_CLASS(pl) != CLASS_CLERIC)&&
		    (!IS_SET(pl->specials.act,PLR_ISMULTICL))) {
			act(buf2, FALSE, pl, 0, 0, TO_ROOM);
			send_to_char(buf, pl);
			return TRUE;
		}
	} else if ((ch->in_room == real_room(3027)) && (cmd == 2)) {
		if ((GET_CLASS(pl) != CLASS_THIEF)&&
		    (!IS_SET(pl->specials.act,PLR_ISMULTITH))) {
			act(buf2, FALSE, pl, 0, 0, TO_ROOM);
			send_to_char(buf, pl);
			return TRUE;
		}
	} else if ((ch->in_room == real_room(3021)) && (cmd == 2)) {
		if ((GET_CLASS(pl) != CLASS_WARRIOR)&&
		    (!IS_SET(pl->specials.act,PLR_ISMULTIWA))) {
			act(buf2, FALSE, pl, 0, 0, TO_ROOM);
			send_to_char(buf, pl);
			return TRUE;
		}

	}

	return FALSE;

}

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

	if (cmd!=4)
		return FALSE;

	strcpy(buf,  "The Arena Gods bid you farewell.\n\r");
	strcpy(buf2, "The Arena Gods bid $n farewell.\n\r");

	if ((ch->in_room == real_room(3751)) && (cmd == 4)) {/*EXIT room*/
		act(buf2, FALSE, ch, 0, 0, TO_ROOM);
		send_to_char(buf, ch);
		if(ch->specials.arena > ARENA_NOTPLAYING){
			ch->specials.arena=ARENA_NOTPLAYING;
			GET_MOVE(ch)=ch->specials.arena_move;
			GET_MANA(ch)=ch->specials.arena_mana;
			GET_HIT(ch)=ch->specials.arena_hits;
			while(ch->affected)
				affect_remove(ch,ch->affected);
			ch->affected=ch->tmp_affected;
			ch->tmp_affected=NULL;
			ch->specials.affected_by=ch->specials.arena_affvector;
		}
	}

	return FALSE;

}


int devour(struct char_data *ch, struct char_data *pl, 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])
				|| GET_ITEM_TYPE(i)==ITEM_FOOD) {

			act("$n savagely devours $o.", FALSE,ch,i,0,TO_ROOM);

			if(GET_ITEM_TYPE(i)==ITEM_CONTAINER)
				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 pet_shops(int room, struct char_data *ch, int cmd, char *arg)
{
	char buf[MAX_STRING_LENGTH], pet_name[256];
	int pet_room;
	struct char_data *pet;
	struct char_data *tmp_ch;
	struct follow_type *fol;
	int count;

	if(!ch)
		return(FALSE);

	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);
		}
		for(count=0, fol=ch->followers; fol; fol=fol->next){
			if(IS_NPC(fol->follower) &&
			   IS_AFFECTED(fol->follower, AFF_CHARM)){
				count++;
			}
		}
		if(count>=2){
			send_to_char("You may only have 2 pets\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 = strdup(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 = strdup(buf);
		}

		char_to_room(pet, ch->in_room,0);
		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);
}

void new_class(struct char_data *ch, int newclass)
{
	char oldclass[80];
	char sendbuf[80];
	char buf[80];

	switch(GET_CLASS(ch)){
	  case CLASS_THIEF:
		SET_BIT(ch->specials.act,PLR_ISMULTITH);
		strcpy(oldclass,"Thief"); break;
	  case CLASS_MAGIC_USER:
		SET_BIT(ch->specials.act,PLR_ISMULTIMU);
		strcpy(oldclass,"Magic User"); break;
	  case CLASS_CLERIC:
		SET_BIT(ch->specials.act,PLR_ISMULTICL);
		strcpy(oldclass,"Cleric"); break;
	  case CLASS_WARRIOR:
		SET_BIT(ch->specials.act,PLR_ISMULTIWA);
		strcpy(oldclass,"Warrior"); break;
	  default:
		send_to_char("Invalid class!\n\r",ch);
		return;
	}
	GET_CLASS(ch)=newclass;
	switch(GET_CLASS(ch)){
	  case CLASS_WARRIOR:
		sprintf(buf,"Warrior/%s",oldclass);break;
	  case CLASS_THIEF:
		sprintf(buf,"Thief/%s",oldclass);break;
	  case CLASS_CLERIC:
		sprintf(buf,"Cleric/%s",oldclass);break;
	  case CLASS_MAGIC_USER:
		sprintf(buf,"Magic User/%s",oldclass);break;
	  default:
		send_to_char("Serious error in new class!\n\r",ch);
		return;
	}
	sprintf(sendbuf,"You are now a %s!\n\r",buf);
	send_to_char(sendbuf,ch);
}

#define MULTI_MIN_LEV		5	/* minimum level to change class*/
#define MULTI_COST_PER_LEV	100000	/* 100k per level to change */

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

	char buf[300];
	long cost;

	if (cmd!=233){
		return(FALSE);
	}

	if (IS_NPC(ch)) {
		send_to_char("Monsters have no class, go away!\n\r",ch);
		return(TRUE);
	}

	arg = one_argument(arg, buf);
	if(strcmp(buf,"cleric")&&strcmp(buf,"mu")&&
	   strcmp(buf,"thief")&&strcmp(buf,"warrior")) {
		send_to_char("You can only switch to a THIEF, CLERIC, MU, or WARRIOR.\n\r",ch);
		return(TRUE);
	}

	if(IS_SET(ch->specials.act,PLR_ISMULTITH)||
	   IS_SET(ch->specials.act,PLR_ISMULTIWA)||
	   IS_SET(ch->specials.act,PLR_ISMULTIMU)||
	   IS_SET(ch->specials.act,PLR_ISMULTICL)){
		send_to_char("You can't switch more than once (yet).\n\r",ch);
		return(TRUE);
	}

	if (GET_LEVEL(ch)<MULTI_MIN_LEV){
		send_to_char("You need more levels first!\n\r",ch);
		return(TRUE);
	}

	if (GET_LEVEL(ch)>LV_IMMORTAL || IS_TRUSTED(ch)){
		send_to_char("Gods may not multiclass!\n\r",ch);
		return(TRUE);
	}

	cost=GET_LEVEL(ch)*MULTI_COST_PER_LEV;

	if(GET_GOLD(ch)<cost){
		sprintf(buf,"You need %d coins to change class at your level.\n\r",cost);
		send_to_char(buf,ch);
		return(TRUE);
	}

	if(GET_EXP(ch)<titles[GET_CLASS(ch)-1][GET_LEVEL(ch)].exp){
		sprintf(buf,"You need %d experience points to multiclass at your level.\n\r",titles[GET_CLASS(ch)-1][GET_LEVEL(ch)].exp);
		send_to_char(buf,ch);
		return(TRUE);
	}

	if(!strcmp(buf,"cleric")&&(GET_CLASS(ch)!=CLASS_CLERIC)){
		new_class(ch,CLASS_CLERIC);
	} else if (!strcmp(buf,"mu")&&(GET_CLASS(ch)!=CLASS_MAGIC_USER)){
		new_class(ch,CLASS_MAGIC_USER);
	} else if (!strcmp(buf,"thief")&&(GET_CLASS(ch)!=CLASS_THIEF)){
		new_class(ch,CLASS_THIEF);
	} else if (!strcmp(buf,"warrior")&&(GET_CLASS(ch)!=CLASS_WARRIOR)){
		new_class(ch,CLASS_WARRIOR);
	} else {
		send_to_char("You are already that class!\n\r",ch);
		return(TRUE);
	}

	GET_GOLD(ch) -= cost;
	sprintf(buf,"It costs you %d coins to switch class.\n\r",cost);
	send_to_char(buf,ch);

	/* Change class. Set player's level at 1. Zero experience. Give
	necessary stats to new class.*/

	GET_LEVEL(ch) = 1;
	GET_EXP(ch) = 1;
	ch->specials.spells_to_learn = 0;
	set_title(ch);

	switch (GET_CLASS(ch)) {

		case CLASS_THIEF : {
			ch->skills[SKILL_SNEAK].learned = 10;
			ch->skills[SKILL_HIDE].learned =  5;
			ch->skills[SKILL_STEAL].learned = 15;
			ch->skills[SKILL_BACKSTAB].learned = 10;
			ch->skills[SKILL_PICK_LOCK].learned = 10;
		} break;

	}
	send_to_char("You feel awesome power surge coarse through your veins.\n\r",ch);
	send_to_char("You feel like a new person!\n\r",ch);
}

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

	void cast_burning_hands( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_chill_touch( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_colour_spray( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_energy_drain( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_fireball( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_magic_missile( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *victim, struct obj_data *tar_obj );
	void cast_blindness( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *tar_ch, struct obj_data *tar_obj );
	void cast_curse( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *tar_ch, struct obj_data *tar_obj );
	void cast_sleep( byte level, struct char_data *ch, char *arg, int type,
		struct char_data *tar_ch, struct obj_data *tar_obj );

	if(cmd) return FALSE;

	if(!AWAKE(ch)) return FALSE;

	/* Find a pc-killer or a thief to do battle with */
	if(GET_POS(ch)!=POSITION_FIGHTING || !ch->specials.fighting) {
		for(vict=world[ch->in_room].people; vict; vict=vict->next_in_room){
			if(!IS_NPC(vict) && CAN_SEE(ch, vict) &&
			   (IS_SET(vict->specials.act, PLR_ISKILLER) ||
			    IS_SET(vict->specials.act, PLR_ISTHIEF))){
				act("$n screams 'Outlaw! Fresh blood! Kill!'", FALSE, ch, 0, 0, TO_ROOM);
				hit(ch, vict, TYPE_UNDEFINED);
				return(TRUE);
			}
		}
		return (FALSE);
	}

	/* We're fighting someone, so
	 * Find a dude to to evil things upon ! */

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

	if (!vict)
		return FALSE;


	if( (vict!=ch->specials.fighting) && (GET_LEVEL(ch)>13) && (number(0,4)==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,4)==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,3)==0) )
	{
		act("$n utters the words 'kholian 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,5)==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 (number(0,16)) {
		case 0: break; /* do nothing */
		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;

}

bool transact(struct char_data *from,struct obj_data *merchandise,
              struct char_data *to,int value)
{
   char buf[MAX_STRING_LENGTH];
   if (to->points.gold >= value && from->in_room == to->in_room) {
      to->points.gold -= value;
      from->points.gold += value;
      if(merchandise) {
         obj_from_char(merchandise);
         obj_to_char(merchandise,to);
         sprintf(buf,"You buy %s for %d coins.\n\r\n\r",merchandise->short_description, value);
         send_to_char(buf,to);
         sprintf(buf,"%s gives you %d coins for %s.\n\r\n\r",to->player.name,value,
            merchandise->short_description);
         send_to_char(buf,from);
         sprintf(buf,"%s sells %s to %s.\n\r\n\r",from->player.name,
               merchandise->short_description, to->player.name);
         send_to_room_except_two(buf,from->in_room,from,to);
         return TRUE;
      } else {
         sprintf(buf,"You give %s %d coins.\n\r\n\r",GET_NAME(from),value);
         send_to_char(buf,to);
         act("$n gives $N some money.",TRUE,to,0,from,TO_NOTVICT);
         act("$N pays your price.",FALSE,from,0,to,TO_CHAR);
         return TRUE;
      }
   } else {
      sprintf(buf,"%s doesn't have the funds for the exchange.\n\r\n\r",GET_NAME(to));
      send_to_char(buf,from);
      send_to_char(buf,to);
      return FALSE;
   }
}


int citizenship(struct char_data *ch, struct char_data *pl, int cmd, char *arg)
{
	struct char_data *c_obj;
	char arg1[MAX_STRING_LENGTH],arg2[MAX_STRING_LENGTH],buf[MAX_STRING_LENGTH];
	bool reject_bad_item=FALSE;

	if(pl) {
		if(cmd==56) { /* Buy */
      			argument_interpreter(arg,arg1,arg2);
			if (!*arg1) 
				return(FALSE);
			if(*arg2) {
				c_obj=get_char_room(arg1,ch->in_room);
				if(c_obj!=ch)
					return(FALSE);
				reject_bad_item=TRUE;
			}
			if(!strcmp(arg1,"midgaard")) {
				if(GET_HOME(pl)==1) {
					if(CAN_SEE(ch,pl)) {
						sprintf(buf,"%s Hey, that's already your hometown!",GET_NAME(pl));
						do_tell(ch,buf,0);
					} else
						do_say(ch,"Hey someone, that's already your hometown!",0);
					return(TRUE);
				}
				if(transact(ch,NULL,pl,20000)) {
					GET_HOME(pl)=1;
					send_to_char("Your hometown has been changed!\n\r\n\r",pl);
					return(TRUE);
				}
			} else if(!strcmp(arg1,"anapest")) {
				if(GET_HOME(pl)==2) {
					if(CAN_SEE(ch,pl)) {
						sprintf(buf,"%s Hey, that's already your hometown!",GET_NAME(pl));
						do_tell(ch,buf,0);
					} else
						do_say(ch,"Hey someone, that's already your hometown!",0);
					return(TRUE);
				}
				if(transact(ch,NULL,pl,20000)) {
					GET_HOME(pl)=2;
					send_to_char("Your hometown has been changed!\n\r\n\r",pl);
					return(TRUE);
				}
			} else if(reject_bad_item) {
				if(CAN_SEE(ch,pl)) {
					sprintf(buf,"%s I don't know that place.",GET_NAME(pl));
					do_tell(ch,buf,0);
				} else
					do_say(ch,"Where?",0);
				return(TRUE);
			}
		}
	}
	return(FALSE);
}


/* Here are the routines governing the jail system */

/* some flags for the apprehender, in spec[2] */
#define APPR_PRISONERS  1
#define APPR_RHETORICAL 2
#define APPR_RETURN     4

int apprehender(struct char_data *ch,struct char_data *pl,int cmd,char *arg)
{
	struct char_data *tch,*temp,*temp2;
	char name[MAX_INPUT_LENGTH],message[MAX_INPUT_LENGTH],buf[MAX_STRING_LENGTH];
	bool action_made=FALSE,pause=FALSE,found_one;
	int to_room,temp_room;

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

	if(pl) {
		if(pl->specials.arrest_by!=ch || GET_POS(pl)<POSITION_SLEEPING)
			return(FALSE);
		switch(cmd) {
			case 19: /* tell-> make sure it is tell <chname> */
				if(ch->specials.spec[2] & APPR_RHETORICAL) {
					half_chop(arg,name,message);
					if(ch!=get_char_room_vis(pl,name))
						return(FALSE);
					strcpy(arg,message);
					if(strstr(arg,"yes"))
						do_say(ch,"That was a rhetorical question, bub.",0);
					else if(strstr("no",arg))
						do_say(ch,"I hope that's the case.",0);
				}
				break;
			case 17: /* say */
			case 169: /* short (') version of say */
				if(ch->specials.spec[2] & APPR_RHETORICAL) {
					do_say(pl,arg,0);
					if(strstr(arg,"yes"))
						do_say(ch,"That was a rhetorical question.",0);
					else if(strstr("no",arg))
						do_say(ch,"I hope that's the case.",0);
				}
				break;
			case 25: /* Kill */
			case 70: /* Hit */
			case 84: /* Cast */
			case 152: /* Sneak */
			case 153: /* Hide */
			case 154: /* Backstab */
			case 157: /* Bash */
			case 159: /* Kick */
				message[0]=0;
				strcat(message,GET_NAME(pl));
				strcat(message," None of that while you're in my custody!");
				do_tell(ch,message,0);
				return(TRUE);
				break;
			case 156: /* pl should have a chance of success */
				break;
			default:
				break;
		}
	} else if(GET_POS(ch)==POSITION_FIGHTING) {
		if(!number(0,4))
			do_say(ch,"Resistance is stupid!",0);
	} else {
		ch->specials.spec[2]&=~APPR_RHETORICAL;
		if(IS_SET(ch->specials.act,NPC_OUTLAW)) {
			/* Release arrested people? */
			switch(number(1,15)) {
			    case 1:
				act("$n fidgets.",TRUE,ch,0,0,TO_ROOM);
				break;
			    case 2:
				do_say(ch,"Ack! I don't know what to do with myself.",0);
				break;
			    case 3:
				do_action(ch,"",53); /* cry */
				break;
			    default:
				break;
			}
			/* Get rid of the arrested people to avoid the */
			/* possibility of strange loops and the like   */
			for(tch=ch->specials.arrest_link;tch;tch=ch->specials.arrest_link) {
				tch->specials.arrest_by=NULL;
				ch->specials.arrest_link=tch->specials.arrest_link;
				tch->specials.arrest_link=NULL;
			}
			return(FALSE);
		}
		if(!ch->specials.fighting)
		  for(tch=world[ch->in_room].people; tch; tch=tch->next_in_room)
			if (IS_HUMANOID(tch) && CAN_SEE(ch, tch) && tch !=ch &&
					tch->specials.fighting) {
				if(IS_HUMANOID(tch->specials.fighting) &&
					!LAW_ENFORCER(tch) &&
					!LAW_ENFORCER(tch->specials.fighting)) {

					stop_fighting(tch);
					action_made=TRUE;
				} else if(LAW_ENFORCER(tch) && !action_made) {
					do_action(ch,"",26);
					hit(ch,tch->specials.fighting,TYPE_UNDEFINED);
					break;
				}
			}
		if(action_made) {
			do_say(ch,"Break it up! Break it up!",0);
			act("$n stops the fight.",TRUE,ch,0,0,TO_ROOM);
			do_say(ch,"Now, do I have to take you people in?",0);
			ch->specials.spec[2]|=APPR_RHETORICAL;
			return(TRUE);
		}
		if(!(ch->specials.spec[2] & APPR_RETURN)) {
		   found_one=FALSE;
 		   for(tch=world[ch->in_room].people; tch; tch=tch->next_in_room) {
			if(IS_OUTLAW(tch) && !tch->specials.arrest_by && GET_POS(ch)>=POSITION_SLEEPING){
			    if(!AWAKE(tch)) {
				do_wake(ch,GET_NAME(tch),0);
				do_say(ch,"Wake up, Outlaw!",0);
			    } else if(GET_POS(tch)>POSITION_SLEEPING) {
				if(!found_one) {
					sprintf(buf,"You're coming with me, %s!",fname(GET_NAME(tch)));
					do_say(ch,buf,0);
					found_one=TRUE;
				} else {
					sprintf(buf,"You too, %s!",fname(GET_NAME(tch)));
					do_say(ch,buf,0);
				}
				if(GET_POS(tch)<POSITION_STANDING) {
					GET_POS(tch)=POSITION_STANDING;
					act("$n forces $N on $S feet.",
						TRUE,ch,0,tch,TO_NOTVICT);
					act("$n forces you on your feet.",
						FALSE,ch,0,tch,TO_VICT);
				}
				tch->specials.arrest_by=ch;
				tch->specials.arrest_link=ch->specials.arrest_link;
				ch->specials.arrest_link=tch;
				ch->specials.spec[2]|=APPR_PRISONERS;
				pause=TRUE;
			    }
			}
		   }
		}
		if((ch->specials.spec[2] & APPR_PRISONERS) && !pause) {
			/* Headed to jail */
			/* Will need a routing method. Probably static */
			/*if(route method fails...)*/
			to_room=real_room(jail_hometown(ch->player.hometown));
			if(to_room==-1) {
				do_say(ch,"Eek! I can't find the jail!",0);
				ch->specials.spec[2]=0;/*
				for(k=ch->specials.arrest_link;k=*/
				teleport_to(ch,NOWHERE);
				return(FALSE);
			}
			do_say(ch,"Shoot! I'm a little lost.",0);
			act("$n snaps $s fingers.",FALSE,ch,0,0,TO_ROOM);
			temp_room=ch->in_room;
			teleport_to(ch,to_room);
			/* To follow the in-room list of chars, we need */
			/* to save the next guy ahead of us before we   */
			/* teleport, so we don't teleport people in the */
			/* place we're teleporting to...                */
			for(temp=ch->specials.arrest_link;temp;temp=ch->specials.arrest_link) {
				if(temp->in_room!=temp_room) {
					sprintf(buf,"%s You can't get away that easily, bub!",GET_NAME(temp));
					do_tell(ch,buf,0);
				}
				teleport_to(temp,to_room);
				temp->specials.arrest_by=NULL;
				ch->specials.arrest_link=temp->specials.arrest_link;
				temp->specials.arrest_link=NULL;
			}
			ch->specials.spec[2]|=APPR_RETURN;
			ch->specials.spec[2]&=~APPR_PRISONERS;
			ch->specials.spec[3]=temp_room;
		} else if(ch->specials.spec[2] & APPR_RETURN) {
			do_say(ch,"Sorry I can't stay, but I have work to do!",0);
			teleport_to(ch,ch->specials.spec[3]);
			act("$n smiles inwardly at a job well done.",TRUE,ch,0,0,TO_ROOM);
			ch->specials.spec[2]=0; /* change for extra flags */
		}
	}

	return(FALSE);
}

int witness(struct char_data *ch,struct char_data *pl,int cmd,char *arg)
{
	struct char_data *vict=NULL;
	char buf[MAX_STRING_LENGTH],arg1[MAX_STRING_LENGTH],arg2[MAX_STRING_LENGTH];
	if(GET_POS(ch)<POSITION_SLEEPING)
		return(FALSE);
	if(!(zone_table[world[ch->in_room].zone].flags & ZONE_HOMETOWN))
		return(FALSE);

	if(pl) {
		if(!IS_HUMANOID(pl) || !CAN_SEE(ch,pl))
			return(FALSE);
		switch(cmd) {
			case 84: /*cast*/
			/*	if(!..is_offensive..) return(FALSE); */
			case 25: /* kill */
			case 70: /* hit */
			case 154: /* backstab */
			case 156: /* steal -should have a different treatment*/
			case 157: /* bash */
			case 159: /* kick */
			case 236: /* murder */
				one_argument(arg,arg1);
				if(!(vict=get_char_room_vis(pl,arg1)))
					return(FALSE);
				if(!IS_HUMANOID(vict))
					return(FALSE);
				if(vict->specials.fighting==pl)
					return(FALSE);
				argument_interpreter(arg,arg1,arg2);
				if(vict->specials.fighting==ch || ch->specials.fighting==vict) {
					sprintf(buf,"%s Hey, thanks!",fname(GET_NAME(pl)));
					do_tell(ch,buf,0);
					return(FALSE);
				}
				if(!ch->specials.witnessing) {
					ch->specials.witnessing=pl;
					ch->specials.witness_vict=vict;
					ch->specials.witness_cmd=cmd;
				}
				break;
		}
	} else {
		if(ch->specials.witnessing) {
			if(ch->specials.witnessing->specials.fighting==
					ch->specials.witness_vict) {
				if(ch->specials.witness_vict==ch)
				 sprintf(buf,"%s is an Outlaw! %s %s me!",
					PERS(ch->specials.witnessing,ch),
					HSSH(ch->specials.witnessing),
					(ch->specials.witness_cmd==156 ?
					"stole from" : "attacked"));
				else
				 sprintf(buf,"%s is an Outlaw! I saw %s %s %s!",
					PERS(ch->specials.witnessing,ch),
					HMHR(ch->specials.witnessing),
					(ch->specials.witness_cmd==156 ?
					"steal from" : "attack"),
					fname(GET_NAME(ch->specials.witness_vict)));
				do_yell(ch,buf,0);
				if(IS_NPC(ch->specials.witnessing))
					SET_BIT(ch->specials.witnessing->specials.act,NPC_OUTLAW);
				else
					SET_BIT(ch->specials.witnessing->specials.act,PLR_OUTLAW);
			}
			ch->specials.witnessing=NULL;
			ch->specials.witness_vict=NULL;
			ch->specials.witness_cmd=0;
		}
	}
	return(FALSE);
}

int judge(struct char_data *ch,struct char_data *pl,int cmd,char *arg)
{
	return(FALSE);
}

int jailtally(struct obj_data *obj, struct char_data *ch,int cmd, char *arg)
{
	struct obj_data *t_obj;
	struct char_data *k;
	char buf[MAX_STRING_LENGTH];
	int room;

	if(!ch)
		return(FALSE);

	if(cmd==63 && ch && GET_POS(ch) >= POSITION_RESTING) {
		while(isspace(*arg))
			arg++;
		t_obj=get_obj_in_list_vis(ch,arg,world[ch->in_room].contents);
		if(t_obj==obj) {
			if(IS_SET(world[ch->in_room].room_flags,JAIL))
				room=ch->in_room;
			else if(IS_SET(world[ch->in_room+1].room_flags,JAIL))
				room=ch->in_room+1;
			else
				return(FALSE);
			send_to_char("The tally sheet lists the following inmates:\n\r",ch);
			for(k=world[room].people;k;k=k->next_in_room) {
				if(IS_OUTLAW(k) && k->specials.jail_time) {
					sprintf(buf,"$N - %d more ticks",k->specials.jail_time);
					act(buf,FALSE,ch,0,k,TO_CHAR);
				}
			}
			return(TRUE);
		}
	}
	return(FALSE);
}
		
int jailkeeper(struct char_data *ch,struct char_data *pl,int cmd,char *arg)
{
	/* This relies on the jail room being one number higher than the */
	/* room in which the jailkeeper resides...                       */

	struct char_data *k,*temp_ch;
	struct obj_data *obj;
	int room;
	bool change_room,feeding=FALSE;
	char buf[MAX_STRING_LENGTH],arg1[MAX_INPUT_LENGTH];
	extern struct time_info_data time_info;

	if(room == top_of_world - 1 || 
			!IS_SET(world[ch->in_room+1].room_flags,JAIL)) {
		return(FALSE); /* temporary solution */
	}
	if(GET_POS(ch)<=POSITION_FIGHTING)
		return(FALSE);

	if(pl) {
		switch(cmd) {/*
			case 1: /* movement commands * /
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
				if(
			case ...unlock..:
			case ...open..:
				break;*/

			case 156: /* steal */
				argument_interpreter(arg,arg1,buf);
				if(ch==get_char_room_vis(pl,buf) && !IS_TRUSTED(pl)) {
					do_action(ch,pl->player.name,130);
					return(TRUE);
				}
				break;
			default:
				break;
		}
	} else {
		if(GET_POS(ch)==POSITION_SLEEPING) {
			do_wake(ch,"",0);
			return(FALSE);
		}
		if(GET_POS(ch)==POSITION_RESTING ||
				GET_POS(ch)==POSITION_SITTING) {
			do_stand(ch,"",0);
			return(FALSE);
		}

		change_room=FALSE;
		/* Check the current room for outlaws */
		for(k=world[ch->in_room].people;k;k=k->next_in_room) {
			if(k!=ch && IS_OUTLAW(k) && !IS_TRUSTED(k))
				change_room=TRUE;
		}
		/* Check the jail room for various things */
		for(k=world[ch->in_room+1].people;k;k=k->next_in_room) {
			if(LAW_ENFORCER(k)&&(k->specials.spec[2] & APPR_RETURN))
				continue;
			if(IS_OUTLAW(k) && !IS_TRUSTED(k))
				k->specials.jail_time--;
			if((!IS_OUTLAW(k) || !k->specials.jail_time ||
				k->specials.jail_time<=-1) && !IS_TRUSTED(k))
				change_room=TRUE;
		}

		if(!time_info.hours%8) {
			if(!ch->specials.spec[0]) {
				feeding=TRUE;
				change_room=TRUE;
				ch->specials.spec[0]=1;
			}
		} else
			ch->specials.spec[0]=0;

		if(!change_room) {
			do_close(ch,"door",0);
			do_lock(ch,"door",0);
			return(FALSE);
		}

		room=ch->in_room;
		/* Open a passage, if possible */
		do_unlock(ch,"door",0);
		do_open(ch,"door",0);
		/* Throw in any outlaws */
		for(k=world[ch->in_room].people;k;k=temp_ch) {
			temp_ch = k->next_in_room;
			if(k!=ch && IS_OUTLAW(k) && !IS_TRUSTED(k)) {
				if(k->specials.jail_time) {
					do_say(ch,"Laws are laws, buddy.",0);
					k->specials.jail_time += 50;
				} else {
					do_say(ch,"Turning yourelf in, eh?",0);
					k->specials.jail_time = 120;
				}
				act("$N throws you in the cell.",FALSE,k,0,ch,TO_CHAR);
				act("$N throws $n in the cell.",FALSE,k,0,ch,TO_NOTVICT);
				char_from_room(k);
				char_to_room(k,room+1,0);
				act("$n is thrown into the room.",FALSE,k,0,0,TO_ROOM);
			}
		}
		act("$n enters the cell.",TRUE,ch,0,0,TO_ROOM);
		char_from_room(ch);
		char_to_room(ch,room+1,0);
		act("$n enters the cell.",TRUE,ch,0,0,TO_ROOM);

		for(k=world[ch->in_room].people;k;k=temp_ch) {
			temp_ch=k->next_in_room;
			if(IS_TRUSTED(k) || ch==k)
				continue;
			if(!IS_OUTLAW(k)) {
				if(!(LAW_ENFORCER(k)&&(k->specials.spec[2] & APPR_RETURN))) {
					sprintf(buf,"What are you doing in here, %s?", fname(GET_NAME(k)));
					do_say(ch,buf,0);
					act("$n tosses $N out of the cell.",TRUE,ch,0,k,TO_NOTVICT);
					act("$n tosses you out of the cell.",FALSE,ch,0,k,TO_VICT);
					char_from_room(k);
					char_to_room(k,room,0);
					act("$n is tossed from the cell.",TRUE,k,0,0,TO_ROOM);
					do_look(k,"",0);
				}
			} else if(!k->specials.jail_time) {
				do_action(ch,GET_NAME(k),23);
				sprintf(buf,"You are free to go, %s.",
					fname(GET_NAME(k)));
				do_say(ch,buf,0);
				act("$n lets $N out of the cell.",TRUE,ch,0,k,TO_NOTVICT);
				act("$n lets you out of the cell.",FALSE,ch,0,k,TO_VICT);
				char_from_room(k);
				char_to_room(k,room,0);
				act("$n has arrived.",FALSE,k,0,0,TO_ROOM);
				REMOVE_BIT(k->specials.act, (IS_NPC(k) ?
					NPC_OUTLAW : PLR_OUTLAW));
			} else if(k->specials.jail_time<=-1) {
				k->specials.jail_time=150; /* ought to #define*/
				act("$n points a finger at $N.",TRUE,ch,0,k,TO_NOTVICT);
				act("$n points a finger at you.",TRUE,ch,0,k,TO_VICT);
				sprintf(buf,"I hope you enjoy your stay, %s.",
					fname(GET_NAME(k)));
				do_say(ch,buf,0);
			}
		}

		if(feeding) {
			for(k=world[ch->in_room].people;k;k=k->next_in_room)
				if(!IS_TRUSTED(k) && ch!=k) {
					obj=read_object(3010,VIRTUAL);
					obj_to_char(obj,ch);
					sprintf(buf,"%s to %s",fname(obj->name),
						GET_NAME(k));
					do_give(ch,buf,0);
/*					if(obj->carried_by==ch) {
						obj_from_ch(*/
					
					obj=read_object(3100,VIRTUAL);
					obj_to_char(obj,ch);
					sprintf(buf,"%s to %s",fname(obj->name),
						GET_NAME(k));
					do_give(ch,buf,0);
				}
		}

		act("$n leaves the cell.",TRUE,ch,0,0,TO_ROOM);
		char_from_room(ch);
		char_to_room(ch,room,0);
		act("$n has arrived.",TRUE,ch,0,0,TO_ROOM);
		do_close(ch,"door",0);
		do_lock(ch,"door",0);
	}
	return(FALSE);
}