/* Returns "mortal", "creature" (if allow_creature), or a more specific term. This is mostly used by immortal/unalive entities when talking to other entities. If they're talking /down/, "creature" is normally inappropriate (e.g. "laughing at cowardly creatures"). If they're merely addressing the permonst in question, "creature" is typically fine (e.g. "Hark, creature!"). Undead can be addressed as "creature"; constructs and demons won't be. */ const char * mortal_or_creature(const struct permonst *data, boolean allow_creature) { if (data->mlet == S_HUMAN) return "mortal"; else if (allow_creature && (!nonliving(data) || is_undead(data)) && !is_demon(data)) return "creature"; else if (!nonliving(data) && !is_demon(data)) return "mortal"; else if (is_demon(data) || monsndx(data) == PM_MANES) return "demon"; else if (nonliving(data) && !is_undead(data)) return "construct"; /* golems and vortices */ else if (data->mlet == S_LICH) return "lich"; else if (data->mlet == S_MUMMY) return "mummy"; else if (data->mlet == S_VAMPIRE) return "vampire"; else if (data->mlet == S_ZOMBIE && monsndx(data) != PM_GHOUL && monsndx(data) != PM_SKELETON) return "zombie"; else return data->mname; }
boolean inside_gas_cloud(void *p1, void *p2) { struct region *reg; struct monst *mtmp; long dam; reg = (struct region *)p1; dam = (long)reg->arg; if (p2 == NULL) { /* This means *YOU* Bozo! */ if (nonliving(youmonst.data) || u.uinvulnerable) return FALSE; /* If you will unblind next turn, extend the blindness so that you do * not get a "You can see again!" message immediately before being * blinded again. */ if (!Blind || Blinded == 1) make_blinded(2L, FALSE); if (Breathless) return FALSE; if (!Poison_resistance) { pline("Something is burning your %s!", makeplural(body_part(LUNG))); pline("You cough and spit blood!"); losehp(rnd(dam) + 5, killer_msg(DIED, "a gas cloud")); return FALSE; } else { pline("You cough!"); return FALSE; } } else { /* A monster is inside the cloud */ mtmp = (struct monst *)p2; /* Non living and non breathing monsters are not concerned */ if (!nonliving(mtmp->data) && !breathless(mtmp->data)) { if (cansee(mtmp->mx, mtmp->my)) pline("%s coughs!", Monnam(mtmp)); setmangry(mtmp); if (haseyes(mtmp->data) && mtmp->mcansee) { mtmp->mblinded = 1; mtmp->mcansee = 0; } if (resists_poison(mtmp)) return FALSE; mtmp->mhp -= rnd(dam) + 5; if (mtmp->mhp <= 0) { if (heros_fault(reg)) killed(mtmp); else monkilled(mtmp, "gas cloud", AD_DRST); if (DEADMONSTER(mtmp)) { /* not lifesaved */ return TRUE; } } } } return FALSE; /* Monster is still alive */ }
/* If dmg is zero, then the monster is not casting at you. If the monster is intentionally not casting at you, we have previously called spell_would_be_useless() and spellnum should always be a valid undirected spell. If you modify either of these, be sure to change is_undirected_spell() and spell_would_be_useless(). */ static void cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum) { if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) { impossible("cast directed wizard spell (%d) with dmg=0?", spellnum); return; } switch (spellnum) { case MGC_DEATH_TOUCH: pline("Oh no, %s's using the touch of death!", mhe(mtmp)); if (nonliving(youmonst.data) || is_demon(youmonst.data)) { pline("You seem no deader than before."); } else if (!Antimagic && rn2(mtmp->m_lev) > 12) { if (Hallucination) { pline("You have an out of body experience."); } else { killer_format = KILLED_BY_AN; killer = "touch of death"; done(DIED); } } else { if (Antimagic) shieldeff(u.ux, u.uy); pline("Lucky for you, it didn't work!"); } dmg = 0; break; case MGC_CLONE_WIZ: if (mtmp->iswiz && flags.no_of_wizards == 1) { pline("Double Trouble..."); clonewiz(); dmg = 0; } else impossible("bad wizard cloning?"); break; case MGC_SUMMON_MONS: { int count; count = nasty(mtmp); /* summon something nasty */ if (mtmp->iswiz) verbalize("Destroy the thief, my pet%s!", plur(count)); else { const char *mappear = (count == 1) ? "A monster appears" : "Monsters appear"; /* messages not quite right if plural monsters created but only a single monster is seen */ if (Invisible && !perceives(mtmp->data) && (mtmp->mux != u.ux || mtmp->muy != u.uy)) pline("%s around a spot near you!", mappear); else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) pline("%s around your displaced image!", mappear); else pline("%s from nowhere!", mappear); } dmg = 0; break; } case MGC_AGGRAVATION: pline("You feel that monsters are aware of your presence."); aggravate(); dmg = 0; break; case MGC_CURSE_ITEMS: pline("You feel as if you need some help."); rndcurse(); dmg = 0; break; case MGC_DESTRY_ARMR: if (Antimagic) { shieldeff(u.ux, u.uy); pline("A field of force surrounds you!"); } else if (!destroy_arm(some_armor(&youmonst))) { pline("Your skin itches."); } dmg = 0; break; case MGC_WEAKEN_YOU: /* drain strength */ if (Antimagic) { shieldeff(u.ux, u.uy); pline("You feel momentarily weakened."); } else { pline("You suddenly feel weaker!"); dmg = mtmp->m_lev - 6; if (Half_spell_damage) dmg = (dmg + 1) / 2; losestr(rnd(dmg)); if (u.uhp < 1) done_in_by(mtmp); } dmg = 0; break; case MGC_DISAPPEAR: /* makes self invisible */ if (!mtmp->minvis && !mtmp->invis_blkd) { if (canseemon(mtmp)) pline("%s suddenly %s!", Monnam(mtmp), !See_invisible ? "disappears" : "becomes transparent"); mon_set_minvis(mtmp); dmg = 0; } else impossible("no reason for monster to cast disappear spell?"); break; case MGC_STUN_YOU: if (Antimagic || Free_action) { shieldeff(u.ux, u.uy); if (!Stunned) pline("You feel momentarily disoriented."); make_stunned(1L, FALSE); } else { pline(Stunned ? "You struggle to keep your balance." : "You reel..."); dmg = dice(ACURR(A_DEX) < 12 ? 6 : 4, 4); if (Half_spell_damage) dmg = (dmg + 1) / 2; make_stunned(HStun + dmg, FALSE); } dmg = 0; break; case MGC_HASTE_SELF: mon_adjust_speed(mtmp, 1, NULL); dmg = 0; break; case MGC_CURE_SELF: if (mtmp->mhp < mtmp->mhpmax) { if (canseemon(mtmp)) pline("%s looks better.", Monnam(mtmp)); /* note: player healing does 6d4; this used to do 1d8 */ if ((mtmp->mhp += dice(3,6)) > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; dmg = 0; } break; case MGC_PSI_BOLT: /* prior to 3.4.0 Antimagic was setting the damage to 1--this made the spell virtually harmless to players with magic res. */ if (Antimagic) { shieldeff(u.ux, u.uy); dmg = (dmg + 1) / 2; } if (dmg <= 5) pline("You get a slight %sache.", body_part(HEAD)); else if (dmg <= 10) pline("Your brain is on fire!"); else if (dmg <= 20) pline("Your %s suddenly aches painfully!", body_part(HEAD)); else pline("Your %s suddenly aches very painfully!", body_part(HEAD)); break; default: impossible("mcastu: invalid magic spell (%d)", spellnum); dmg = 0; break; } if (dmg) mdamageu(mtmp, dmg); }
/* 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; }
/* Note: I had to choose one of three possible kinds of "type" when writing * this function: a wand type (like in zap.c), an adtyp, or an object type. * Wand types get complex because they must be converted to adtyps for * determining such things as fire resistance. Adtyps get complex in that * they don't supply enough information--was it a player or a monster that * did it, and with a wand, spell, or breath weapon? Object types share both * these disadvantages.... * * The descr argument should be used to describe the explosion. It should be * a string suitable for use with an(). * raylevel is used for explosions caused by skilled wand usage (0=no wand) */ void explode(int x, int y, int type, /* the same as in zap.c */ int dam, char olet, int expltype, const char *descr, int raylevel) { int i, j, k, damu = dam; boolean visible, any_shield, resist_death; resist_death = FALSE; int uhurt = 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */ const char *str; const char *dispbuf = ""; /* lint suppression; I think the code's OK */ boolean expl_needs_the = TRUE; int idamres, idamnonres; struct monst *mtmp; uchar adtyp; int explmask[3][3]; /* 0=normal explosion, 1=do shieldeff, 2=do nothing */ boolean shopdamage = FALSE; #if 0 /* Damage reduction from wand explosions */ if (olet == WAND_CLASS) /* retributive strike */ switch (Role_switch) { case PM_PRIEST: case PM_MONK: case PM_WIZARD: damu /= 5; break; case PM_HEALER: case PM_KNIGHT: damu /= 2; break; default: break; } #endif if (olet == MON_EXPLODE) { str = descr; adtyp = AD_PHYS; if (Hallucination) { int name = rndmonidx(); dispbuf = msgcat(s_suffix(monnam_for_index(name)), " explosion"); expl_needs_the = !monnam_is_pname(name); } else { dispbuf = str; } } else { int whattype = abs(type) % 10; adtyp = whattype + 1; boolean done = FALSE, hallu = Hallucination; if (hallu) { do { whattype = rn2(8); } while (whattype == 3); } tryagain: switch (whattype) { case 0: str = "magical blast"; break; case 1: str = olet == BURNING_OIL ? "burning oil" : olet == SCROLL_CLASS ? "tower of flame" : "fireball"; break; case 2: str = "ball of cold"; break; case 3: str = "sleeping gas"; break; case 4: str = (olet == WAND_CLASS) ? "death field" : "disintegration field"; break; case 5: str = "ball of lightning"; break; case 6: str = "poison gas cloud"; break; case 7: str = "splash of acid"; break; default: impossible("explosion base type %d?", type); return; } if (!done) { dispbuf = str; done = TRUE; if (hallu) { whattype = adtyp - 1; goto tryagain; } } } any_shield = visible = FALSE; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { if (!isok(i + x - 1, j + y - 1)) { explmask[i][j] = 2; continue; } else explmask[i][j] = 0; if (i + x - 1 == u.ux && j + y - 1 == u.uy) { switch (adtyp) { case AD_PHYS: explmask[i][j] = 0; break; case AD_MAGM: explmask[i][j] = !!(raylevel >= P_EXPERT || Antimagic); break; case AD_FIRE: explmask[i][j] = !!Fire_resistance; break; case AD_COLD: explmask[i][j] = !!Cold_resistance; break; case AD_SLEE: explmask[i][j] = !!Sleep_resistance; break; case AD_DISN: if (raylevel == P_UNSKILLED && Drain_resistance) resist_death = TRUE; /* why MR doesn't resist general deathfields is beyond me, but... */ if (nonliving(youmonst.data) || is_demon(youmonst.data)) resist_death = TRUE; if (raylevel && Antimagic) resist_death = TRUE; if (raylevel >= P_EXPERT && !Drain_resistance) resist_death = FALSE; explmask[i][j] = (olet == WAND_CLASS) ? !!resist_death : !!Disint_resistance; break; case AD_ELEC: explmask[i][j] = !!Shock_resistance; break; case AD_DRST: explmask[i][j] = !!Poison_resistance; break; case AD_ACID: explmask[i][j] = !!Acid_resistance; break; default: impossible("explosion type %d?", adtyp); break; } } /* can be both you and mtmp if you're swallowed */ mtmp = m_at(level, i + x - 1, j + y - 1); if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy) mtmp = u.usteed; if (mtmp) { if (mtmp->mhp < 1) explmask[i][j] = 2; else switch (adtyp) { case AD_PHYS: break; case AD_MAGM: explmask[i][j] |= (raylevel >= 4 || resists_magm(mtmp)); break; case AD_FIRE: explmask[i][j] |= resists_fire(mtmp); break; case AD_COLD: explmask[i][j] |= resists_cold(mtmp); break; case AD_SLEE: explmask[i][j] |= resists_sleep(mtmp); case AD_DISN: if (raylevel == P_UNSKILLED && resists_drli(mtmp)) resist_death = TRUE; if (nonliving(mtmp->data) || is_demon(mtmp->data)) resist_death = TRUE; if (raylevel && resists_magm(mtmp)) resist_death = TRUE; if (raylevel >= P_EXPERT && !resists_drli(mtmp)) resist_death = FALSE; explmask[i][j] |= (olet == WAND_CLASS) ? resist_death : resists_disint(mtmp); break; case AD_ELEC: explmask[i][j] |= resists_elec(mtmp); break; case AD_DRST: explmask[i][j] |= resists_poison(mtmp); break; case AD_ACID: explmask[i][j] |= resists_acid(mtmp); break; default: impossible("explosion type %d?", adtyp); break; } } reveal_monster_at(i + x - 1, j + y - 1, TRUE); if (cansee(i + x - 1, j + y - 1)) visible = TRUE; if (explmask[i][j] == 1) any_shield = TRUE; } if (visible) { struct tmp_sym *tsym = tmpsym_init(DISP_BEAM, 0); /* Start the explosion */ for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { if (explmask[i][j] == 2) continue; tmpsym_change(tsym, dbuf_explosion(expltype, explosion[i][j])); tmpsym_at(tsym, i + x - 1, j + y - 1); } flush_screen(); /* will flush screen and output */ if (any_shield && flags.sparkle) { /* simulate shield effect */ for (k = 0; k < SHIELD_COUNT; k++) { for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { if (explmask[i][j] == 1) /* * Bypass tmpsym_at() and send the shield glyphs * directly to the buffered screen. tmpsym_at() * will clean up the location for us later. */ dbuf_set_effect(i + x - 1, j + y - 1, dbuf_effect(E_MISC, shield_static[k])); } flush_screen(); /* will flush screen and output */ win_delay_output(); } /* Cover last shield glyph with blast symbol. */ for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { if (explmask[i][j] == 1) dbuf_set_effect(i + x - 1, j + y - 1, dbuf_explosion(expltype, explosion[i][j])); } } else { /* delay a little bit. */ win_delay_output(); win_delay_output(); } tmpsym_end(tsym); /* clear the explosion */ } else { if (olet == MON_EXPLODE) { str = "explosion"; } You_hear("a blast."); } if (dam) for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { if (explmask[i][j] == 2) continue; if (i + x - 1 == u.ux && j + y - 1 == u.uy) uhurt = (explmask[i][j] == 1) ? 1 : 2; idamres = idamnonres = 0; if (type >= 0) zap_over_floor((xchar) (i + x - 1), (xchar) (j + y - 1), type, &shopdamage); mtmp = m_at(level, i + x - 1, j + y - 1); if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy) mtmp = u.usteed; if (!mtmp) continue; if (Engulfed && mtmp == u.ustuck) { if (is_animal(u.ustuck->data)) pline("%s gets %s!", Monnam(u.ustuck), (adtyp == AD_FIRE) ? "heartburn" : (adtyp == AD_COLD) ? "chilly" : (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ? "irradiated by pure energy" : "perforated") : (adtyp == AD_ELEC) ? "shocked" : (adtyp == AD_DRST) ? "poisoned" : (adtyp == AD_ACID) ? "an upset stomach" : "fried"); else pline("%s gets slightly %s!", Monnam(u.ustuck), (adtyp == AD_FIRE) ? "toasted" : (adtyp == AD_COLD) ? "chilly" : (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ? "overwhelmed by pure energy" : "perforated") : (adtyp == AD_ELEC) ? "shocked" : (adtyp == AD_DRST) ? "intoxicated" : (adtyp == AD_ACID) ? "burned" : "fried"); } else if (cansee(i + x - 1, j + y - 1)) { if (mtmp->m_ap_type) seemimic(mtmp); pline("%s is caught in %s%s!", Monnam(mtmp), expl_needs_the ? "the " : "", dispbuf); } idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int)adtyp); idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int)adtyp); idamnonres += destroy_mitem(mtmp, POTION_CLASS, (int)adtyp); idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int)adtyp); idamnonres += destroy_mitem(mtmp, RING_CLASS, (int)adtyp); if (explmask[i][j] == 1) { golemeffects(mtmp, (int)adtyp, dam + idamres); mtmp->mhp -= idamnonres; } else { /* call resist with 0 and do damage manually so 1) we can get out the message before doing the damage, and 2) we can call mondied, not killed, if it's not your blast */ int mdam = dam; if (resist(mtmp, olet, 0, FALSE)) { if (cansee(i + x - 1, j + y - 1)) pline("%s resists %s%s!", Monnam(mtmp), expl_needs_the ? "the " : "", dispbuf); mdam = dam / 2; } if (mtmp == u.ustuck) mdam *= 2; if (resists_cold(mtmp) && adtyp == AD_FIRE) mdam *= 2; else if (resists_fire(mtmp) && adtyp == AD_COLD) mdam *= 2; if (adtyp == AD_MAGM && raylevel >= P_EXPERT && resists_magm(mtmp)) mdam = (mdam + 1) / 2; if (adtyp == AD_SLEE && raylevel) { sleep_monst(mtmp, mdam, WAND_CLASS); mdam = 0; } if (adtyp == AD_DISN && raylevel) { if (nonliving(mtmp->data) || is_demon(mtmp->data) || resists_magm(mtmp) || raylevel == P_UNSKILLED) { /* monster is deathresistant or raylevel==unskilled, since monster apparently failed to resist earlier, monster must be vulnerable to drli */ /* FIXME: make a generic losexp() for monsters */ mdam = dice(2, 6); if (cansee(i + x - 1, j + y - 1)) pline("%s suddenly seems weaker!", Monnam(mtmp)); mtmp->mhpmax -= mdam; if (mtmp->m_lev == 0) mdam = mtmp->mhp; else mtmp->m_lev--; } else mdam = mtmp->mhp; /* instadeath */ } mtmp->mhp -= mdam; mtmp->mhp -= (idamres + idamnonres); } if (mtmp->mhp <= 0) { /* KMH -- Don't blame the player for pets killing gas spores */ if (!flags.mon_moving) killed(mtmp); else monkilled(mtmp, "", (int)adtyp); } else if (!flags.mon_moving) setmangry(mtmp); } /* Do your injury last */ if (uhurt) { if ((type >= 0 || adtyp == AD_PHYS) && /* gas spores */ flags.verbose && olet != SCROLL_CLASS) pline("You are caught in %s%s!", expl_needs_the ? "the " : "", dispbuf); /* do property damage first, in case we end up leaving bones */ if (adtyp == AD_FIRE) burn_away_slime(); if (u.uinvulnerable) { damu = 0; pline("You are unharmed!"); } else if (Half_physical_damage && adtyp == AD_PHYS) damu = (damu + 1) / 2; else if (raylevel) { if (adtyp == AD_MAGM && Antimagic) damu = (damu + 1) / 2; if (adtyp == AD_SLEE) { helpless(damu, hr_asleep, "sleeping", NULL); damu = 0; } if (adtyp == AD_DISN) { if (nonliving(youmonst.data) || is_demon(youmonst.data) || Antimagic || raylevel == P_UNSKILLED) { losexp("drained by a death field",FALSE); damu = 0; } else { done(DIED, "killed by a death field"); damu = 0; /* lifesaved */ } } } if (adtyp == AD_FIRE) { burnarmor(&youmonst); set_candles_afire(); } destroy_item(SCROLL_CLASS, (int)adtyp); destroy_item(SPBOOK_CLASS, (int)adtyp); destroy_item(POTION_CLASS, (int)adtyp); destroy_item(RING_CLASS, (int)adtyp); destroy_item(WAND_CLASS, (int)adtyp); ugolemeffects((int)adtyp, damu); if (uhurt == 2) { if (Upolyd) u.mh -= damu; else u.uhp -= damu; } if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) { int death = adtyp == AD_FIRE ? BURNING : DIED; const char *killer; if (olet == MON_EXPLODE) { killer = killer_msg(death, an(str)); } else if (type >= 0 && olet != SCROLL_CLASS) { /* check whether or not we were the source of the explosion */ if (!flags.mon_moving) killer = msgprintf("caught %sself in %s own %s", uhim(), uhis(), str); else killer = msgprintf("killed by a %s", str); } else if (!strcmp(str, "burning oil")) { /* This manual check hack really sucks */ killer = killer_msg(death, str); } else { killer = killer_msg(death, an(str)); } /* Known BUG: BURNING suppresses corpse in bones data, but done does not handle killer reason correctly */ if (Upolyd) { rehumanize(death, killer); } else { done(death, killer); } } exercise(A_STR, FALSE); } if (shopdamage) { pay_for_damage(adtyp == AD_FIRE ? "burn away" : adtyp == AD_COLD ? "shatter" : adtyp == AD_DISN ? "disintegrate" : "destroy", FALSE); } /* explosions are noisy */ i = dam * dam; if (i < 50) i = 50; /* in case random damage is very small */ wake_nearto(x, y, i); }