cm3/
cm3/clans/
cm3/mudprogs/
cm3/player/a/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 *			   "Special procedure" module			    *
 ****************************************************************************/

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


/* from makeobjs.c */
extern OBJ_DATA *make_ore (int number);

/* from elkandu.c */
extern void ch_harm(CHAR_DATA *victim, CHAR_DATA *ch, int dam, int type, bool dont_wait);

/*
 * The following special functions are available for mobiles.
 */
DECLARE_SPEC_FUN(	spec_cast_fire		);
DECLARE_SPEC_FUN(       spec_cast_wind          );
DECLARE_SPEC_FUN(       spec_cast_water         );
DECLARE_SPEC_FUN(       spec_cast_earth         );
DECLARE_SPEC_FUN(       spec_cast_drain         );
DECLARE_SPEC_FUN(       spec_cast_mind          );
DECLARE_SPEC_FUN(       spec_cast_lightning     );
DECLARE_SPEC_FUN(       spec_cast_frost         );
DECLARE_SPEC_FUN(       spec_cast_poison        );
DECLARE_SPEC_FUN(       spec_cast_light         );
DECLARE_SPEC_FUN(       spec_cast_healing       );
DECLARE_SPEC_FUN(       spec_cast_all		);
DECLARE_SPEC_FUN(	spec_fido		);
DECLARE_SPEC_FUN(	spec_guard		);
DECLARE_SPEC_FUN(	spec_janitor		);
DECLARE_SPEC_FUN(	spec_poison		);
DECLARE_SPEC_FUN(	spec_thief		);
DECLARE_SPEC_FUN(	spec_catalyst		);
DECLARE_SPEC_FUN(	spec_miner		);
DECLARE_SPEC_FUN(	spec_farmer		);



/*
 * Given a name, return the appropriate spec fun.
 */
SPEC_FUN *spec_lookup( const char *name )
{
    if ( !str_cmp( name, "spec_cast_healing"	  ) ) return spec_cast_healing;
    if ( !str_cmp( name, "spec_cast_fire"	  ) ) return spec_cast_fire;
    if ( !str_cmp( name, "spec_cast_wind"         ) ) return spec_cast_wind;
    if ( !str_cmp( name, "spec_cast_frost"        ) ) return spec_cast_frost;
    if ( !str_cmp( name, "spec_cast_lightning"    ) ) return spec_cast_lightning;
    if ( !str_cmp( name, "spec_cast_water"        ) ) return spec_cast_water;
    if ( !str_cmp( name, "spec_cast_earth"        ) ) return spec_cast_earth;
    if ( !str_cmp( name, "spec_cast_drain"        ) ) return spec_cast_drain;
    if ( !str_cmp( name, "spec_cast_mind"         ) ) return spec_cast_mind;
    if ( !str_cmp( name, "spec_cast_poison"       ) ) return spec_cast_poison;
    if ( !str_cmp( name, "spec_cast_light"        ) ) return spec_cast_light;
    if ( !str_cmp( name, "spec_cast_all"          ) ) return spec_cast_all;
    if ( !str_cmp( name, "spec_fido"		  ) ) return spec_fido;
    if ( !str_cmp( name, "spec_guard"		  ) ) return spec_guard;
    if ( !str_cmp( name, "spec_janitor"		  ) ) return spec_janitor;
    if ( !str_cmp( name, "spec_poison"		  ) ) return spec_poison;
    if ( !str_cmp( name, "spec_thief"		  ) ) return spec_thief;
    if ( !str_cmp( name, "spec_catalyst"	  ) ) return spec_catalyst;
    if ( !str_cmp( name, "spec_miner"		  ) ) return spec_miner;
    if ( !str_cmp( name, "spec_farmer"		  ) ) return spec_farmer;
    return 0;
}

/*
 * Given a pointer, return the appropriate spec fun text.
 */
char *lookup_spec( SPEC_FUN *special )
{
    if ( special == spec_cast_healing	)	return "spec_cast_healing";
    if ( special == spec_cast_fire	)	return "spec_cast_fire";
    if ( special == spec_cast_wind      )       return "spec_cast_wind";
    if ( special == spec_cast_water     )       return "spec_cast_water";
    if ( special == spec_cast_frost     )       return "spec_cast_frost";
    if ( special == spec_cast_lightning )       return "spec_cast_lightning";
    if ( special == spec_cast_mind      )       return "spec_cast_mind";
    if ( special == spec_cast_poison    )       return "spec_cast_poison";
    if ( special == spec_cast_drain     )       return "spec_cast_drain";
    if ( special == spec_cast_earth     )       return "spec_cast_earth";
    if ( special == spec_cast_light     )       return "spec_cast_light";
    if ( special == spec_cast_all       )       return "spec_cast_all";
    if ( special == spec_fido		)	return "spec_fido";
    if ( special == spec_guard		)	return "spec_guard";
    if ( special == spec_janitor	)	return "spec_janitor";
    if ( special == spec_poison		)	return "spec_poison";
    if ( special == spec_thief		)	return "spec_thief";
    if ( special == spec_catalyst	)	return "spec_catalyst";
    if ( special == spec_miner		)	return "spec_miner";
    if ( special == spec_farmer		)	return "spec_farmer";
    return "";
}

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

    if ( !IS_AWAKE(ch) || IS_FIGHTING(ch) )
        return FALSE;

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

    if ( !victim )
	return FALSE;

    switch ( number_range(0,7)) {
    case 0:
    act( AT_MAGIC, "$n utters the word 'sikana'.", ch, NULL, NULL, TO_ROOM);
	spell_smaug( skill_lookup( "security magic" ), get_curr_wil(ch), ch, victim);
	return TRUE;

    case 1:
    act( AT_MAGIC, "$n utters the word 'nambreh'.", ch, NULL, NULL, TO_ROOM );
	spell_smaug( skill_lookup( "motion magic" ), get_curr_wil(ch), ch, victim);
	return TRUE;

    case 2:
    act( AT_MAGIC, "$n utters the word 'anomis'.", ch, NULL, NULL, TO_ROOM );
	spell_cure_blindness( skill_lookup( "cure blindness" ),
	    get_curr_wil(ch), ch, victim );
	return TRUE;

    case 3:
    act( AT_MAGIC, "$n utters the word 'neosporin'.", ch, NULL, NULL, TO_ROOM );
	spell_smaug( skill_lookup( "cure light" ),
	    get_curr_wil(ch), ch, victim );
	return TRUE;

    case 4:
    act( AT_MAGIC, "$n utters the word 'ahpla'.", ch, NULL, NULL, TO_ROOM);
	spell_cure_poison( skill_lookup( "cure poison" ),
	    get_curr_wil(ch), ch, victim );
	return TRUE;

    case 5:
    act( AT_MAGIC, "$n utters the word 'gatorade'.", ch, NULL, NULL, TO_ROOM );
	spell_smaug( skill_lookup( "refresh" ), get_curr_wil(ch), ch,victim );
	return TRUE;

    case 6:
    act( AT_MAGIC, "$n utters the word 'haloek'.", ch, NULL, NULL, TO_ROOM );
        spell_smaug( skill_lookup( "cure serious" ), get_curr_wil(ch), ch, victim );
        return TRUE;

    case 7:
    act( AT_MAGIC, "$n utters the word 'noics'.", ch, NULL, NULL, TO_ROOM );
        spell_remove_curse( skill_lookup( "remove curse" ),get_curr_wil(ch), ch, victim );
        return TRUE;
	}

    return FALSE;
}


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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
	return FALSE;

    victim = ch->last_hit;

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

	ch_harm(victim, ch, get_curr_int(ch), MAG_FIRE, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

	ch_harm(victim, ch, get_curr_int(ch), MAG_WIND, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), MAG_WATER, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), MAG_EARTH, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), MAG_COLD, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), MAG_ELECTRICITY, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), MAG_PSIONIC, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), MAG_POISON, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), MAG_DRAIN, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), MAG_LIGHT, FALSE);
    return TRUE;
}

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

    if (!IS_FIGHTING(ch) || !ch->in_room || char_died(ch))
        return FALSE;

    victim = ch->last_hit;

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

    ch_harm(victim, ch, get_curr_int(ch), number_range(MAG_NONE, MAG_ALL), FALSE);
    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->first_content; corpse; corpse = c_next )
    {
	c_next = corpse->next_content;
	if ( corpse->item_type != ITEM_CORPSE_NPC )
	    continue;

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

    return FALSE;
}



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

    if ( !IS_AWAKE(ch) || ch->last_hit )
	return FALSE;

    ech      = NULL;
    crime    = "";

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

	if ( !IS_NPC(victim) && xIS_SET(victim->act, PLR_KILLER) )
	    { crime = "KILLER"; break; }

	if ( !IS_NPC(victim) && xIS_SET(victim->act, PLR_THIEF) )
	    { crime = "THIEF"; break; }

    }

    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->first_content; trash; trash = trash_next )
    {
	trash_next = trash->next_content;
	if ( IS_OBJ_STAT( trash, ITEM_NO_TAKE )
	||    IS_OBJ_STAT( trash, ITEM_BURIED )
	||   trash->item_type == ITEM_MONEY
	||   trash->item_type == ITEM_CORPSE_NPC )
	    continue;
	if ( trash->item_type == ITEM_DRINK_CON
	||   trash->item_type == ITEM_TRASH
	||   trash->cost < 1000
	||  (trash->pIndexData->vnum == OBJ_VNUM_SHOPPING_BAG
	&&  !trash->first_content) )
	{
	    act( AT_ACTION, "$n picks up some trash.", ch, NULL, NULL, TO_ROOM );
	    obj_from_room( trash );
	    extract_obj( trash );
	    return TRUE;
	}
    }

    return FALSE;
}


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

   if (!IS_FIGHTING(ch))
	return FALSE;

   if ( ( victim = ch->last_hit ) == NULL
    ||   number_percent( ) > get_curr_dex(ch) )
	return FALSE;

    act( AT_HIT, "You bite $N!",  ch, NULL, victim, TO_CHAR    );
    act( AT_ACTION, "$n bites $N!",  ch, NULL, victim, TO_NOTVICT );
    act( AT_POISON, "$n bites you!", ch, NULL, victim, TO_VICT    );
    spell_poison( gsn_poison, get_curr_end(ch), ch, victim );
    return TRUE;
}



bool spec_thief( CHAR_DATA *ch )
{
    CHAR_DATA *victim;
    CHAR_DATA *v_next;
	char buf[MAX_STRING_LENGTH];
	char name[MAX_STRING_LENGTH];

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

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

	if ( IS_NPC(victim)
	||   number_bits( 2 ) != 0
	||   !can_see( ch, victim ) )	/* Thx Glop */
	    continue;

	if (number_percent() < 80 || victim->last_carrying==NULL) {
		sprintf(buf, "coins %s", victim->name);
		do_steal(ch, buf);
		return TRUE;
	} else {
		one_argument(victim->last_carrying->name, name);
		sprintf(buf, "%s %s", name, victim->name);
		do_steal(ch, buf);
		return TRUE;
    }
	}

    return FALSE;
}

bool spec_catalyst( CHAR_DATA *ch ) {
    OBJ_DATA *obj;
    CHAR_DATA *victim;
    unsigned long oldgold;
    oldgold = ch->gold;

    if ( !IS_AWAKE(ch) )
	return FALSE;

    if (ch->gold == 0) {
	for (victim = ch->in_room->first_person;victim;victim = victim->next_in_room) {
		if (victim == ch) continue;
		for (obj = ch->first_carrying;obj;obj = obj->next_content) {
			if (obj->obj_by && !str_cmp(obj->obj_by, victim->name)) {
				do_say(ch, "Your funneling is done, Master");
				do_drop(ch, "all");
				return TRUE;
			}
		}
	}
	return FALSE;
    }

    for (obj = ch->first_carrying;obj;obj = obj->next_content) {
	obj->mana += 1;
	if (xIS_SET(obj->extra_flags, ITEM_MAGIC)) obj->mana += 1;
	if (obj->material) {
		ch->gold -= obj->material->magic * obj->weight*20;
	} else {
		ch->gold -= 100 * obj->weight*20;
	}
    }

    if (ch->gold < 0) ch->gold = 0;
    if (ch->gold > oldgold) ch->gold = 0;

    return TRUE;
}

bool spec_miner( CHAR_DATA *ch ) {
    CHAR_DATA *victim;
    OBJ_DATA *obj;
    MATERIAL_DATA *material;

    if ( !IS_AWAKE(ch) )
	return FALSE;

    if (ch->gold <= 40000) {
	for (victim = ch->in_room->first_person;victim;victim = victim->next_in_room) {
		if (victim == ch) continue;
		for (obj = ch->first_carrying;obj;obj = obj->next_content) {
			if (obj->obj_by && !str_cmp(obj->obj_by, victim->name)) {
				do_say(ch, "Your mining is done, Master");
				do_mpjunk(ch, "receipt");
				do_drop(ch, "all");
				return TRUE;
			}
		}
	}
	return FALSE;
    }

    if (!ch->first_carrying) {
	for (victim = ch->in_room->first_person;victim;victim = victim->next_in_room) {
		if (victim == ch) continue;
		obj = create_object(get_obj_index(OBJ_VNUM_SCROLL_SCRIBING), 0);
		STRFREE(obj->obj_by);
		obj->obj_by = STRALLOC(victim->name);
		STRFREE(obj->short_descr);
		obj->short_descr = STRALLOC("receipt");
		STRFREE(obj->name);
		obj->name = STRALLOC("receipt");
		obj_to_char(obj, ch);
		return TRUE;
	}
	return FALSE;
    }

    for (material = first_material;material;material = material->next) {
	if (material->sector != ch->in_room->sector_type) continue;
	if (material->cost < 100) continue; /* only get "good" ores */
	if (xIS_SET(material->extra_flags, ITEM_MAGIC)
	&&  ch->in_room->area->weather->mana < -100) continue;
	if (number_percent() > material->rarity) continue;

	obj = make_ore(material->number);
	obj_to_char(obj, ch);
	ch->gold = UMAX(ch->gold - 50000, 0);
	return TRUE;
    }

    return FALSE;
}

bool spec_farmer( CHAR_DATA *ch ) {
    CHAR_DATA *victim;
    OBJ_DATA *obj;
    INGRED_DATA *ingred;

    if ( !IS_AWAKE(ch) )
	return FALSE;

    if (ch->gold <= 500) {
	for (victim = ch->in_room->first_person;victim;victim = victim->next_in_room) {
		if (victim == ch) continue;
		for (obj = ch->first_carrying;obj;obj = obj->next_content) {
			if (obj->obj_by && !str_cmp(obj->obj_by, victim->name)) {
				do_say(ch, "Your harvesting is done, Master");
				do_mpjunk(ch, "receipt");
				do_drop(ch, "all");
				return TRUE;
			}
		}
	}
	return FALSE;
    }

    if (!ch->first_carrying) {
	for (victim = ch->in_room->first_person;victim;victim = victim->next_in_room) {
		if (victim == ch) continue;
		obj = create_object(get_obj_index(OBJ_VNUM_SCROLL_SCRIBING), 0);
		STRFREE(obj->obj_by);
		obj->obj_by = STRALLOC(victim->name);
		STRFREE(obj->short_descr);
		obj->short_descr = STRALLOC("receipt");
		STRFREE(obj->name);
		obj->name = STRALLOC("receipt");
		obj_to_char(obj, ch);
		return TRUE;
	}
	return FALSE;
    }

    for (ingred = first_ingred;ingred;ingred = ingred->next) {
	if (ingred->sector != ch->in_room->sector_type) continue;
	if (ingred->moisture > ch->in_room->curr_water) continue;
	if (ingred->elevation > ch->in_room->curr_elevation) continue;
	if (ingred->precip > 0
	&&  ingred->precip < ch->in_room->area->weather->precip) continue;
	if (ingred->precip < 0
	&&  ingred->precip > ch->in_room->area->weather->precip) continue;
	if (ingred->temp > 0
	&&  ingred->temp < ch->in_room->area->weather->temp) continue;
	if (ingred->temp < 0
	&&  ingred->temp > ch->in_room->area->weather->temp) continue;
	if (ingred->mana > 0
	&&  ingred->mana < ch->in_room->area->weather->mana) continue;
	if (number_percent() > ingred->rarity) continue;

	if (!get_obj_index(ingred->vnum)) {
		bug("Spec_farmer: can't find vnum for ingredient %d!",
			ingred->vnum);
		continue;
	}

	obj = create_object( get_obj_index( ingred->vnum), 0);
	obj_to_char(obj, ch);
	ch->gold = UMAX(ch->gold - 1000, 0);
	return TRUE;
    }

    return FALSE;
}