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