/***************************************************************************
* 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;
}