Mud20/accounts/
Mud20/accounts/c/
Mud20/accounts/f/
Mud20/accounts/k/
Mud20/accounts/s/
Mud20/accounts/t/
Mud20/area_current/
Mud20/area_current/newareas/
Mud20/bin/
Mud20/clans/
Mud20/gods/
Mud20/old-sources/
Mud20/player/
Mud20/player/a/del/
Mud20/player/b/
Mud20/player/b/bak/
Mud20/player/b/del/
Mud20/player/f/
Mud20/player/f/bak/
Mud20/player/f/del/
Mud20/player/k/
Mud20/player/k/bak/
Mud20/player/k/del/
Mud20/player/k/dmp/
Mud20/player/m/
Mud20/player/m/bak/
Mud20/player/o/
Mud20/player/o/bak/
Mud20/player/p/
Mud20/player/s/
Mud20/player/s/bak/
Mud20/player/s/del/
Mud20/player/t/
Mud20/player/t/del/
Mud20/player/v/
Mud20/public_html/
Mud20/races/
Mud20/skilltables/
__MACOSX/Mud20/accounts/
__MACOSX/Mud20/accounts/c/
__MACOSX/Mud20/accounts/f/
__MACOSX/Mud20/accounts/k/
__MACOSX/Mud20/accounts/s/
__MACOSX/Mud20/area_current/
__MACOSX/Mud20/area_current/core_areas/
__MACOSX/Mud20/area_current/helps/
__MACOSX/Mud20/area_current/newareas/
__MACOSX/Mud20/backups/
__MACOSX/Mud20/bin/
__MACOSX/Mud20/clans/
__MACOSX/Mud20/gods/
__MACOSX/Mud20/log/
__MACOSX/Mud20/old-sources/
__MACOSX/Mud20/player/
__MACOSX/Mud20/player/a/del/
__MACOSX/Mud20/player/b/
__MACOSX/Mud20/player/b/bak/
__MACOSX/Mud20/player/f/
__MACOSX/Mud20/player/f/bak/
__MACOSX/Mud20/player/f/del/
__MACOSX/Mud20/player/k/
__MACOSX/Mud20/player/k/bak/
__MACOSX/Mud20/player/k/del/
__MACOSX/Mud20/player/k/dmp/
__MACOSX/Mud20/player/m/
__MACOSX/Mud20/player/m/bak/
__MACOSX/Mud20/player/o/
__MACOSX/Mud20/player/o/bak/
__MACOSX/Mud20/player/p/
__MACOSX/Mud20/player/s/
__MACOSX/Mud20/player/s/bak/
__MACOSX/Mud20/player/t/del/
__MACOSX/Mud20/player/v/
__MACOSX/Mud20/public_html/
__MACOSX/Mud20/races/
__MACOSX/Mud20/skilltables/
/***************************************************************************
 * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming   *
 * License by Wizards of the Coast. All comments referring to D20, OGL,    *
 * and SRD refer to the System Reference Document for the Open Gaming      *
 * system. Any inclusion of these derivatives must include credit to the   *
 * Mud20 system, the full and complete Open Gaming LIcense, and credit to  *
 * the respective authors. See ../doc/srd.txt for more information.        *
 *                                                                         *
 * Emud  2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem.   *
 *                                                                         *
 * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey                *
 *                                                                         *
 * 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{rfeld, Tom Madsen, and Katje Nyboe.     *
 ***************************************************************************/
/***************************************************************************
 * traps.c
 * Adapted from code by Helix of Twilight mud <twilight@boreal.alfred.edu>
 * as well as bits of the trap code in Smaug 1.8 by Kregor
 ***************************************************************************/
 
#include "mud.h"

/*
	local functions
*/
void trapdamage			args( (CHAR_DATA *ch, OBJ_DATA *obj) );

/*
 * If an object contains a trap, return the pointer to the trap
 */
OBJ_DATA *get_obj_trap( OBJ_DATA *obj )
{
	OBJ_DATA *trap;

	if( !obj->first_content )
		return NULL;

	for( trap = obj->first_content; trap; trap = trap->next_content )
		if(is_trap_armed(trap))
			return trap;

	return NULL;
}

/*
 * Get trap of specified type in room.
 */
OBJ_DATA *get_room_trap( ROOM_INDEX_DATA *room, int type )
{
	OBJ_DATA *trap;

	if (!room->first_content)
	{
		return NULL;
	}

	for( trap = room->first_content; trap; trap = trap->next_content )
	{
		if (TRAP_TRIGGER(trap, type) && is_trap_armed(trap))
		{
			return trap;
		}
	}
	return NULL;
}


/*
 * trigger a trap if it works. return FALSE if it doesn't
 */
bool spring_trap(CHAR_DATA *ch, OBJ_DATA *obj)
{
	CHAR_DATA *owner;

	if (is_trap_armed(obj))
	{
		/* owner of a trap or his group will not trigger trap */
		if ((owner = get_obj_owner(obj)) == NULL || !is_same_group(owner, ch))
		{
			trapdamage(ch, obj);
			return TRUE;
		}
	}
	return FALSE;
}
	
/*
 * Check an object for a trap, returns FALSE if none
 */
bool trig_obj_trap( CHAR_DATA * ch, OBJ_DATA * obj, int type )
{
	OBJ_DATA *trap;

	if (!obj->first_content)
		return FALSE;

	for (trap = obj->first_content; trap; trap = trap->next_content)
	{
		if (TRAP_TRIGGER(trap, type))
		{
			return spring_trap(ch, trap);
		}
	}
	return FALSE;
}

/*
 * Check the room for a trap, returns FALSE if none.
 */
bool trig_room_trap( CHAR_DATA * ch, int type )
{
	OBJ_DATA *trap;

	if (!ch || !ch->in_room || !ch->in_room->first_content)
	{
		return FALSE;
	}

	for( trap = ch->in_room->first_content; trap; trap = trap->next_content )
	{
		if (TRAP_TRIGGER(trap, type))
		{
			return spring_trap(ch, trap);
		}
	}
	return FALSE;
}



/*
 * Allows an object to make an attack upon victim - Kregor
 */
bool obj_attack( OBJ_DATA *obj, CHAR_DATA *victim, int dt, int hit_adj, int dam )
{
	int diceroll = 0;
	int ac = 0;
	int ranks;

	push_call("obj_attack(%p,%p,%p)",obj,victim,dt,hit_adj,dam);

	if (victim == NULL || is_safe(victim, NULL))
	{
		pop_call();
		return FALSE;
	}
	
	if (dam <= 0)
	{
		pop_call();
		return FALSE;
	}

	ac = GET_AC(victim, FALSE);

	if (can_dodge(victim, NULL))
	{
		ac += dodge_bonus(victim);
	}
	else
	{
		ac += dodge_penalty(victim);
	}
	
	if ((ranks = multi_class_level(victim, gsn_trap_sense)) > 0 && IS_OBJ_TYPE(obj, ITEM_TRAP))
		ac += ranks / 3;

	diceroll = dice(1,20);
	
	if (diceroll == 1)
	{
		pop_call();
		return FALSE;
	}
	else if (diceroll < 20 && diceroll + hit_adj < ac)
	{
		pop_call();
		return FALSE;
	}
	else if (diceroll == 20) //traps get critical hits too - Kregor
	{
		if (dice(1,20) + hit_adj >= ac)
		{
			dam *= 2;
		}
	}	

	dam = damage_modify(victim, victim, dt, dam, NULL);
	dam_message(victim, victim, dam, dt, NULL);
	damage(victim, victim, dam, TYPE_NOFIGHT, NULL);
	
	pop_call();
	return TRUE;
}

/*
 * Calculate the DC of a trap, based on obj level - Kregor
 */
int trap_dc(OBJ_DATA *obj)
{
	int dc;
	
	push_call("trap_dc(%p)",obj);
	
	if (!obj)
	{
		pop_call();
		return -1;
	}
	
	dc = obj->level * 3 / 2;
	dc += 10;

	if (TRAP_TRIGGER(obj, TRAP_TRIG_MAGIC))
		dc += 5;
	
	pop_call();
	return dc;
}


/*
 * The big function, where all the magic happens extesively reworked from
 * original code damaging traps have their dam mssgs issued in this function,
 * spells can be triggered using the level of the trap as the caster level.
 * mload and oload triggers will also trigger mobile progs upon load. 
 * Also, traps with a trap_prog obj prog set on them will trigger - Kregor
 */
void trapdamage(CHAR_DATA *ch, OBJ_DATA *obj)
{
	AFFECT_DATA af; 
	CHAR_DATA *vch, *vch_next, *mob;
	OBJ_DATA *vobj;
	static char *nounce;
	int dam = 0;
	int dt = TYPE_NOFIGHT;
	int sn;
	int vnum;
	bool room = TRAP_TRIGGER(obj, TRAP_TRIG_ROOMAFFECT);

	if (TRAP_STATE(obj) <= 0)
		return;
		
	--TRAP_STATE(obj);
	
	act( "$n springs a trap!", ch, NULL, NULL, TO_ROOM);
	act( "You spring a trap!", ch, NULL, NULL, TO_CHAR);
	
	if (ch->in_room == NULL)
	{
		act( "but it somehow malfunctions.", ch, NULL, NULL, TO_ROOM);
		act( "but it somehow malfunctions.", ch, NULL, NULL, TO_CHAR);
		return;
	}

	switch(obj->value[0])
	{
		case TRAP_TYPE_SLEEP:
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				if ( IS_AFFECTED(vch, AFF_SLEEP) )
					continue;
	
				if (fort_save(vch, NULL, trap_dc(obj) - (multi_class_level(vch, gsn_trap_sense) / 3) + 10, -1))
					continue;

				af.type			= 0;
				af.duration	= 4 + TRAP_VALUE(obj);
				af.location	= APPLY_NONE;
				af.modifier	= 0;
				af.bitvector = AFF_SLEEP;
				af.level	= obj->level;
				affect_join( vch, vch, &af );

				if ( IS_AWAKE(vch) )
				{
					send_to_char( "You feel very sleepy ..... zzzzzz.\n\r", vch );
					act( "$n goes to sleep.", vch, NULL, NULL, TO_ROOM );
					vch->position = POS_SLEEPING;
				}
			} 
			break;
		
		case TRAP_TYPE_TELEPORT:
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				if (refl_save(vch, NULL, trap_dc(obj) - (multi_class_level(vch, gsn_trap_sense) / 3), -1))
					continue;

				if (vch->in_room == NULL
				|| IS_SET(vch->in_room->room_flags, ROOM_NO_RECALL))
				{
					send_to_char( "The trap seems to have malfunctioned.\n\r", vch);
					continue;
				}

				if ((vnum = TRAP_VALUE(obj)) == -1)
					vnum = number_range(vch->in_room->area->low_r_vnum, vch->in_room->area->hi_r_vnum);

				if (room_index[vnum] == NULL || !is_room_good_for_teleport(vch, vnum))
				{
					send_to_char( "Nothing seems to happen to you.\n\r", vch);
					continue;
				}
				act( "$n slowly fades out of existence.", vch, NULL, NULL, TO_ROOM );
				char_from_room( vch );
				char_to_room( vch, vnum, TRUE );
				act( "$n slowly fades into existence.", vch, NULL, NULL, TO_ROOM );
				do_look( ch, "auto" );
			} 
			break;

		case TRAP_TYPE_FIRE:
			dt = gsn_fire_hit;
			if (obj->pIndexData->attack_string[0] == '\0')
				nounce = "burst of flame";
			else
				nounce = obj->pIndexData->attack_string;
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				dam = dice(TRAP_VALUE(obj),6);

				if (refl_save(vch, NULL, trap_dc(obj) - (multi_class_level(vch, gsn_trap_sense) / 3), -1))
					dam /= 2;

				act( "A $T strikes $n!", vch, obj, nounce, TO_ROOM);
				act( "A $T strikes you!", vch, obj, nounce, TO_CHAR);

				dam = damage_modify(vch, vch, dt, dam, NULL);
				damage(vch, vch, dam, TYPE_NOFIGHT, NULL);
			}
			break;

		case TRAP_TYPE_COLD:
			dt = gsn_frost_hit;
			if (obj->pIndexData->attack_string[0] == '\0')
				nounce = "blast of frost";
			else
				nounce = obj->pIndexData->attack_string;
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				dam = dice(TRAP_VALUE(obj),6);

				if (refl_save(vch, NULL, trap_dc(obj) - (multi_class_level(vch, gsn_trap_sense) / 3) + 10, dt))
					dam /= 2;

				act( "A $T strikes $n!", vch, obj, nounce, TO_ROOM);
				act( "A $T strikes you!", vch, obj, nounce, TO_CHAR);

				dam = damage_modify(vch, vch, dt, dam, NULL);
				damage(vch, vch, dam, TYPE_NOFIGHT, NULL);
			}
			break;
			
		case TRAP_TYPE_ACID:
			dt = gsn_acid_hit;
			if (obj->pIndexData->attack_string[0] == '\0')
				nounce = "spray of acid";
			else
				nounce = obj->pIndexData->attack_string;
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				dam = dice(TRAP_VALUE(obj),6);

				if (refl_save(vch, NULL, trap_dc(obj) - (multi_class_level(vch, gsn_trap_sense) / 3), -1))
					dam /= 2;

				act( "A $T strikes $n!", vch, obj, nounce, TO_ROOM);
				act( "A $T strikes you!", vch, obj, nounce, TO_CHAR);

				dam = damage_modify(vch, vch, dt, dam, NULL);
				damage(vch, vch, dam, TYPE_NOFIGHT, NULL);
			}
			break;

		case TRAP_TYPE_ELECTRIC:
			dt = gsn_shock_hit;
			if (obj->pIndexData->attack_string[0] == '\0')
				nounce = "pulse of electricity";
			else
				nounce = obj->pIndexData->attack_string;
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				dam = dice(TRAP_VALUE(obj),6);

				if (refl_save(vch, NULL, trap_dc(obj) - (multi_class_level(vch, gsn_trap_sense) / 3), -1))
					dam /= 2;

				act( "A $T strikes $n!", vch, obj, nounce, TO_ROOM);
				act( "A $T strikes you!", vch, obj, nounce, TO_CHAR);

				dam = damage_modify(vch, vch, dt, dam, NULL);
				dam_message(vch, vch, dam, dt, NULL);
				damage(vch, vch, dam, TYPE_NOFIGHT, NULL);
			}
			break;

		case TRAP_TYPE_BLUNT:
			dt = gsn_bash_hit;
			if (obj->pIndexData->attack_string[0] == '\0')
				nounce = "falling rock trap";
			else
				nounce = obj->pIndexData->attack_string;
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				dam = dice(TRAP_VALUE(obj),6);

				if (refl_save(vch, NULL, trap_dc(obj) - (multi_class_level(vch, gsn_trap_sense) / 3), -1))
				{
					act( "$n dodges aside of a $T!", vch, obj, nounce, TO_ROOM);
					act( "You dodge aside of a $T!", vch, obj, nounce, TO_CHAR);
					continue;
				}

				act( "$n is pummeled by a $T!", vch, obj, nounce, TO_ROOM);
				act( "You are pummeled by a $T!", vch, obj, nounce, TO_CHAR);

				dam = damage_modify(vch, vch, dt, dam, NULL);
				damage(vch, vch, dam, TYPE_NOFIGHT, NULL);
			}
			break;
	
		case TRAP_TYPE_PIERCE:
			dt = gsn_pierce_hit;
			if (obj->pIndexData->attack_string[0] == '\0')
				nounce = "sharp spike";
			else
				nounce = obj->pIndexData->attack_string;
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				dam = dice(TRAP_VALUE(obj),6);

				if (refl_save(vch, NULL, trap_dc(obj) - (multi_class_level(vch, gsn_trap_sense) / 3), -1))
				{
					act( "$n dodges away from a $T!", vch, obj, nounce, TO_ROOM);
					act( "You dodge away from a $T!", vch, obj, nounce, TO_CHAR);
					continue;
				}

				act( "$n is pierced by a $T!", vch, obj, nounce, TO_ROOM);
				act( "You are pierced by a $T!", vch, obj, nounce, TO_CHAR);

				dam = damage_modify(vch, vch, dt, dam, NULL);
				damage(vch, vch, dam, TYPE_NOFIGHT, NULL);
			}
			break;
	
		case TRAP_TYPE_SLASH:
			dt = gsn_slash_hit;
			if (obj->pIndexData->attack_string[0] == '\0')
				nounce = "spinning blade";
			else
				nounce = obj->pIndexData->attack_string;
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				dam = dice(TRAP_VALUE(obj),6);

				if (!obj_attack(obj, vch, dt, obj->level, dam))
				{
					act( "A $T misses $n!", vch, obj, NULL, TO_ROOM);
					act( "A $T misses you!", vch, obj, NULL, TO_CHAR);
					continue;
				}
				act( "A $T slashes $n!", vch, obj, nounce, TO_ROOM);
				act( "A $T slashes you!", vch, obj, nounce, TO_CHAR);
			}
			break;
	
		case TRAP_TYPE_RANGED:
			dt = gsn_pierce_hit;
			if (obj->pIndexData->attack_string[0] == '\0')
				nounce = "volley of missiles";
			else
				nounce = obj->pIndexData->attack_string;
			for (vch = ch->in_room->first_person; vch != NULL; vch = vch_next)
			{
				vch_next = vch->next_in_room;

				if (!room && vch != ch)
					continue;

				dam = dice(TRAP_VALUE(obj),6);

				if (!obj_attack(obj, vch, dt, obj->level, dam))
				{
					act( "A $T misses $n!", vch, obj, NULL, TO_ROOM);
					act( "A $T misses you!", vch, obj, NULL, TO_CHAR);
					continue;
				}
				act( "A $T strikes $n!", vch, obj, nounce, TO_ROOM);
				act( "A $T strikes you!", vch, obj, nounce, TO_CHAR);
			}
			break;
	
		case TRAP_TYPE_MLOAD:
			if (mob_index[TRAP_VALUE(obj)] == NULL)
			{
				bug("trapdamage: null mobile", 0);
				return;
			}
			mob = create_mobile(mob_index[TRAP_VALUE(obj)]);
			mob->npcdata->mloaded = TRUE;
			char_to_room(mob, ch->in_room->vnum, TRUE);

			if (obj->pIndexData->attack_string[0] == '\0')
				act( "$N suddenly appears from nowhere!", ch, obj, mob, TO_ALL);
			else
				act( obj->pIndexData->attack_string, ch, obj, mob, TO_ALL);
			/* check for trap_prog on summoned mobile,
				otherwise, check for aggro */
			if (mprog_trap_trigger(mob, ch))
				break;
			else if (IS_ACT(mob, ACT_AGGRESSIVE))
				fight(mob, ch);
			break;
	
		case TRAP_TYPE_OLOAD:
			if (obj_index[TRAP_VALUE(obj)] == NULL)
			{
				bug("trapdamage: null obj", 0);
				return;
			}
			vobj = create_object(obj_index[TRAP_VALUE(obj)],0);
			obj_to_room(vobj, ch->in_room->vnum);

			if (obj->pIndexData->attack_string[0] == '\0')
				act( "$p suddenly appears from nowhere!", ch, vobj, obj, TO_ALL);
			else
				act( obj->pIndexData->attack_string, ch, vobj, obj, TO_ALL);
			/* check for trap_prog on summoned obj */
			oprog_trap_trigger(ch, vobj);
			break;
	
		case TRAP_TYPE_SPELL:
			if ((sn = TRAP_VALUE(obj)) <= 0 || !is_spell(sn))
			{
				bug("trapdamage: not a spell", 0);
				return;
			}
			set_supermob(obj);
			if (!obj_cast_spell( sn, obj->level, supermob, ch, obj ))
			{
				act( "The trap somehow malfunctions...", supermob, obj, NULL, TO_ROOM);
			}
			release_supermob();
			break;

		case TRAP_TYPE_MPROG:
			if (!IS_SET(obj->pIndexData->progtypes, TRAP_PROG))
			{
				bug("trapdamage: no trap_prog set on trap", 0);
				return;
			}
			if (!oprog_trap_trigger(ch, obj))
			{
				act( "The trap somehow malfunctions.", ch, obj, NULL, TO_ROOM);	
			}
			break;
	}
	return;
}