/*
 * $Id: special.c,v 1.43 1999/04/16 15:52:21 fjoe Exp $
 */

/***************************************************************************
 *     ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT		           *	
 *     ANATOLIA has been brought to you by ANATOLIA consortium		   *
 *	 Serdar BULUT {Chronos}		bulut@rorqual.cc.metu.edu.tr       *
 *	 Ibrahim Canpunar  {Mandrake}	canpunar@rorqual.cc.metu.edu.tr    *	
 *	 Murat BICER  {KIO}		mbicer@rorqual.cc.metu.edu.tr	   *	
 *	 D.Baris ACAR {Powerman}	dbacar@rorqual.cc.metu.edu.tr	   *	
 *     By using this code, you have agreed to follow the terms of the      *
 *     ANATOLIA license, in the file Anatolia/anatolia.licence             *	
 ***************************************************************************/

/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
*	ROM 2.4 is copyright 1993-1995 Russ Taylor			   *
*	ROM has been brought to you by the ROM consortium		   *
*	    Russ Taylor (rtaylor@pacinfo.com)				   *
*	    Gabrielle Taylor (gtaylor@pacinfo.com)			   *
*	    Brian Moore (rom@rom.efn.org)				   *
*	By using this code, you have agreed to follow the terms of the	   *
*	ROM license, in the file Rom24/doc/rom.license			   *
***************************************************************************/

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

/* command procedures needed */
DECLARE_DO_FUN(do_yell		);
DECLARE_DO_FUN(do_open		);
DECLARE_DO_FUN(do_close		);
DECLARE_DO_FUN(do_say		);
DECLARE_DO_FUN(do_backstab	);
DECLARE_DO_FUN(do_flee		);
DECLARE_DO_FUN(do_tell		);
DECLARE_DO_FUN(do_track		);
DECLARE_DO_FUN(do_murder	);
DECLARE_DO_FUN(do_kill		);
DECLARE_DO_FUN(do_unlock	);
DECLARE_DO_FUN(do_lock		);
DECLARE_DO_FUN(do_drop		);
DECLARE_DO_FUN(do_get		);
DECLARE_DO_FUN(do_north		);
DECLARE_DO_FUN(do_south		);
DECLARE_DO_FUN(do_east		);
DECLARE_DO_FUN(do_west		);
DECLARE_DO_FUN(do_sacrifice	);
DECLARE_DO_FUN(do_give		);
DECLARE_DO_FUN(do_rescue	);
DECLARE_DO_FUN(do_assassinate	);

/*
 * The following special functions are available for mobiles.
 */

DECLARE_SPEC_FUN(spec_breath_any	);
DECLARE_SPEC_FUN(spec_breath_acid	);
DECLARE_SPEC_FUN(spec_breath_fire	);
DECLARE_SPEC_FUN(spec_breath_frost	);
DECLARE_SPEC_FUN(spec_breath_gas	);
DECLARE_SPEC_FUN(spec_breath_lightning	);
DECLARE_SPEC_FUN(spec_cast_adept	);
DECLARE_SPEC_FUN(spec_cast_cleric	);
DECLARE_SPEC_FUN(spec_cast_judge	);
DECLARE_SPEC_FUN(spec_cast_mage		);
DECLARE_SPEC_FUN(spec_cast_beholder	);
DECLARE_SPEC_FUN(spec_cast_undead	);
DECLARE_SPEC_FUN(spec_cast_seneschal	);
DECLARE_SPEC_FUN(spec_executioner	);
DECLARE_SPEC_FUN(spec_fido		);
DECLARE_SPEC_FUN(spec_guard		);
DECLARE_SPEC_FUN(spec_janitor		);
DECLARE_SPEC_FUN(spec_mayor		);
DECLARE_SPEC_FUN(spec_poison		);
DECLARE_SPEC_FUN(spec_thief		);
DECLARE_SPEC_FUN(spec_nasty		);
DECLARE_SPEC_FUN(spec_troll_member	);
DECLARE_SPEC_FUN(spec_ogre_member	);
DECLARE_SPEC_FUN(spec_patrolman		);
DECLARE_SPEC_FUN(spec_cast_clan		);
DECLARE_SPEC_FUN(spec_special_guard	);
DECLARE_SPEC_FUN(spec_assassinater	);
DECLARE_SPEC_FUN(spec_captain		);
DECLARE_SPEC_FUN(spec_headlamia		); 

/* the function table */
const   struct  spec_type    spec_table[] =
{
	{ "spec_breath_any",		spec_breath_any		},
	{ "spec_breath_acid",		spec_breath_acid	},
	{ "spec_breath_fire",		spec_breath_fire	},
	{ "spec_breath_frost",		spec_breath_frost	},
	{ "spec_breath_gas",		spec_breath_gas		},
	{ "spec_breath_lightning",	spec_breath_lightning	},	
	{ "spec_cast_adept",		spec_cast_adept		},
	{ "spec_cast_cleric",		spec_cast_cleric	},
	{ "spec_cast_judge",		spec_cast_judge		},
	{ "spec_cast_mage",		spec_cast_mage		},
	{ "spec_cast_seneschal",	spec_cast_seneschal	},
	{ "spec_cast_beholder",		spec_cast_beholder	},
	{ "spec_cast_undead",		spec_cast_undead	},
	{ "spec_executioner",		spec_executioner	},
	{ "spec_fido",			spec_fido		},
	{ "spec_guard",			spec_guard		},
	{ "spec_janitor",		spec_janitor		},
	{ "spec_mayor",			spec_mayor		},
	{ "spec_poison",		spec_poison		},
	{ "spec_thief",			spec_thief		},
	{ "spec_nasty",			spec_nasty		},
	{ "spec_troll_member",		spec_troll_member	},
	{ "spec_ogre_member",		spec_ogre_member	},
	{ "spec_patrolman",		spec_patrolman		},
	{ "spec_cast_clan",		spec_cast_clan		},
/*	{ "spec_stalker",		spec_stalker		}, */
	{ "spec_special_guard",		spec_special_guard	},
	{ "spec_assassinater",          spec_assassinater	},  
	{ "spec_captain",		spec_captain		},
	{ "spec_headlamia",		spec_headlamia		},   
	{ NULL }
};

static bool dragon(CHAR_DATA *ch, const char *spell_name);
static void spec_cast(CHAR_DATA *ch, const char *spell_name, CHAR_DATA *victim);

/*
 * Given a name, return the appropriate spec fun.
 */
SPEC_FUN *spec_lookup(const char *name)
{
	 int i;
 
	 for (i = 0; spec_table[i].name != NULL; i++) {
	 	if (LOWER(name[0]) == LOWER(spec_table[i].name[0])
	 	&&  !str_prefix(name,spec_table[i].name))
	 		return spec_table[i].function;
	 }
 
	return 0;
}

char *spec_name(SPEC_FUN *function)
{
	int i;

	for (i = 0; spec_table[i].function != NULL; i++) {
		if (function == spec_table[i].function)
			return spec_table[i].name;
	}

	return NULL;
}

bool spec_troll_member(CHAR_DATA *ch)
{
	CHAR_DATA *vch, *victim = NULL;
	int count = 0;
	char *message;

	if (!IS_AWAKE(ch) || IS_AFFECTED(ch,AFF_CALM) || ch->in_room == NULL 
	||  IS_AFFECTED(ch,AFF_CHARM) || ch->fighting != NULL)
		return FALSE;

	/* find an ogre to beat up */
	for (vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room) {
		if (!IS_NPC(vch) || ch == vch)
			continue;

		if (vch->pIndexData->vnum == MOB_VNUM_PATROLMAN)
			return FALSE;

		if (vch->pIndexData->group == GROUP_VNUM_OGRES
		&&  ch->level > vch->level - 2 && !is_safe(ch,vch)) {
			if (number_range(0,count) == 0)
				victim = vch;

			count++;
		}
	}

	if (victim == NULL)
		return FALSE;

	/* say something, then raise hell */
	switch (number_range(0, 6)) {
	default:message = NULL;
		break;
	case 0:	message = "$n yells 'I've been looking for you, punk!'";
		break;
	case 1: message = "With a scream of rage, $n attacks $N.";
		break;
	case 2: message = 
		"$n says 'What's slimy Ogre trash like you doing around here?'";
		break;
	case 3: message = "$n cracks his knuckles and says 'Do ya feel lucky?'";
		break;
	case 4: message = "$n says 'There's no cops to save you this time!'";
		break;	
	case 5: message = "$n says 'Time to join your brother, spud.'";
		break;
	case 6: message = "$n says 'Let's rock.'";
		break;
	}

	if (message)
		act(message,ch,NULL,victim,TO_ALL);
	multi_hit(ch, victim, TYPE_UNDEFINED);
	return TRUE;
}

bool spec_ogre_member(CHAR_DATA *ch)
{
	CHAR_DATA *vch, *victim = NULL;
	int count = 0;
	char *message;
 
	if (!IS_AWAKE(ch) || IS_AFFECTED(ch,AFF_CALM) || ch->in_room == NULL
	||  IS_AFFECTED(ch,AFF_CHARM) || ch->fighting != NULL)
	    return FALSE;

	/* find an troll to beat up */
	for (vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room) {
		if (!IS_NPC(vch) || ch == vch)
			continue;
 
		if (vch->pIndexData->vnum == MOB_VNUM_PATROLMAN)
			return FALSE;
 
		if (vch->pIndexData->group == GROUP_VNUM_TROLLS
		&&  ch->level > vch->level - 2 && !is_safe(ch,vch)) {
			if (number_range(0,count) == 0)
				victim = vch;
 
			count++;
		}
	}
 
	if (victim == NULL)
		return FALSE;
 
	/* say something, then raise hell */
	switch (number_range(0, 6)) {
	default:message = NULL;
		break;
	case 0: message = "$n yells 'I've been looking for you, punk!'";
		break;
	case 1: message = "With a scream of rage, $n attacks $N.'";
		break;
	case 2: message =
		"$n says 'What's Troll filth like you doing around here?'";
		break;
	case 3: message = "$n cracks his knuckles and says 'Do ya feel lucky?'";
		break;
	case 4: message = "$n says 'There's no cops to save you this time!'";
		break;
	case 5: message = "$n says 'Time to join your brother, spud.'";
		break;
	case 6: message = "$n says 'Let's rock.'";
		break;
	}
 
	if (message)
		act(message, ch, NULL, victim, TO_ALL);
	multi_hit(ch, victim, TYPE_UNDEFINED);
	return TRUE;
}

bool spec_patrolman(CHAR_DATA *ch)
{
	CHAR_DATA *vch,*victim = NULL;
	OBJ_DATA *obj;
	char *message;
	int count = 0;

	if (!IS_AWAKE(ch) || IS_AFFECTED(ch,AFF_CALM) || ch->in_room == NULL
	||  IS_AFFECTED(ch,AFF_CHARM) || ch->fighting != NULL)
		return FALSE;

	/* look for a fight in the room */
	for (vch = ch->in_room->people; vch != NULL; vch = vch->next_in_room) {
		if (vch == ch)
			continue;

		if (vch->fighting != NULL) { /* break it up! */
			if (number_range(0,count) == 0)
				victim = (vch->level > vch->fighting->level) ?
					 vch : vch->fighting;
			count++;
		}
	}

	if (victim == NULL
	||  (IS_NPC(victim) && victim->spec_fun == ch->spec_fun))
		return FALSE;

	if (((obj = get_eq_char(ch,WEAR_NECK_1)) != NULL &&
	     obj->pIndexData->vnum == OBJ_VNUM_WHISTLE)
	||  ((obj = get_eq_char(ch,WEAR_NECK_2)) != NULL &&
	     obj->pIndexData->vnum == OBJ_VNUM_WHISTLE)) {
		act("You blow down hard on $p.", ch, obj, NULL, TO_CHAR);
		act("$n blows on $p, ***WHEEEEEEEEEEEET***",
		    ch, obj, NULL, TO_ROOM);

		for (vch = char_list; vch; vch = vch->next) {
	        	if (!vch->in_room)
				continue;

			if (vch->in_room != ch->in_room 
			&&  vch->in_room->area == ch->in_room->area)
				char_puts("You hear a shrill whistling sound.\n", vch);
		}
	}

	switch (number_range(0,6)) {
	default:message = NULL;
		break;
	case 0:	message = "$n yells 'All roit! All roit! break it up!'";
		break;
	case 1: message = 
		"$n says 'Society's to blame, but what's a bloke to do?'";
		break;
	case 2: message = 
		"$n mumbles 'bloody kids will be the death of us all.'";
		break;
	case 3: message = "$n shouts 'Stop that! Stop that!' and attacks.";
		break;
	case 4: message = "$n pulls out his billy and goes to work.";
		break;
	case 5: message = 
		"$n sighs in resignation and proceeds to break up the fight.";
		break;
	case 6: message = "$n says 'Settle down, you hooligans!'";
		break;
	}

	if (message)
		act(message, ch, NULL, NULL, TO_ALL);

	multi_hit(ch, victim, TYPE_UNDEFINED);
	return TRUE;
}
	
/*
 * Special procedures for mobiles.
 */
bool spec_breath_any(CHAR_DATA *ch)
{
	if (ch->position != POS_FIGHTING)
		return FALSE;

	switch (number_bits(3)) {
	case 0: return spec_breath_fire		(ch);
	case 1:
	case 2: return spec_breath_lightning	(ch);
	case 3: return spec_breath_gas		(ch);
	case 4: return spec_breath_acid		(ch);
	case 5:
	case 6:
	case 7: return spec_breath_frost	(ch);
	}

	return FALSE;
}

bool spec_breath_acid(CHAR_DATA *ch)
{
	return dragon(ch, "acid breath");
}

bool spec_breath_fire(CHAR_DATA *ch)
{
	return dragon(ch, "fire breath");
}

bool spec_breath_frost(CHAR_DATA *ch)
{
	return dragon(ch, "frost breath");
}

bool spec_breath_gas(CHAR_DATA *ch)
{
	return dragon(ch, "gas breath");
}

bool spec_breath_lightning(CHAR_DATA *ch)
{
	return dragon(ch, "lightning breath");
}

bool spec_cast_adept(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;

	if (!IS_AWAKE(ch))
		return FALSE;

	for (victim = ch->in_room->people; victim != NULL; victim = v_next) {
		v_next = victim->next_in_room;
		if (victim != ch
		&&  can_see(ch, victim)
		&&  number_bits(1) == 0 
		&&  !IS_NPC(victim)
		&&  victim->level < 10)
			break;
	}

	if (victim == NULL)
		return FALSE;

	switch (number_bits(4)) {
	case 0:
		act("$n utters the words, '$t'.", ch, "abrazak", NULL, TO_ROOM);
		spell_armor(sn_lookup("armor"), ch->level, ch, victim,
			    TARGET_CHAR);
		return TRUE;

	case 1:
		act("$n utters the words, '$t'.", ch, "fido", NULL, TO_ROOM);
		spell_bless(sn_lookup("bless"), ch->level, ch, victim,
			    TARGET_CHAR);
		return TRUE;

	case 2:
		act("$n utters the words, '$t'.",
		    ch, "judicandus noselacri", NULL, TO_ROOM);
		spell_cure_blindness(sn_lookup("cure blindness"),
				     ch->level, ch, victim, TARGET_CHAR);
		return TRUE;

	case 3:
		act("$n utters the words, '$t'.",
		    ch, "judicandus dies", NULL, TO_ROOM);
		spell_cure_light(sn_lookup("cure light"),
				 ch->level, ch, victim,TARGET_CHAR);
		return TRUE;

	case 4:
		act("$n utters the words, '$t'.",
		    ch, "judicandus sausabru", NULL, TO_ROOM);
		spell_cure_poison(sn_lookup("cure poison"),
				  ch->level, ch, victim, TARGET_CHAR);
	return TRUE;

	case 5:
		act("$n utters the words, '$t'.",
		    ch, "candusima", NULL, TO_ROOM);
		spell_refresh(sn_lookup("refresh"),
			      ch->level, ch, victim, TARGET_CHAR);
		return TRUE;

	case 6:
		act("$n utters the words, '$t'.",
		    ch, "judicandus eugzagz", NULL, TO_ROOM);
		spell_cure_disease(sn_lookup("cure disease"),
				   ch->level, ch, victim, TARGET_CHAR);
	}

	return FALSE;
}

bool spec_cast_cleric(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	char *spell;

	if (ch->position != POS_FIGHTING)
		return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;
		if (victim->fighting == ch && number_bits(2) == 0)
			break;
	}

	if (victim == NULL)
		return FALSE;

	for (;;) {
		int min_level;

		switch (number_bits(4)) {
		case  0: min_level =  0; spell = "blindness";      break;
		case  1: min_level =  3; spell = "cause serious";  break;
		case  2: min_level =  7; spell = "earthquake";     break;
		case  3: min_level =  9; spell = "cause critical"; break;
		case  4: min_level = 10; spell = "dispel evil";    break;
		case  5: min_level = 12; spell = "curse";          break;
		case  6: min_level = 12; spell = "change sex";     break;
		case  7: min_level = 13; spell = "flamestrike";    break;
		case  8:
		case  9:
		case 10: min_level = 15; spell = "harm";           break;
		case 11: min_level = 15; spell = "plague";	   break;
		default: min_level = 16; spell = "dispel magic";   break;
		}

		if (ch->level >= min_level)
			break;
	}

	spec_cast(ch, spell, victim);
	return TRUE;
}

bool spec_cast_judge(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
 
	if (ch->position != POS_FIGHTING)
		return FALSE;
 
	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;
		if (victim->fighting == ch && number_bits(2) == 0)
			break;
	}
 
	if (victim == NULL)
		return FALSE;
 
	spec_cast(ch, "high explosive", victim);
	return TRUE;
}

bool spec_cast_mage(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	char *spell;

	if (ch->position != POS_FIGHTING)
		return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;
		if (victim->fighting == ch && number_bits(2) == 0)
			break;
	}

	if (victim == NULL)
		return FALSE;

	for (;;) {
		int min_level;

		switch (number_bits(4)) {
		case  0: min_level =  0; spell = "blindness";      break;
		case  1: min_level =  3; spell = "chill touch";    break;
		case  2: min_level =  7; spell = "weaken";         break;
		case  3: min_level =  8; spell = "teleport";       break;
		case  4: min_level = 11; spell = "colour spray";   break;
		case  5: min_level = 12; spell = "change sex";     break;
		case  6: min_level = 13; spell = "energy drain";   break;
		case  7:
		case  8:
		case  9: min_level = 15; spell = "fireball";       break;
		case 10: min_level = 20; spell = "plague";	   break;
		default: min_level = 20; spell = "acid blast";     break;
		}

		if (ch->level >= min_level)
			break;
	}

	spec_cast(ch, spell, victim);
	return TRUE;
}

bool spec_cast_seneschal(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	char *spell;

	if (ch->position != POS_FIGHTING)
		return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;
		if (victim->fighting == ch && number_bits(1) == 0)
			break;
	}

	if (victim == NULL)
		return FALSE;

	switch (dice(1, 16)) {
	case  0: spell = "blindness";      break;
	case  1: spell = "dispel magic";    break;
	case  2: spell = "weaken";         break;
	case  3: spell = "blindness";      break;
	case  4: spell = "acid arrow";   break;
	case  5: spell = "fireball";     break;
	case  6: spell = "energy drain";   break;
	case  7:
	case  8:
	case  9: spell = "acid blast";       break;
	case 10: spell = "plague";	   break;
	case 11: spell = "acid blast";         break;
	case 12:  
	case 13: spell = "lightning breath";  break;
	case 14:
	case 15: spell = "acid blast";	break;
	default: return FALSE;
	}

	spec_cast(ch, spell, victim);
	return TRUE;
}

bool spec_cast_undead(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	char *spell;

	if (ch->position != POS_FIGHTING)
		return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;
		if (victim->fighting == ch && number_bits(2) == 0)
			break;
	}

	if (victim == NULL)
		return FALSE;

	for (;;) {
		int min_level;

		switch (number_bits(4)) {
		case  0: min_level =  0; spell = "curse";          break;
		case  1: min_level =  3; spell = "weaken";         break;
		case  2: min_level =  6; spell = "chill touch";    break;
		case  3: min_level =  9; spell = "blindness";      break;
		case  4: min_level = 12; spell = "poison";         break;
		case  5: min_level = 15; spell = "energy drain";   break;
		case  6: min_level = 18; spell = "harm";           break;
		case  7: min_level = 21; spell = "teleport";       break;
		case  8: min_level = 20; spell = "plague";	   break;
		default: min_level = 18; spell = "harm";           break;
		}

		if (ch->level >= min_level)
			break;
	}

	spec_cast(ch, spell, victim);
	return TRUE;
}

bool spec_executioner(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	char *crime;

	if (!IS_AWAKE(ch) || ch->fighting != NULL)
		return FALSE;

	crime = str_empty;
	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;

		if (!IS_NPC(victim) && IS_SET(victim->plr_flags, PLR_WANTED) 
		&&  can_see(ch, victim)) {
			crime = "CRIMINAL";
			break;
		}
	}

	if (victim == NULL)
		return FALSE;

	REMOVE_BIT(ch->comm, COMM_NOSHOUT);
	doprintf(do_yell, ch,
		 "%s is a %s!  PROTECT THE INNOCENT!  MORE BLOOOOD!!!",
		 victim->name, crime);
	multi_hit(ch, victim, TYPE_UNDEFINED);
	return TRUE;
}

bool spec_fido(CHAR_DATA *ch)
{
	OBJ_DATA *corpse;
	OBJ_DATA *c_next;
	OBJ_DATA *obj;
	OBJ_DATA *obj_next;

	if (!IS_AWAKE(ch))
		return FALSE;

	for (corpse = ch->in_room->contents; corpse; corpse = c_next) {
		c_next = corpse->next_content;
		if (corpse->pIndexData->item_type != ITEM_CORPSE_NPC)
			continue;

		act("$n savagely devours a corpse.", ch, NULL, NULL, TO_ROOM);
		for (obj = corpse->contains; obj; obj = obj_next) {
			obj_next = obj->next_content;
			obj_from_obj(obj);
			obj_to_room(obj, ch->in_room);
		}
		extract_obj(corpse, 0);
		return TRUE;
	}

	return FALSE;
}

bool spec_janitor(CHAR_DATA *ch)
{
	OBJ_DATA *trash;
	OBJ_DATA *trash_next;

	if (!IS_AWAKE(ch))
	return FALSE;

	for (trash = ch->in_room->contents; trash != NULL; trash = trash_next) {
		trash_next = trash->next_content;
		if (!IS_SET(trash->wear_flags, ITEM_TAKE)
		||  !can_loot(ch, trash))
			continue;
		if (trash->pIndexData->item_type == ITEM_DRINK_CON
		||  trash->pIndexData->item_type == ITEM_TRASH
		||  trash->cost < 10) {
			act("$n picks up some trash.", ch, NULL, NULL, TO_ROOM);
			obj_from_room(trash);
			obj_to_char(trash, ch);
			return TRUE;
		}
	}

	return FALSE;
}

bool spec_mayor(CHAR_DATA *ch)
{
	static const char open_path[] =
		"W3a3003b000c000d111Oe333333Oe22c222112212111a1S.";

	static const char close_path[] =
		"W3a3003b000c000d111CE333333CE22c222112212111a1S.";

	static const char *path;
	static int pos;
	static bool move;
	OBJ_DATA *key;

	if (!move) {
		if (time_info.hour ==  6) {
			path = open_path;
			move = TRUE;
			pos  = 0;
		}

		if (time_info.hour == 20) {
			path = close_path;
			move = TRUE;
			pos  = 0;
		}
	}

	if (!move || ch->position < POS_SLEEPING)
		return FALSE;

	switch (path[pos]) {
	case '0':
	case '1':
	case '2':
	case '3':
		move_char(ch, path[pos] - '0', FALSE);
		break;

	case 'W':
		ch->position = POS_STANDING;
		act("$n awakens and groans loudly.", ch, NULL, NULL, TO_ROOM);
		break;

	case 'S':
		ch->position = POS_SLEEPING;
		act("$n lies down and falls asleep.", ch, NULL, NULL, TO_ROOM);
		break;

	case 'a':
		do_say(ch, "Hello Honey!");
		break;

	case 'b':
		do_say(ch, "What a view!  I must do something about that dump!");
		break;

	case 'c':
		do_say(ch,"Vandals!  Youngsters have no respect for anything!");
		break;

	case 'd':
		do_say(ch, "Good day, citizens!");
		break;

	case 'e':
		do_say(ch,"I hereby declare the city of Midgaard open!");
		break;

	case 'E':
		do_say(ch,"I hereby declare the city of Midgaard closed!");
		break;

	case 'O':
		do_unlock(ch, "gate");
		do_open(ch, "gate");
		interpret(ch, "emote unlocks the gate key from the gate.");
		for(key = ch->in_room->contents; key; key = key->next_content)
			if (key->pIndexData->vnum == 3379)
				break;
			if (key)
				SET_BIT(key->wear_flags, ITEM_TAKE);
			do_get(ch, "gatekey");
		break;

	case 'C':
		do_close(ch, "gate");
		do_lock(ch, "gate");
		do_drop(ch, "key");
		interpret(ch, "emote locks the gate key to the gate, with chain.");
		for(key = ch->in_room->contents; key; key = key->next_content)
			if (key->pIndexData->vnum == 3379)
				break;
		if (key)
			REMOVE_BIT(key->wear_flags, ITEM_TAKE);
		break;

	case '.' :
		move = FALSE;
		break;
	}

	pos++;
	return FALSE;
}

bool spec_poison(CHAR_DATA *ch)
{
	CHAR_DATA *victim;

	if (ch->position != POS_FIGHTING
	||  (victim = ch->fighting) == NULL
	||  number_percent() > 2 * ch->level)
		return FALSE;

	act_puts("You bite $N!", ch, NULL, victim, TO_CHAR, POS_DEAD);
	act("$n bites $N!", ch, NULL, victim, TO_NOTVICT);
	act_puts("$n bites you!", ch, NULL, victim, TO_VICT, POS_DEAD);
	spell_poison(gsn_poison, ch->level, ch, victim,TARGET_CHAR);
	return TRUE;
}

bool spec_thief(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	long gold,silver;

	if (ch->position != POS_STANDING)
		return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;

		if (IS_NPC(victim)
		||  victim->level >= LEVEL_IMMORTAL
		||  number_bits(5) != 0 
		||  !can_see(ch,victim))
			continue;

		if (IS_AWAKE(victim) && number_range(0, ch->level) == 0) {
			act("You discover $n's hands in your wallet!",
			    ch, NULL, victim, TO_VICT);
			act("$N discovers $n's hands in $S wallet!",
			    ch, NULL, victim, TO_NOTVICT);
			return TRUE;
		}
		else {
			gold = victim->gold * UMIN(number_range(1, 20),
						   ch->level / 2) / 100;
			gold = UMIN(gold, ch->level * ch->level * 10);
			ch->gold     += gold;
			victim->gold -= gold;
			silver = victim->silver * UMIN(number_range(1, 20),
						       ch->level / 2) / 100;
			silver = UMIN(silver, ch->level * ch->level * 25);
			ch->silver     += silver;
			victim->silver -= silver;
			return TRUE;
		}
	}

	return FALSE;
}

bool spec_cast_clan(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;

	if (!IS_AWAKE(ch))
	return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;
		if (victim != ch && can_see(ch, victim) && number_bits(1) == 0)
			break;
	}

	if (victim == NULL)
		return FALSE;

	switch (number_bits(4)) {
	case 0:
		act("$n utters the words, '$t'.", ch, "abracal", NULL, TO_ROOM);
		spell_armor(sn_lookup("armor"),
			    ch->level, ch, victim, TARGET_CHAR);
		return TRUE;

	case 1:
		act("$n utters the words, '$t'.", ch, "balc", NULL, TO_ROOM);
		spell_bless(sn_lookup("bless"),
			    ch->level, ch, victim, TARGET_CHAR);
		return TRUE;

	case 2:
		act("$n utters the words, '$t'.",
		    ch, "judicandus noselacba", NULL, TO_ROOM);
		spell_cure_blindness(sn_lookup("cure blindness"),
				     ch->level, ch, victim, TARGET_CHAR);
		return TRUE;

	case 3:
		act("$n utters the words, '$t'.",
		    ch, "judicandus bacla", NULL, TO_ROOM);
		spell_cure_light(sn_lookup("cure light"),
				 ch->level, ch, victim, TARGET_CHAR);
		return TRUE;

	case 4:
		act("$n utters the words, '$t'.",
		    ch, "judicandus sausabcla", NULL, TO_ROOM);
		spell_cure_poison(sn_lookup("cure poison"),
				  ch->level, ch, victim, TARGET_CHAR);
		return TRUE;

	case 5:
		act("$n utters the words, '$t'.",
		    ch, "candabala", NULL, TO_ROOM);
		spell_refresh(sn_lookup("refresh"),
			      ch->level, ch, victim, TARGET_CHAR);
		return TRUE;
	}

	return FALSE;
}

bool spec_guard(CHAR_DATA *ch)
{
	CHAR_DATA *victim, *v_next;
	CHAR_DATA *ech;
	char *crime;
	int max_evil; 
 
	if (!IS_AWAKE(ch) || ch->fighting != NULL)
		return FALSE;
	
	max_evil = 300; 
	ech      = NULL;
	crime    = str_empty;
	
	for (victim = ch->in_room->people; victim != NULL; victim = v_next) {
		v_next = victim->next_in_room;
	
		if (number_percent() < 2 && !IS_IMMORTAL(victim)) {
			do_say(ch, "Do I know you?");
 			if (str_cmp(ch->in_room->area->name,
				    hometown_name(victim->hometown)))
				do_say(ch, "I don't remember you. Go away!");
			else {
				do_say(ch, "Ok, my dear! I have just remembered.");
				interpret(ch, "smile");
			}
		}

		if (!IS_NPC(victim) && IS_SET(victim->plr_flags, PLR_WANTED)) {
			crime = "CRIMINAL";
			break;
		}
	
		if (victim->fighting != NULL
		&&  victim->fighting != ch
		&&  victim->alignment < max_evil) {
			if (IS_EVIL(victim)) {
				max_evil = -350; 
				ech      = victim;
			}
			else
				ech = victim;
		}
	}
	
	if (victim) {
		doprintf(do_yell, ch,
			 "%s is a %s!  PROTECT THE INNOCENT!!  BANZAI!!",
			 victim->name, crime);
		multi_hit(ch, victim, TYPE_UNDEFINED);
		return TRUE;
	}
	
	if (ech) {
		act("$n screams 'PROTECT THE INNOCENT!!  BANZAI!!",
		    ch, NULL, NULL, TO_ROOM);
		multi_hit(ch, ech, TYPE_UNDEFINED);
		return TRUE;
	}
	
	return FALSE;
}

bool spec_special_guard(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	char *crime;

	if (!IS_AWAKE(ch) || ch->fighting != NULL)
		return FALSE;

	crime    = str_empty;

	for (victim = ch->in_room->people; victim != NULL; victim = v_next) {
		v_next = victim->next_in_room;
		
		if (!IS_NPC(victim) && IS_SET(victim->plr_flags, PLR_WANTED)) {
			crime = "CRIMINAL";
			break;
		}
	}

	if (victim) {
		doprintf(do_yell, ch,
			 "%s is a %s!  PROTECT THE INNOCENT!!  BANZAI!!",
			 victim->name, crime);
		multi_hit(ch, victim, TYPE_UNDEFINED);
		return TRUE;
	}

	return FALSE;
}

bool spec_nasty(CHAR_DATA *ch)
{
	CHAR_DATA *victim, *v_next;
	long gold;
 
	if (!IS_AWAKE(ch))
		return FALSE;
 
	if (ch->position != POS_FIGHTING) {
		for (victim = ch->in_room->people; victim; victim = v_next) {
			v_next = victim->next_in_room;
			if (!IS_NPC(victim)
			&&  (victim->level > ch->level)
			&&  (victim->level < ch->level + 10)) {
				do_backstab(ch, victim->name);
				if (ch->position != POS_FIGHTING)
					do_murder(ch, victim->name);
				/* should steal some coins right away? :) */
				return TRUE;
			}
		}
		return FALSE;    /*  No one to attack */
	}
 
	/* okay, we must be fighting.... steal some coins and flee */
	if ((victim = ch->fighting) == NULL)
		return FALSE;   /* let's be paranoid.... */
 
	switch (number_bits(2)) {
	case 0:
		act("$n rips apart your coin purse, spilling your gold!",
	            ch, NULL, victim, TO_VICT);
		act("You slash apart $N's coin purse and gather his gold.",
		    ch, NULL, victim, TO_CHAR);
		act("$N's coin purse is ripped apart!",
		    ch, NULL, victim, TO_NOTVICT);
		gold = victim->gold / 10;  /* steal 10% of his gold */
		victim->gold -= gold;
		ch->gold     += gold;
		return TRUE;
 
	case 1:
		do_flee(ch, str_empty);
		return TRUE;
 
	default:
		return FALSE;
	}
}
	
bool spec_assassinater(CHAR_DATA *ch)
{
	char* msg;
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	int rnd_say;

	if (ch->fighting != NULL)
		return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		class_t *vcl;

		v_next = ch->next_in_room;

		if (IS_NPC(ch)
		||  (vcl = class_lookup(victim->class)) == NULL
		||  !str_cmp(vcl->name, "thief")
		||  !str_cmp(vcl->name, "ninja"))
			continue;
	}

	if (victim == NULL || victim == ch || IS_IMMORTAL(victim))
		return FALSE;
	if (victim->level > ch->level + 7 || IS_NPC(victim))
		return FALSE;
	if (victim->hit < victim->max_hit) 
		return FALSE;

	rnd_say = number_range(1, 40);

	switch (rnd_say) {
		case  5:
		msg = "Death to is the true end...";
		break;

	case  6:
		msg = "Time to die....";
		break;

	case  7:
		msg = "Cabrone...."; 
		break;

	case  8:
		msg = "Welcome to your fate....";
		break;

	case  9:
		msg = "A sacrifice to immortals.. ";
		break;

	case 10:
		msg = "Ever dance with the devil...."; 
		break;

	default:
		return FALSE;
	}

	do_say(ch, msg);
	multi_hit(ch, victim, gsn_assassinate);
	return TRUE;
}

bool spec_captain(CHAR_DATA *ch)
{
	static const char open_path[] =
"Wn0onc0oe1f2212211s2tw3xw3xd3322a22b22yO00d00a0011e1fe1fn0o3300300w3xs2ts2tS.";
 
	static const char close_path[] =
"Wn0on0oe1f2212211s2twc3xw3x3322d22a22EC0a00d0b0011e1fe1fn0o3300300w3xs2ts2tS.";
 
	static const char *path;
	static int pos;
	static bool move;
 
	if (!move) {
		if (time_info.hour ==  6) {
			path = open_path;
			move = TRUE;
			pos  = 0;
		}
 
		if (time_info.hour == 20) {
			path = close_path;
			move = TRUE;
			pos  = 0;
		}
	}
 
	if (ch->fighting != NULL)
		return spec_cast_cleric(ch);
 
	if (!move || ch->position < POS_SLEEPING)
		return FALSE;
 
	switch (path[pos]) {
	case '0':
	case '1':
	case '2':
	case '3':
		move_char(ch, path[pos] - '0' ,FALSE);
		break;
 
	case 'W':
		ch->position = POS_STANDING;
		act_puts("$n awakens suddenly and yawns.", ch, NULL, NULL, TO_ROOM,
			 POS_RESTING);
		break;
 
	case 'S':
		ch->position = POS_SLEEPING;
		act_puts("$n lies down and falls asleep.", ch, NULL, NULL, TO_ROOM,
			 POS_RESTING);
	break;
 
	case 'a':
		do_say(ch, "Greetings! Good Hunting to you!");
		break;
 
	case 'b':
		do_say(ch, "Keep the streets clean please. Keep Solace tidy.");
		break;

	case 'c':
		do_say(ch, "I must do something about all these doors.");
		do_say(ch, "I will never get out of here.");
		break;
 
	case 'd':
		do_say(ch, "Salutations Citizens of Solace!");
		break;
 
	case 'y':
		do_say(ch, "I hereby declare the city of Solace open!");
		break;
 
	case 'E':
		do_say(ch, "I hereby declare the city of Solace closed!");
		break;
 
	case 'O':
		do_unlock(ch, "gate");
		do_open(ch, "gate");
		break;
 
	case 'C':
		do_close(ch, "gate");
		do_lock(ch, "gate");
		break;
 
	case 'n':
		do_open(ch, "north");
		break;

	case 'o':
		do_close(ch, "south");
		break;
 
	case 's':
		do_open(ch, "south");
		break;

	case 't':
		do_close(ch, "north");
		break;
 
	case 'e':
		do_open(ch, "east");
		break;
 
	case 'f':
		do_close(ch, "west");
		break;

	case 'w':
		do_open(ch, "west");
		break;

	case 'x':
		do_close(ch, "east");
		break;
 
	case '.' :
		move = FALSE;
		break;
	}
 
	pos++;
	return FALSE;
}

bool spec_headlamia(CHAR_DATA *ch)
{
	static const char path[] = "T111111100003332222232211.";
	static int pos = 0;
	static bool move;
	static int count = 0;
	CHAR_DATA *vch;

	if (!move && count++ == 10000)
		move = 1;

	if (ch->position < POS_SLEEPING || ch->fighting)
		return FALSE;
	
	for (vch = ch->in_room->people; vch; vch = vch->next_in_room) {
		if (IS_NPC(vch) && vch->pIndexData->vnum == 3143) {
			do_kill(ch, vch->name);
			break;
		}
	}

	if (!move)
		return FALSE;

	switch (path[pos]) {
		case '0':
		case '1':
		case '2':
		case '3':
		move_char(ch, path[pos] - '0', FALSE);
		pos++;
		break;

	case 'T':
		pos++;
		for(vch = npc_list; vch; vch = vch->next) {
			if (vch->pIndexData->vnum == 5201) {
				if (!vch->fighting && !vch->last_fought) {
					char_from_room(vch);
					vch->master = ch;
					vch->leader = ch;
					char_to_room(vch, ch->in_room);
				}
			}
		}
		break;

	case '.' :
		move = FALSE;
		count = 0;
		pos = 0;
		break;
	}

	return FALSE;
}

bool spec_cast_beholder(CHAR_DATA *ch)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;
	char *spell;

	if (ch->position != POS_FIGHTING)
		return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;
		if (victim->fighting == ch && number_bits(1) == 0)
			break;
	}

	if (victim == NULL)
		return FALSE;

	switch (dice(1, 16)) {
	case  0: spell = "fear";		break; /* charm person */
	case  1: spell = "slow";		break; /* charm monster */
	case  2: spell = "cause serious";	break; /* sleep */
	case  3: spell = "cause critical";	break; /* disinteg */
	case  4: spell = "harm";		break; 
	case  5: spell = "dispel magic";	break; 
	default: return FALSE;
	}

	spec_cast(ch, spell, victim);
	return TRUE;
}

/*----------------------------------------------------------------------------
 * local functions
 */

/*
 * Core procedure for dragons.
 */
static bool dragon(CHAR_DATA *ch, const char *spell_name)
{
	CHAR_DATA *victim;
	CHAR_DATA *v_next;

	if (ch->position != POS_FIGHTING)
		return FALSE;

	for (victim = ch->in_room->people; victim; victim = v_next) {
		v_next = victim->next_in_room;
		if (((RIDDEN(ch) && RIDDEN(ch)->fighting == victim) ||
		     victim->fighting == ch)
		&&  number_bits(3) == 0)
			break;
	}

	if (victim == NULL)
		return FALSE;

	spec_cast(ch, spell_name, victim);
	return TRUE;
}

static void spec_cast(CHAR_DATA *ch, const char *spell_name, CHAR_DATA *victim)
{
	int num = 1;
	CHAR_DATA *vch;
	char name[MAX_STRING_LENGTH];
	int sn;
	flag32_t target;

	if ((sn = sn_lookup(spell_name)) < 0)
		return;
	target = SKILL(sn)->target;

	if (ch->fighting == victim
	&&  (target == TAR_CHAR_OFFENSIVE || target == TAR_OBJ_CHAR_OFF)) {
		doprintf(interpret, ch, "cast '%s'", spell_name);
		return;
	}

	one_argument(victim->name, name, sizeof(name));
	for (vch = ch->in_room->people; vch; vch = vch->next_in_room) {
		if (vch == victim)
			break;
		if (can_see(ch, vch) && is_name(name, vch->name))
			num++;
	}

	if (vch == NULL)
		return;		/* can't happen */

	doprintf(interpret, ch, "cast '%s' %d.'%s'", spell_name, num, name);
}

