static void e_died(struct entity *etmp, int dest, int how) { if (is_u(etmp)) { if (how == DROWNING) { killer = 0; /* drown() sets its own killer */ drown(); } else if (how == BURNING) { killer = 0; /* lava_effects() sets its own killer */ lava_effects(); } else { coord xy; /* use more specific killer if specified */ if (!killer) { killer_format = KILLED_BY_AN; killer = "falling drawbridge"; } done(how); /* So, you didn't die */ if (!e_survives_at(etmp, etmp->ex, etmp->ey)) { if (enexto(&xy, level, etmp->ex, etmp->ey, etmp->edata)) { pline("A %s force teleports you away...", Hallucination ? "normal" : "strange"); teleds(xy.x, xy.y, FALSE); } /* otherwise on top of the drawbridge is the * only viable spot in the dungeon, so stay there */ } } /* we might have crawled out of the moat to survive */ etmp->ex = u.ux, etmp->ey = u.uy; } else { int entitycnt; killer = 0; /* fake "digested to death" damage-type suppresses corpse */ #define mk_message(dest) ((dest & 1) ? "" : NULL) #define mk_corpse(dest) ((dest & 2) ? AD_DGST : AD_PHYS) /* if monsters are moving, one of them caused the destruction */ if (flags.mon_moving) monkilled(etmp->emon, mk_message(dest), mk_corpse(dest)); else /* you caused it */ xkilled(etmp->emon, dest); etmp->edata = NULL; /* dead long worm handling */ for (entitycnt = 0; entitycnt < ENTITIES; entitycnt++) { if (etmp != &(occupants[entitycnt]) && etmp->emon == occupants[entitycnt].emon) occupants[entitycnt].edata = NULL; } #undef mk_message #undef mk_corpse } }
/* Generate earthquake :-) of desired force. That is: create random chasms (pits). Currently assumes that the player created it (you'll need to change at least messages, angering, and kill credit if you generalize it). */ static void do_earthquake(int force) { int x, y; struct monst *mtmp; struct obj *otmp; struct trap *chasm, *oldtrap; int start_x, start_y, end_x, end_y; start_x = youmonst.mx - (force * 2); start_y = youmonst.my - (force * 2); end_x = youmonst.mx + (force * 2); end_y = youmonst.my + (force * 2); if (start_x < 0) start_x = 0; if (start_y < 0) start_y = 0; if (end_x >= COLNO) end_x = COLNO - 1; if (end_y >= ROWNO) end_y = ROWNO - 1; for (x = start_x; x <= end_x; x++) for (y = start_y; y <= end_y; y++) { if ((mtmp = m_at(level, x, y)) != 0) { wakeup(mtmp, FALSE); /* peaceful monster will become hostile */ if (mtmp->mundetected && is_hider(mtmp->data)) { mtmp->mundetected = 0; if (cansee(x, y)) pline(msgc_youdiscover, "%s is shaken loose from %s!", Amonnam(mtmp), mtmp->data == &mons[PM_TRAPPER] ? "its hiding place" : the(ceiling(youmonst.mx, youmonst.my))); else You_hear(msgc_levelsound, "a thumping sound."); if (x == youmonst.mx && y == youmonst.my && mtmp->data != &mons[PM_TRAPPER]) pline(msgc_moncombatgood, "You easily dodge the falling %s.", mon_nam(mtmp)); newsym(x, y); } } if (!rn2(14 - force)) switch (level->locations[x][y].typ) { case FOUNTAIN: /* Make the fountain disappear */ if (cansee(x, y)) pline(msgc_consequence, "The fountain falls into a chasm."); goto do_pit; case SINK: if (cansee(x, y)) pline(msgc_consequence, "The kitchen sink falls into a chasm."); goto do_pit; case ALTAR: if (level->locations[x][y].altarmask & AM_SANCTUM) break; if (cansee(x, y)) pline(msgc_consequence, "The altar falls into a chasm."); goto do_pit; case GRAVE: if (cansee(x, y)) pline(msgc_consequence, "The headstone topples into a chasm."); goto do_pit; case THRONE: if (cansee(x, y)) pline(msgc_consequence, "The throne falls into a chasm."); /* Falls into next case */ case ROOM: case CORR: /* Try to make a pit */ /* Pits, spiked pits, holes, trapdoors, vibrating squares, magic portals are immune. A bear trap will leave the trap in the pit. It would be kind of cool to make landmines detonate, but that's more trouble than it's worth. */ if ((oldtrap = t_at(level, x, y))) { if (oldtrap->ttyp == PIT || oldtrap->ttyp == SPIKED_PIT || oldtrap->ttyp == HOLE || oldtrap->ttyp == TRAPDOOR || oldtrap->ttyp == VIBRATING_SQUARE || oldtrap->ttyp == MAGIC_PORTAL) break; if (oldtrap->ttyp == BEAR_TRAP) { if (mtmp) mtmp->mtrapped = 0; cnv_trap_obj(level, BEARTRAP, 1, oldtrap); } } do_pit: chasm = maketrap(level, x, y, PIT, rng_main); if (!chasm) break; /* no pit if portal at that location */ chasm->tseen = 1; level->locations[x][y].doormask = 0; mtmp = m_at(level, x, y); if ((otmp = sobj_at(BOULDER, level, x, y)) != 0) { if (cansee(x, y)) pline(msgc_consequence, "KADOOM! The boulder falls into a chasm%s!", ((x == youmonst.mx) && (y == youmonst.my)) ? " below you" : ""); if (mtmp) mtmp->mtrapped = 0; obj_extract_self(otmp); flooreffects(otmp, x, y, ""); break; } /* We have to check whether monsters or player falls in a chasm... */ if (mtmp) { if (!flying(mtmp) && !levitates(mtmp) && !is_clinger(mtmp->data)) { mtmp->mtrapped = 1; if (cansee(x, y)) pline(combat_msgc(&youmonst, mtmp, cr_hit), "%s falls into a chasm!", Monnam(mtmp)); else if (humanoid(mtmp->data)) You_hear(msgc_levelsound, "a scream!"); mselftouch(mtmp, "Falling, ", &youmonst); if (!DEADMONSTER(mtmp)) if ((mtmp->mhp -= rnd(6)) <= 0) { if (!cansee(x, y)) pline(msgc_kill, "It is destroyed!"); else { pline(msgc_petfatal, "You destroy %s!", mtmp->mtame ? x_monnam(mtmp, ARTICLE_THE, "poor", mx_name(mtmp) ? SUPPRESS_SADDLE : 0, FALSE) : mon_nam(mtmp)); } xkilled(mtmp, 0); } } } else if (!u.utrap && x == youmonst.mx && y == youmonst.my) { if (Levitation || Flying || is_clinger(youmonst.data)) { pline(msgc_noconsequence, "A chasm opens up under you!"); pline(msgc_noconsequence, "You don't fall in!"); } else { pline(msgc_badidea, "You fall into a chasm!"); u.utrap = rn1(6, 2); u.utraptype = TT_PIT; turnstate.vision_full_recalc = TRUE; losehp(rnd(6), "fell into a chasm"); selftouch("Falling, you", "falling into a chasm while wielding"); } } else newsym(x, y); break; case DOOR: /* Make the door collapse */ if (level->locations[x][y].doormask == D_NODOOR) goto do_pit; if (cansee(x, y)) pline(msgc_consequence, "The door collapses."); if (*in_rooms(level, x, y, SHOPBASE)) add_damage(x, y, 0L); level->locations[x][y].doormask = D_NODOOR; unblock_point(x, y); newsym(x, y); break; } } }
/* an object launched by someone/thing other than player attacks a monster; return 1 if the object has stopped moving (hit or its range used up) */ int ohitmon(struct monst *mtmp, /* accidental target */ struct obj *otmp, /* missile; might be destroyed by drop_throw */ int range, /* how much farther will object travel if it misses */ /* Use -1 to signify to keep going even after hit, unless it's gone (used for rolling_boulder_traps) */ boolean verbose) { /* give message(s) even when you can't see what happened */ int damage, tmp; boolean vis, ismimic; int objgone = 1; ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER; vis = cansee(bhitpos.x, bhitpos.y); tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE); if (tmp < rnd(20)) { if (!ismimic) { if (vis) miss(distant_name(otmp, mshot_xname), mtmp); else if (verbose) pline("It is missed."); } if (!range) { /* Last position; object drops */ if (is_pole(otmp)) return 1; drop_throw(otmp, 0, mtmp->mx, mtmp->my); return 1; } } else if (otmp->oclass == POTION_CLASS) { if (ismimic) seemimic(mtmp); mtmp->msleeping = 0; if (vis) otmp->dknown = 1; potionhit(mtmp, otmp, FALSE); return 1; } else { damage = dmgval(otmp, mtmp); if (otmp->otyp == ACID_VENOM && resists_acid(mtmp)) damage = 0; if (otmp->otyp == VAMPIRE_BLOOD && resists_drli(mtmp)) damage = 0; if (ismimic) seemimic(mtmp); mtmp->msleeping = 0; if (vis) hit(distant_name(otmp, mshot_xname), mtmp, exclam(damage)); else if (verbose) pline("%s is hit%s", Monnam(mtmp), exclam(damage)); if (otmp->opoisoned && is_poisonable(otmp)) { if (resists_poison(mtmp)) { if (vis) pline("The poison doesn't seem to affect %s.", mon_nam(mtmp)); } else { if (rn2(30)) { damage += rnd(6); } else { if (vis) pline("The poison was deadly..."); damage = mtmp->mhp; } } } if (objects[otmp->otyp].oc_material == SILVER && hates_silver(mtmp->data)) { if (vis) pline("The silver sears %s flesh!", s_suffix(mon_nam(mtmp))); else if (verbose) pline("Its flesh is seared!"); } if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx, mtmp->my)) { if (resists_acid(mtmp)) { if (vis || verbose) pline("%s is unaffected.", Monnam(mtmp)); damage = 0; } else { if (vis) pline("The acid burns %s!", mon_nam(mtmp)); else if (verbose) pline("It is burned!"); } } mtmp->mhp -= damage; if (mtmp->mhp < 1) { if (vis || verbose) pline("%s is %s!", Monnam(mtmp), (nonliving(mtmp->data) || !canclassifymon(mtmp)) ? "destroyed" : "killed"); /* don't blame hero for unknown rolling boulder trap */ if (!flags.mon_moving && (otmp->otyp != BOULDER || range >= 0 || otmp->otrapped)) xkilled(mtmp, 0); else mondied(mtmp); } if (can_blnd (NULL, mtmp, (uchar) (otmp->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP), otmp)) { if (vis && mtmp->mcansee) pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp))); mtmp->mcansee = 0; tmp = (int)mtmp->mblinded + rnd(25) + 20; if (tmp > 127) tmp = 127; mtmp->mblinded = tmp; } if (otmp->otyp == VAMPIRE_BLOOD) { if (!resists_drli(mtmp)) { int xtmp = dice(2, 6); if (vis) pline("%s suddenly seems weaker!", Monnam(mtmp)); mtmp->mhpmax -= xtmp; if ((mtmp->mhp -= xtmp) <= 0 || !mtmp->m_lev) { if (vis) pline("%s dies!", Monnam(mtmp)); xkilled(mtmp, 0); } else mtmp->m_lev--; } obfree(otmp, NULL); return 1; } if (is_pole(otmp)) return 1; objgone = drop_throw(otmp, 1, bhitpos.x, bhitpos.y); if (!objgone && range == -1) { /* special case */ obj_extract_self(otmp); /* free it for motion again */ return 0; } return 1; } return 0; }