-/* NetHack 3.6 zap.c $NHDT-Date: 1524470244 2018/04/23 07:57:24 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.275 $ */
+/* NetHack 3.6 zap.c $NHDT-Date: 1573688696 2019/11/13 23:44:56 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.316 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
/* JNetHack Copyright */
/* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
-/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2019 */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
/* JNetHack may be freely redistributed. See license for details. */
#include "hack.h"
STATIC_DCL void FDECL(revive_egg, (struct obj *));
STATIC_DCL boolean FDECL(zap_steed, (struct obj *));
STATIC_DCL void FDECL(skiprange, (int, int *, int *));
-
STATIC_DCL int FDECL(zap_hit, (int, int));
STATIC_OVL void FDECL(disintegrate_mon, (struct monst *, int, const char *));
STATIC_DCL void FDECL(backfire, (struct obj *));
STATIC_DCL int FDECL(spell_hit_bonus, (int));
+STATIC_DCL void FDECL(destroy_one_item, (struct obj *, int, int));
+STATIC_DCL void FDECL(wishcmdassist, (int));
#define ZT_MAGIC_MISSILE (AD_MAGM - 1)
#define ZT_FIRE (AD_FIRE - 1)
const char *const flash_types[] = /* also used in buzzmu(mcastu.c) */
{
-#if 0 /*JP*/
+#if 0 /*JP:T*/
"magic missile", /* Wands must be 0-9 */
"bolt of fire", "bolt of cold", "sleep ray", "death ray",
"bolt of lightning", "", "", "", "",
"",
#endif
-#if 0 /*JP*/
+#if 0 /*JP:T*/
"magic missile", /* Spell equivalents must be 10-19 */
"fireball", "cone of cold", "sleep ray", "finger of death",
"bolt of lightning", /* there is no spell, used for retribution */
"",
#endif
-#if 0 /*JP*/
+#if 0 /*JP:T*/
"blast of missiles", /* Dragon breath equivalents 20-29*/
"blast of fire", "blast of frost", "blast of sleep gas",
"blast of disintegration", "blast of lightning",
if (obj->dknown)
makeknown(obj->otyp);
}
+ update_inventory();
}
}
boolean wake = TRUE; /* Most 'zaps' should wake monster */
boolean reveal_invis = FALSE, learn_it = FALSE;
boolean dbldam = Role_if(PM_KNIGHT) && u.uhave.questart;
- boolean helpful_gesture = FALSE;
+ boolean skilled_spell, helpful_gesture = FALSE;
int dmg, otyp = otmp->otyp;
/*JP
const char *zap_type_text = "spell";
const char *zap_type_text = "\96\82\96@";
struct obj *obj;
boolean disguised_mimic = (mtmp->data->mlet == S_MIMIC
- && mtmp->m_ap_type != M_AP_NOTHING);
+ && M_AP_TYPE(mtmp) != M_AP_NOTHING);
if (u.uswallow && mtmp == u.ustuck)
reveal_invis = FALSE;
notonhead = (mtmp->mx != bhitpos.x || mtmp->my != bhitpos.y);
+ skilled_spell = (otmp && otmp->oclass == SPBOOK_CLASS && otmp->blessed);
+
switch (otyp) {
case WAN_STRIKING:
/*JP
zap_type_text = "wand";
*/
zap_type_text = "\8fñ";
- /* fall through */
+ /*FALLTHRU*/
case SPE_FORCE_BOLT:
reveal_invis = TRUE;
if (disguised_mimic)
dmg = spell_damage_bonus(dmg);
context.bypasses = TRUE; /* for make_corpse() */
if (!resist(mtmp, otmp->oclass, dmg, NOTELL)) {
- if (mtmp->mhp > 0)
+ if (!DEADMONSTER(mtmp))
monflee(mtmp, 0, FALSE, TRUE);
}
}
case WAN_POLYMORPH:
case SPE_POLYMORPH:
case POT_POLYMORPH:
- if (resists_magm(mtmp)) {
+ if (mtmp->data == &mons[PM_LONG_WORM] && has_mcorpsenm(mtmp)) {
+ /* if a long worm has mcorpsenm set, it was polymophed by
+ the current zap and shouldn't be affected if hit again */
+ ;
+ } else if (resists_magm(mtmp)) {
/* magic resistance protects from polymorph traps, so make
it guard against involuntary polymorph attacks too... */
shieldeff(mtmp->mx, mtmp->my);
if (polyspot)
for (obj = mtmp->minvent; obj; obj = obj->nobj)
bypass_obj(obj);
+
/* natural shapechangers aren't affected by system shock
(unless protection from shapechangers is interfering
with their metabolism...) */
|| (u.uswallow && mtmp == u.ustuck)))
learn_it = TRUE;
}
+
+ /* do this even if polymorphed failed (otherwise using
+ flags.mon_polycontrol prompting to force mtmp to remain
+ 'long worm' would prompt again if zap hit another segment) */
+ if (!DEADMONSTER(mtmp) && mtmp->data == &mons[PM_LONG_WORM]) {
+ if (!has_mcorpsenm(mtmp))
+ newmcorpsenm(mtmp);
+ /* flag to indicate that mtmp became a long worm
+ on current zap, so further hits (on mtmp's new
+ tail) don't do further transforms */
+ MCORPSENM(mtmp) = PM_LONG_WORM;
+ /* flag to indicate that cleanup is needed; object
+ bypass cleanup also clears mon->mextra->mcorpsenm
+ for all long worms on the level */
+ context.bypasses = TRUE;
+ }
}
break;
case WAN_CANCELLATION:
} else if ((obj = which_armor(mtmp, W_SADDLE)) != 0) {
char buf[BUFSZ];
-#if 0 /*JP*/
+#if 0 /*JP:T*/
Sprintf(buf, "%s %s", s_suffix(Monnam(mtmp)),
distant_name(obj, xname));
#else
if (cansee(mtmp->mx, mtmp->my)) {
if (!canspotmon(mtmp))
Strcpy(buf, An(distant_name(obj, xname)));
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s falls to the %s.", buf,
surface(mtmp->mx, mtmp->my));
#else
mtmp->mhp += d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4);
if (mtmp->mhp > mtmp->mhpmax)
mtmp->mhp = mtmp->mhpmax;
- if (mtmp->mblinded) {
- mtmp->mblinded = 0;
- mtmp->mcansee = 1;
- }
+ /* plain healing must be blessed to cure blindness; extra
+ healing only needs to not be cursed, so spell always cures
+ [potions quaffed by monsters behave slightly differently;
+ we use the rules for the hero here...] */
+ if (skilled_spell || otyp == SPE_EXTRA_HEALING)
+ mcureblindness(mtmp, canseemon(mtmp));
if (canseemon(mtmp)) {
if (disguised_mimic) {
if (is_obj_mappear(mtmp,STRANGE_OBJECT)) {
} else
mimic_hit_msg(mtmp, otyp);
} else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s looks%s better.", Monnam(mtmp),
otyp == SPE_EXTRA_HEALING ? " much" : "");
#else
dmg = spell_damage_bonus(dmg);
if (resists_drli(mtmp)) {
shieldeff(mtmp->mx, mtmp->my);
- } else if (!resist(mtmp, otmp->oclass, dmg, NOTELL) && mtmp->mhp > 0) {
+ } else if (!resist(mtmp, otmp->oclass, dmg, NOTELL)
+ && !DEADMONSTER(mtmp)) {
mtmp->mhp -= dmg;
mtmp->mhpmax -= dmg;
/* die if already level 0, regardless of hit points */
- if (mtmp->mhp <= 0 || mtmp->mhpmax <= 0 || mtmp->m_lev < 1) {
+ if (DEADMONSTER(mtmp) || mtmp->mhpmax <= 0 || mtmp->m_lev < 1) {
killed(mtmp);
} else {
mtmp->m_lev--;
break;
}
if (wake) {
- if (mtmp->mhp > 0) {
+ if (!DEADMONSTER(mtmp)) {
wakeup(mtmp, helpful_gesture ? FALSE : TRUE);
m_respond(mtmp);
if (mtmp->isshk && !*u.ushops)
hot_pursuit(mtmp);
- } else if (mtmp->m_ap_type)
+ } else if (M_AP_TYPE(mtmp))
seemimic(mtmp); /* might unblock if mimicing a boulder/door */
}
/* note: bhitpos won't be set if swallowed, but that's okay since
* might be an invisible worm hit on the tail.
*/
if (reveal_invis) {
- if (mtmp->mhp > 0 && cansee(bhitpos.x, bhitpos.y)
+ if (!DEADMONSTER(mtmp) && cansee(bhitpos.x, bhitpos.y)
&& !canspotmon(mtmp))
map_invisible(bhitpos.x, bhitpos.y);
}
(void) display_minventory(mtmp, MINV_ALL | MINV_NOLET | PICK_NONE,
(char *) 0);
} else {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s is not carrying anything%s.", noit_Monnam(mtmp),
(u.uswallow && mtmp == u.ustuck) ? " besides you" : "");
#else
/* used by revive() and animate_statue() */
struct monst *
-montraits(obj, cc)
+montraits(obj, cc, adjacentok)
struct obj *obj;
coord *cc;
+boolean adjacentok; /* False: at obj's spot only, True: nearby is allowed */
{
- struct monst *mtmp = (struct monst *) 0;
- struct monst *mtmp2 = (struct monst *) 0;
+ struct monst *mtmp, *mtmp2 = has_omonst(obj) ? get_mtraits(obj, TRUE) : 0;
- if (has_omonst(obj))
- mtmp2 = get_mtraits(obj, TRUE);
if (mtmp2) {
/* save_mtraits() validated mtmp2->mnum */
mtmp2->data = &mons[mtmp2->mnum];
if (mtmp2->mhpmax <= 0 && !is_rider(mtmp2->data))
return (struct monst *) 0;
mtmp = makemon(mtmp2->data, cc->x, cc->y,
- NO_MINVENT | MM_NOWAIT | MM_NOCOUNTBIRTH);
- if (!mtmp)
- return mtmp;
+ (NO_MINVENT | MM_NOWAIT | MM_NOCOUNTBIRTH
+ | (adjacentok ? MM_ADJACENTOK : 0)));
+ if (!mtmp) {
+ /* mtmp2 is a copy of obj's object->oextra->omonst extension
+ and is not on the map or on any monst lists */
+ dealloc_monst(mtmp2);
+ return (struct monst *) 0;
+ }
/* heal the monster */
if (mtmp->mhpmax > mtmp2->mhpmax && is_rider(mtmp2->data))
if ((mons[montype].mlet == S_EEL && !IS_POOL(levl[x][y].typ))
|| (mons[montype].mlet == S_TROLL
&& uwep && uwep->oartifact == ART_TROLLSBANE)) {
- if (by_hero && cansee(x,y))
+ if (by_hero && cansee(x, y))
/*JP
pline("%s twitches feebly.",
*/
} else if (has_omonst(corpse)) {
/* use saved traits */
xy.x = x, xy.y = y;
- mtmp = montraits(corpse, &xy);
+ mtmp = montraits(corpse, &xy, FALSE);
if (mtmp && mtmp->mtame && !mtmp->isminion)
wary_dog(mtmp, TRUE);
} else {
mtmp->mundetected = 0;
newsym(mtmp->mx, mtmp->my);
}
- if (mtmp->m_ap_type)
+ if (M_AP_TYPE(mtmp))
seemimic(mtmp);
one_of = (corpse->quan > 1L);
if (cansee(x, y)) {
char buf[BUFSZ];
- unsigned pfx = CXN_PFX_THE;
-#if 1 /*JP*//* \8e\80\91Ì\96¼\82Í\90æ\82É */
- Strcpy(buf, corpse_xname(corpse, (const char *) 0, pfx));
+#if 1 /*JP*//* \8e\80\91Ì\96¼\82Í\90æ\82É; Strcpy\82É\82È\82é */
+ Strcpy(buf, corpse_xname(corpse, (const char *) 0, CXN_NO_PFX));
#endif
#if 0 /*JP*/
Strcpy(buf, one_of ? "one of " : "");
}
#endif
#if 0 /*JP*//* \82±\82±\82Å\81u\82 \82È\82½\82Ì\81v\82Í\95s\8e©\91R */
- if (carried(corpse) && !corpse->unpaid) {
- Strcat(buf, "your ");
- pfx = CXN_NO_PFX;
- }
+ /* shk_your: "the " or "your " or "<mon>'s " or "<Shk>'s ".
+ If the result is "Shk's " then it will be ambiguous:
+ is Shk the mon carrying it, or does Shk's shop own it?
+ Let's not worry about that... */
+ (void) shk_your(eos(buf), corpse);
#endif
if (one_of)
corpse->quan++; /* force plural */
#if 0 /*JP*//* \8aù\82É\90Ý\92è\8dÏ\82Ý */
- Strcat(buf, corpse_xname(corpse, (const char *) 0, pfx));
+ Strcat(buf, corpse_xname(corpse, (const char *) 0, CXN_NO_PFX));
#endif
if (one_of) /* could be simplified to ''corpse->quan = 1L;'' */
corpse->quan--;
ghost = find_mid(m_id, FM_FMON);
if (ghost && ghost->data == &mons[PM_GHOST]) {
if (canseemon(ghost))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s is suddenly drawn into its former body!",
#else
pline("%s\82Í\93Ë\91R\82à\82Æ\82Ì\91Ì\82É\88ø\82«\8d\9e\82Ü\82ê\82½\81I",
default:
/* if all else fails... */
pm_index = PM_STRAW_GOLEM;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
material = "";
#else
material = "\95¨\91Ì";
polyuse(obj, okind, (int) mons[pm_index].cwt);
if (mtmp && cansee(mtmp->mx, mtmp->my)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("Some %sobjects meld, and %s arises from the pile!", material,
a_monnam(mtmp));
#else
int id;
{
struct obj *otmp;
- xchar ox, oy;
+ xchar ox = 0, oy = 0;
+ long old_wornmask, new_wornmask = 0L;
boolean can_merge = (id == STRANGE_OBJECT);
int obj_location = obj->where;
/* now change it into something laid by the hero */
while (tryct--) {
- mnum = can_be_hatched(random_monster());
+ mnum = can_be_hatched(random_monster(rn2));
if (mnum != NON_PM && !dead_species(mnum, TRUE)) {
otmp->spe = 1; /* laid by hero */
set_corpsenm(otmp, mnum); /* also sets hatch timer */
/* update the weight */
otmp->owt = weight(otmp);
- /* handle polymorph of worn item: stone-to-flesh cast on self can
- affect multiple objects at once, but their new forms won't
- produce any side-effects; a single worn item dipped into potion
- of polymorph can produce side-effects but those won't yield out
- of sequence messages because current polymorph is finished */
- if (obj_location == OBJ_INVENT && obj->owornmask) {
- long old_wornmask = obj->owornmask & ~(W_ART | W_ARTI),
- new_wornmask = wearslot(otmp);
- boolean was_twohanded = bimanual(obj), was_twoweap = u.twoweap;
-
- remove_worn_item(obj, TRUE);
- /* if the new form can be worn in the same slot, make it so
- [possible extension: if it could be worn in some other
- slot which is currently unfilled, wear it there instead] */
- if ((old_wornmask & W_QUIVER) != 0L) {
- setuqwep(otmp);
- } else if ((old_wornmask & W_SWAPWEP) != 0L) {
- if (was_twohanded || !bimanual(otmp))
- setuswapwep(otmp);
- if (was_twoweap && uswapwep)
- u.twoweap = TRUE;
- } else if ((old_wornmask & W_WEP) != 0L) {
- if (was_twohanded || !bimanual(otmp) || !uarms)
- setuwep(otmp);
- if (was_twoweap && uwep && !bimanual(uwep))
- u.twoweap = TRUE;
- } else if ((old_wornmask & new_wornmask) != 0L) {
- new_wornmask &= old_wornmask;
- setworn(otmp, new_wornmask);
- set_wear(otmp); /* Armor_on() for side-effects */
- }
- }
-
- /* ** we are now done adjusting the object ** */
+ /*
+ * ** we are now done adjusting the object (except possibly wearing it) **
+ */
+ (void) get_obj_location(obj, &ox, &oy, BURIED_TOO | CONTAINED_TOO);
+ old_wornmask = obj->owornmask & ~(W_ART | W_ARTI);
/* swap otmp for obj */
replace_object(obj, otmp);
if (obj_location == OBJ_INVENT) {
freeinv_core(obj);
addinv_core1(otmp);
addinv_core2(otmp);
+ /*
+ * Handle polymorph of worn item. Stone-to-flesh cast on self can
+ * affect multiple objects at once, but their new forms won't
+ * produce any side-effects. A single worn item dipped into potion
+ * of polymorph can produce side-effects but those won't yield out
+ * of sequence messages because current polymorph is finished.
+ */
+ if (old_wornmask) {
+ boolean was_twohanded = bimanual(obj), was_twoweap = u.twoweap;
+
+ /* wearslot() returns a mask which might have multiple bits set;
+ narrow that down to the bit(s) currently in use */
+ new_wornmask = wearslot(otmp) & old_wornmask;
+ remove_worn_item(obj, TRUE);
+ /* if the new form can be worn in the same slot, make it so */
+ if ((new_wornmask & W_WEP) != 0L) {
+ if (was_twohanded || !bimanual(otmp) || !uarms)
+ setuwep(otmp);
+ if (was_twoweap && uwep && !bimanual(uwep))
+ u.twoweap = TRUE;
+ } else if ((new_wornmask & W_SWAPWEP) != 0L) {
+ if (was_twohanded || !bimanual(otmp))
+ setuswapwep(otmp);
+ if (was_twoweap && uswapwep)
+ u.twoweap = TRUE;
+ } else if ((new_wornmask & W_QUIVER) != 0L) {
+ setuqwep(otmp);
+ } else if (new_wornmask) {
+ setworn(otmp, new_wornmask);
+ /* set_wear() might result in otmp being destroyed if
+ worn amulet has been turned into an amulet of change */
+ set_wear(otmp);
+ otmp = wearmask_to_obj(new_wornmask); /* might be Null */
+ }
+ } /* old_wornmask */
} else if (obj_location == OBJ_FLOOR) {
- ox = otmp->ox, oy = otmp->oy; /* set by replace_object() */
if (obj->otyp == BOULDER && otmp->otyp != BOULDER
&& !does_block(ox, oy, &levl[ox][oy]))
unblock_point(ox, oy);
block_point(ox, oy);
}
- if ((!carried(otmp) || obj->unpaid)
- && get_obj_location(otmp, &ox, &oy, BURIED_TOO | CONTAINED_TOO)
- && costly_spot(ox, oy)) {
- register struct monst *shkp =
- shop_keeper(*in_rooms(ox, oy, SHOPBASE));
+ /* note: if otmp is gone, billing for it was handled by useup() */
+ if (((otmp && !carried(otmp)) || obj->unpaid) && costly_spot(ox, oy)) {
+ struct monst *shkp = shop_keeper(*in_rooms(ox, oy, SHOPBASE));
if ((!obj->no_charge
|| (Has_contents(obj)
&& inhishop(shkp)) {
if (shkp->mpeaceful) {
if (*u.ushops
- && *in_rooms(u.ux, u.uy, 0)
- == *in_rooms(shkp->mx, shkp->my, 0)
+ && (*in_rooms(u.ux, u.uy, 0)
+ == *in_rooms(shkp->mx, shkp->my, 0))
&& !costly_spot(u.ux, u.uy)) {
make_angry_shk(shkp, ox, oy);
} else {
else
delobj(obj);
if (cansee(mon->mx, mon->my))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline_The("figurine %sanimates!",
golem_xform ? "turns to flesh and " : "");
#else
* from its inventory as a result of the change.
* If the items fall to the floor, they are not
* subject to direct subsequent polymorphing
- * themselves on that same zap. This makes it
- * consistent with items that remain in the
- * monster's inventory. They are not polymorphed
- * either.
+ * themselves on that same zap. This makes it
+ * consistent with items that remain in the monster's
+ * inventory. They are not polymorphed either.
* UNDEAD_TURNING - When an undead creature gets killed via
* undead turning, prevent its corpse from being
* immediately revived by the same effect.
* menu_drop(), askchain() - inventory traversal where multiple
* Drop can alter the invent chain while traversal
* is in progress (bhito isn't involved).
+ * destroy_item(), destroy_mitem() - inventory traversal where
+ * item destruction can trigger drop or destruction of
+ * other item(s) and alter the invent or mon->minvent
+ * chain, possibly recursively.
*
* The bypass bit on all objects is reset each turn, whenever
* context.bypasses is set.
(void) boxlock(obj, otmp);
if (obj_shudders(obj)) {
- boolean cover =
- ((obj == level.objects[u.ux][u.uy]) && u.uundetected
- && hides_under(youmonst.data));
+ boolean cover = ((obj == level.objects[u.ux][u.uy])
+ && u.uundetected
+ && hides_under(youmonst.data));
if (cansee(obj->ox, obj->oy))
learn_it = TRUE;
if (Is_container(obj) || obj->otyp == STATUE) {
obj->cknown = obj->lknown = 1;
if (!obj->cobj) {
- boolean catbox = SchroedingersBox(obj);
-
+/*JP
+ pline("%s empty.", Tobjnam(obj, "are"));
+*/
+ pline("%s\82Í\8bó\82Á\82Û\82¾\81D", xname(obj));
+ } else if (SchroedingersBox(obj)) {
/* we don't want to force alive vs dead
determination for Schroedinger's Cat here,
so just make probing be inconclusive for it */
- if (catbox)
- obj->cknown = 0;
-/*JP
- pline("%s empty.", Tobjnam(obj, catbox ? "seem" : "are"));
-*/
- pline("%s\82Í\8bó\82Á\82Û%s\82¾\81D", xname(obj), catbox ? "\82Ì\82æ\82¤" : "");
+#if 0 /*JP:T*/
+ You("aren't sure whether %s has %s or its corpse inside.",
+ the(xname(obj)),
+ /* unfortunately, we can't tell whether rndmonnam()
+ picks a form which can't leave a corpse */
+ an(Hallucination ? rndmonnam((char *) 0) : "cat"));
+#else
+ pline("%s\82É%s\82ª\93ü\82Á\82Ä\82¢\82é\82Ì\82©\82»\82Ì\8e\80\91Ì\82ª\93ü\82Á\82Ä\82¢\82é\82Ì\82©\82í\82©\82ç\82È\82¢\81D",
+ xname(obj),
+ /* unfortunately, we can't tell whether rndmonnam()
+ picks a form which can't leave a corpse */
+ Hallucination ? rndmonnam((char *) 0) : "\94L");
+#endif
+ obj->cknown = 0;
} else {
struct obj *o;
+
/* view contents (not recursively) */
for (o = obj->cobj; o; o = o->nobj)
o->dknown = 1; /* "seen", even if blind */
if (obj->otyp == EGG) {
revive_egg(obj);
} else if (obj->otyp == CORPSE) {
+ struct monst *mtmp;
+ xchar ox, oy;
int corpsenm = corpse_revive_type(obj);
+ char *corpsname = cxname_singular(obj);
- res = !!revive(obj, TRUE);
- if (res && Role_if(PM_HEALER)) {
- if (Hallucination && !Deaf) {
-/*JP
- You_hear("the sound of a defibrillator.");
-*/
- You_hear("\8f\9c\8d×\93®\8aí\82Ì\89¹\82ð\95·\82¢\82½\81D");
- learn_it = TRUE;
- } else if (!Blind) {
-#if 0 /*JP*/
- You("observe %s %s change dramatically.",
- s_suffix(an(mons[corpsenm].mname)),
- nonliving(&mons[corpsenm]) ? "motility"
- : "health");
+ /* get corpse's location before revive() uses it up */
+ if (!get_obj_location(obj, &ox, &oy, 0))
+ ox = obj->ox, oy = obj->oy; /* won't happen */
+
+ mtmp = revive(obj, TRUE);
+ if (!mtmp) {
+ res = 0; /* no monster implies corpse was left intact */
+ } else {
+ if (cansee(ox, oy)) {
+ if (canspotmon(mtmp)) {
+#if 0 /*JP:T*/
+ pline("%s is resurrected!",
+ upstart(noname_monnam(mtmp, ARTICLE_THE)));
#else
- You("%s\82Ì%s\82ª\8c\80\93I\82É\95Ï\89»\82·\82é\82Ì\82ð\8c©\82½\81D",
- mons[corpsenm].mname,
- nonliving(&mons[corpsenm]) ? "\89^\93®\90«"
- : "\8c\92\8dN");
+ pline("%s\82Í\90¶\82«\95Ô\82Á\82½\81I",
+ upstart(noname_monnam(mtmp, ARTICLE_THE)));
#endif
- learn_it = TRUE;
+ learn_it = TRUE;
+ } else {
+ /* saw corpse but don't see monster: maybe
+ mtmp is invisible, or has been placed at
+ a different spot than <ox,oy> */
+ if (!type_is_pname(&mons[corpsenm]))
+ corpsname = The(corpsname);
+/*JP
+ pline("%s disappears.", corpsname);
+*/
+ pline("%s\82Í\8fÁ\82¦\82½\81D", corpsname);
+ }
+ } else {
+ /* couldn't see corpse's location */
+ if (Role_if(PM_HEALER) && !Deaf
+ && !nonliving(&mons[corpsenm])) {
+ if (!type_is_pname(&mons[corpsenm]))
+ corpsname = an(corpsname);
+ if (!Hallucination)
+/*JP
+ You_hear("%s reviving.", corpsname);
+*/
+ You_hear("%s\82ª\90¶\82«\95Ô\82é\89¹\82ð\95·\82¢\82½\81D", corpsname);
+ else
+/*JP
+ You_hear("a defibrillator.");
+*/
+ You_hear("\8f\9c\8d×\93®\8aí\82Ì\89¹\82ð\95·\82¢\82½\81D");
+ learn_it = TRUE;
+ }
+ if (canspotmon(mtmp))
+ /* didn't see corpse but do see monster: it
+ has been placed somewhere other than <ox,oy>
+ or blind hero spots it with ESP */
+/*JP
+ pline("%s appears.", Monnam(mtmp));
+*/
+ pline("%s\82ª\8c»\82ê\82½\81D", Monnam(mtmp));
}
if (learn_it)
exercise(A_WIS, TRUE);
case WAN_CANCELLATION:
case SPE_CANCELLATION:
- (void) cancel_monst(&youmonst, obj, TRUE, FALSE, TRUE);
+ (void) cancel_monst(&youmonst, obj, TRUE, TRUE, TRUE);
break;
case SPE_DRAIN_LIFE:
learn_it = TRUE;
(void) unturn_dead(&youmonst);
if (is_undead(youmonst.data)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
You_feel("frightened and %sstunned.",
Stunned ? "even more " : "");
#else
case SPE_EXTRA_HEALING:
learn_it = TRUE; /* (no effect for spells...) */
healup(d(6, obj->otyp == SPE_EXTRA_HEALING ? 8 : 4), 0, FALSE,
- (obj->otyp == SPE_EXTRA_HEALING));
+ (obj->blessed || obj->otyp == SPE_EXTRA_HEALING));
/*JP
You_feel("%sbetter.", obj->otyp == SPE_EXTRA_HEALING ? "much " : "");
*/
learn_it = TRUE;
unpunish();
}
- if (u.utrap) { /* escape web or bear trap */
- (void) openholdingtrap(&youmonst, &learn_it);
- } else {
+ /* invent is hit iff hero doesn't escape from a trap */
+ if (!u.utrap || !openholdingtrap(&youmonst, &learn_it)) {
struct obj *otmp;
+ boolean boxing = FALSE;
+
/* unlock carried boxes */
for (otmp = invent; otmp; otmp = otmp->nobj)
- if (Is_box(otmp))
+ if (Is_box(otmp)) {
(void) boxlock(otmp, obj);
+ boxing = TRUE;
+ }
+ if (boxing)
+ update_inventory(); /* in case any box->lknown has changed */
+
/* trigger previously escaped trapdoor */
(void) openfallingtrap(&youmonst, TRUE, &learn_it);
}
break;
case WAN_LOCKING:
case SPE_WIZARD_LOCK:
- if (!u.utrap) {
- (void) closeholdingtrap(&youmonst, &learn_it);
+ /* similar logic to opening; invent is hit iff no trap triggered */
+ if (u.utrap || !closeholdingtrap(&youmonst, &learn_it)) {
+ struct obj *otmp;
+ boolean boxing = FALSE;
+
+ /* lock carried boxes */
+ for (otmp = invent; otmp; otmp = otmp->nobj)
+ if (Is_box(otmp)) {
+ (void) boxlock(otmp, obj);
+ boxing = TRUE;
+ }
+ if (boxing)
+ update_inventory(); /* in case any box->lknown has changed */
}
break;
case WAN_DIGGING:
otmp->cknown = 1;
}
}
+ update_inventory();
learn_it = TRUE;
ustatusline();
break;
/* now handle special cases */
if (youdefend) {
- if (Upolyd) {
- if ((u.umonnum == PM_CLAY_GOLEM) && !Blind)
- pline(writing_vanishes, your);
-
- if (Unchanging)
+ if (Upolyd) { /* includes lycanthrope in creature form */
+ /*
+ * Return to normal form unless Unchanging.
+ * Hero in clay golem form dies if Unchanging.
+ * Does not cure lycanthropy or stop timed random polymorph.
+ */
+ if (u.umonnum == PM_CLAY_GOLEM) {
+ if (!Blind)
+ pline(writing_vanishes, your);
+ else /* note: "dark" rather than "heavy" is intentional... */
+#if 0 /*JP*/
+ You_feel("%s headed.", Hallucination ? "dark" : "light");
+#else /*\82¢\82¢\96ó\8cê\82ð\8ev\82¢\82Â\82©\82È\82¢\82Ì\82Å\8c¶\8ao\82Å\83\81\83b\83Z\81[\83W\82ð\95Ï\82¦\82È\82¢ */
+ You_feel("\8cy\82Í\82¸\82Ý\82¾\82Á\82½\8bC\82ª\82µ\82½\81D");
+#endif
+ u.mh = 0; /* fatal; death handled by rehumanize() */
+ }
+ if (Unchanging && u.mh > 0)
/*JP
Your("amulet grows hot for a moment, then cools.");
*/
rehumanize();
}
} else {
- mdef->mcan = TRUE;
-
- if (is_were(mdef->data) && mdef->data->mlet != S_HUMAN)
+ mdef->mcan = 1;
+ /* force shapeshifter into its base form */
+ if (M_AP_TYPE(mdef) != M_AP_NOTHING)
+ seemimic(mdef);
+ /* [not 'else if'; chameleon might have been hiding as a mimic] */
+ if (mdef->cham >= LOW_PM) {
+ /* note: newcham() uncancels shapechangers (resets m->mcan
+ to 0), but only for shapechangers whose m->cham is already
+ NON_PM and we just verified that it's LOW_PM or higher */
+ newcham(mdef, &mons[mdef->cham], FALSE, FALSE);
+ mdef->cham = NON_PM; /* cancelled shapeshifter can't shift */
+ }
+ if (is_were(mdef->data) && !is_human(mdef->data))
were_change(mdef);
if (mdef->data == &mons[PM_CLAY_GOLEM]) {
#else
pline(writing_vanishes, mon_nam(mdef));
#endif
-
+ /* !allow_cancel_kill is for Magicbane, where clay golem
+ will be killed somewhere back up the call/return chain... */
if (allow_cancel_kill) {
if (youattack)
killed(mdef);
&& !Is_qstart(&u.uz)) {
int dmg;
/* similar to zap_dig() */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("A rock is dislodged from the %s and falls on your %s.",
ceiling(x, y), body_part(HEAD));
#else
/* locking transforms hole into trapdoor */
ttmp->ttyp = TRAPDOOR;
if (Blind || !ttmp->tseen) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("Some %s swirls beneath you.",
is_ice(x, y) ? "frost" : "dust");
#else
case SPE_STONE_TO_FLESH:
if (e->engr_type == ENGRAVE) {
/* only affects things in stone */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline_The(Hallucination
? "floor runs like butter!"
: "edges on the floor get smoother.");
*/
pline("%s\82Í\89½\82©\82É\96½\92\86\82µ\82½\81D", str);
else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s %s %s%s", The(str), vtense(str, "hit"),
mon_nam(mtmp), force);
#else
register const char *str;
register struct monst *mtmp;
{
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline(
"%s %s %s.", The(str), vtense(str, "miss"),
((cansee(bhitpos.x, bhitpos.y) || canspotmon(mtmp)) && flags.verbose)
struct monst *
bhit(ddx, ddy, range, weapon, fhitm, fhito, pobj)
register int ddx, ddy, range; /* direction and range */
-int weapon; /* see values in hack.h */
+enum bhit_call_types weapon; /* defined in hack.h */
int FDECL((*fhitm), (MONST_P, OBJ_P)), /* fns called when mon/obj hit */
FDECL((*fhito), (OBJ_P, OBJ_P));
struct obj **pobj; /* object tossed/used, set to NULL
* if object is destroyed */
{
- struct monst *mtmp;
+ struct monst *mtmp, *result = (struct monst *) 0;
struct obj *obj = *pobj;
uchar typ;
boolean shopdoor = FALSE, point_blank = TRUE;
boolean in_skip = FALSE, allow_skip = FALSE;
+ boolean tethered_weapon = FALSE;
int skiprange_start = 0, skiprange_end = 0, skipcount = 0;
if (weapon == KICKED_WEAPON) {
if (weapon == FLASHED_LIGHT) {
tmp_at(DISP_BEAM, cmap_to_glyph(S_flashbeam));
+ } else if (weapon == THROWN_TETHERED_WEAPON && obj) {
+ tethered_weapon = TRUE;
+ weapon = THROWN_WEAPON; /* simplify 'if's that follow below */
+ tmp_at(DISP_TETHER, obj_to_glyph(obj, rn2_on_display_rng));
} else if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
- tmp_at(DISP_FLASH, obj_to_glyph(obj));
+ tmp_at(DISP_FLASH, obj_to_glyph(obj, rn2_on_display_rng));
while (range-- > 0) {
int x, y;
if (is_pick(obj) && inside_shop(x, y)
&& (mtmp = shkcatch(obj, x, y)) != 0) {
tmp_at(DISP_END, 0);
- return mtmp;
+ result = mtmp;
+ goto bhit_done;
}
typ = levl[bhitpos.x][bhitpos.y].typ;
- /* iron bars will block anything big enough */
- if ((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
- && typ == IRONBARS
- && hits_bars(pobj, x - ddx, y - ddy, bhitpos.x, bhitpos.y,
- point_blank ? 0 : !rn2(5), 1)) {
- /* caveat: obj might now be null... */
- obj = *pobj;
- bhitpos.x -= ddx;
- bhitpos.y -= ddy;
- break;
+ /* iron bars will block anything big enough and break some things */
+ if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) {
+ if (typ == IRONBARS
+ && hits_bars(pobj, x - ddx, y - ddy, bhitpos.x, bhitpos.y,
+ point_blank ? 0 : !rn2(5), 1)) {
+ /* caveat: obj might now be null... */
+ obj = *pobj;
+ bhitpos.x -= ddx;
+ bhitpos.y -= ddy;
+ break;
+ } else if (obj->lamplit && !Blind) {
+ show_transient_light(obj, bhitpos.x, bhitpos.y);
+ }
}
if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) {
if (is_pool(bhitpos.x, bhitpos.y) && !mtmp) {
in_skip = TRUE;
if (!Blind)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s %s%s.", Yname2(obj), otense(obj, "skip"),
skipcount ? " again" : "");
#else
if (range > 3) /* another bounce? */
skiprange(range, &skiprange_start, &skiprange_end);
} else if (mtmp && M_IN_WATER(mtmp->data)) {
- if ((!Blind && canseemon(mtmp)) || sensemon(mtmp))
-#if 0 /*JP*/
+ if (!Blind && canspotmon(mtmp))
+#if 0 /*JP:T*/
pline("%s %s over %s.", Yname2(obj), otense(obj, "pass"),
mon_nam(mtmp));
#else
pline("%s\82Í%s\82ð\94ò\82Ñ\89z\82¦\82½\81D", Yname2(obj),
mon_nam(mtmp));
#endif
+ mtmp = (struct monst *) 0;
}
}
- if (mtmp && !(in_skip && M_IN_WATER(mtmp->data))) {
+ /* if mtmp is a shade and missile passes harmlessly through it,
+ give message and skip it in order to keep going */
+ if (mtmp && (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
+ && shade_miss(&youmonst, mtmp, obj, TRUE, TRUE))
+ mtmp = (struct monst *) 0;
+
+ if (mtmp) {
notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
if (weapon == FLASHED_LIGHT) {
/* FLASHED_LIGHT hitting invisible monster should
(void) flash_hits_mon(mtmp, obj);
} else {
tmp_at(DISP_END, 0);
- return mtmp; /* caller will call flash_hits_mon */
+ result = mtmp; /* caller will call flash_hits_mon */
+ goto bhit_done;
}
} else if (weapon == INVIS_BEAM) {
/* Like FLASHED_LIGHT, INVIS_BEAM should continue
prepared for multiple hits so just get first one
that's either visible or could see its invisible
self. [No tmp_at() cleanup is needed here.] */
- if (!mtmp->minvis || perceives(mtmp->data))
- return mtmp;
+ if (!mtmp->minvis || perceives(mtmp->data)) {
+ result = mtmp;
+ goto bhit_done;
+ }
} else if (weapon != ZAPPED_WAND) {
+
/* THROWN_WEAPON, KICKED_WEAPON */
- tmp_at(DISP_END, 0);
+ if (!tethered_weapon)
+ tmp_at(DISP_END, 0);
+
if (cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp))
map_invisible(bhitpos.x, bhitpos.y);
- return mtmp;
+ result = mtmp;
+ goto bhit_done;
} else {
/* ZAPPED_WAND */
(*fhitm)(mtmp, obj);
|| ship_object(obj, bhitpos.x, bhitpos.y,
costly_spot(bhitpos.x, bhitpos.y)))) {
tmp_at(DISP_END, 0);
- return (struct monst *) 0;
+ goto bhit_done; /* result == (struct monst *) 0 */
}
}
if (weapon == ZAPPED_WAND && (IS_DOOR(typ) || typ == SDOOR)) {
if ((bobj = sobj_at(BOULDER, x, y)) != 0) {
if (cansee(x, y))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s hits %s.", The(distant_name(obj, xname)),
an(xname(bobj)));
#else
if (!test_move(x - ddx, y - ddy, ddx, ddy, TEST_MOVE)) {
/* nb: it didn't hit anything directly */
if (cansee(x, y))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s jerks to an abrupt halt.",
The(distant_name(obj, xname))); /* lame */
#else
#endif
range = 0;
} else if (Sokoban && (t = t_at(x, y)) != 0
- && (t->ttyp == PIT || t->ttyp == SPIKED_PIT
- || t->ttyp == HOLE || t->ttyp == TRAPDOOR)) {
+ && (is_pit(t->ttyp) || is_hole(t->ttyp))) {
/* hero falls into the trap, so ball stops */
range = 0;
}
point_blank = FALSE; /* affects passing through iron bars */
}
- if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
+ if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM && !tethered_weapon)
tmp_at(DISP_END, 0);
if (shopdoor)
*/
pay_for_damage("\94j\89ó\82·\82é", FALSE);
- return (struct monst *) 0;
+ bhit_done:
+ if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
+ transient_light_cleanup();
+
+ return result;
}
/* process thrown boomerang, which travels a curving path...
if (!u.ustuck)
u.uswallow = 0;
else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s rips into %s%s", The(fltxt), mon_nam(u.ustuck),
exclam(tmp));
#else
/* Using disintegration from the inside only makes a hole... */
if (tmp == MAGIC_COOKIE)
u.ustuck->mhp = 0;
- if (u.ustuck->mhp < 1)
+ if (DEADMONSTER(u.ustuck))
killed(u.ustuck);
return;
}
/* hit() and miss() need bhitpos to match the target */
bhitpos.x = sx, bhitpos.y = sy;
/* Fireballs only damage when they explode */
- if (type != ZT_SPELL(ZT_FIRE))
+ if (type != ZT_SPELL(ZT_FIRE)) {
range += zap_over_floor(sx, sy, type, &shopdamage, 0);
+ /* zap with fire -> melt ice -> drown monster, so monster
+ found and cached above might not be here any more */
+ mon = m_at(sx, sy);
+ }
if (mon) {
if (type == ZT_SPELL(ZT_FIRE))
break;
if (type >= 0)
mon->mstrategy &= ~STRAT_WAITMASK;
- buzzmonst:
+ buzzmonst:
notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y);
if (zap_hit(find_mac(mon), spell_type)) {
if (mon_reflects(mon, (char *) 0)) {
if (cansee(mon->mx, mon->my)) {
hit(fltxt, mon, exclam(0));
shieldeff(mon->mx, mon->my);
-#if 0 /*JP*/
+#if 0 /*JP:T*/
(void) mon_reflects(mon,
"But it reflects from %s %s!");
#else
hit(fltxt, mon, ".");
*/
hit(fltxt, mon, "\81D");
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s absorbs the deadly %s!", Monnam(mon),
type == ZT_BREATH(ZT_DEATH) ? "blast"
: "ray");
if (tmp == MAGIC_COOKIE) { /* disintegration */
disintegrate_mon(mon, type, fltxt);
- } else if (mon->mhp < 1) {
+ } else if (DEADMONSTER(mon)) {
if (type < 0) {
/* mon has just been killed by another monster */
monkilled(mon, fltxt, AD_RBRE);
} else {
/* some armor was destroyed; no damage done */
if (canseemon(mon))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s %s is disintegrated!",
s_suffix(Monnam(mon)),
distant_name(otmp, xname));
pline("%s\82Í\82 \82È\82½\82É\96½\92\86\82µ\82½\81I", fltxt);
if (Reflecting) {
if (!Blind) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
(void) ureflects("But %s reflects from your %s!",
"it");
#else
uchar rmn;
boolean fireball;
- make_bounce:
+ make_bounce:
bchance = (levl[sx][sy].typ == STONE) ? 10
: (In_mines(&u.uz) && IS_WALL(levl[sx][sy].typ)) ? 20
: 75;
if (type == ZT_SPELL(ZT_FIRE))
explode(sx, sy, type, d(12, 6), 0, EXPL_FIERY);
if (shopdamage)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pay_for_damage(abstype == ZT_FIRE
? "burn away"
: abstype == ZT_COLD
{
struct rm *lev = &levl[x][y];
struct obj *otmp;
+ struct monst *mtmp;
if (!msg)
/*JP
}
if (x == u.ux && y == u.uy)
spoteffects(TRUE); /* possibly drown, notice objects */
+ else if (is_pool(x, y) && (mtmp = m_at(x, y)) != 0)
+ (void) minliquid(mtmp);
}
#define MIN_ICE_TIME 50
{
xchar x, y;
long where = arg->a_long;
+ boolean save_mon_moving = context.mon_moving; /* will be False */
+ /* melt_ice -> minliquid -> mondead|xkilled shouldn't credit/blame hero */
+ context.mon_moving = TRUE; /* hero isn't causing this ice to melt */
y = (xchar) (where & 0xFFFF);
x = (xchar) ((where >> 16) & 0xFFFF);
/* melt_ice does newsym when appropriate */
melt_ice(x, y, "Some ice melts away.");
*/
melt_ice(x, y, "\95X\82ª\8f\82µ\97n\82¯\82½\81D");
+ context.mon_moving = save_mon_moving;
}
/* Burn floor scrolls, evaporate pools, etc... in a single square.
if (is_ice(x, y)) {
melt_ice(x, y, (char *) 0);
} else if (is_pool(x, y)) {
-/*JP
- const char *msgtxt = "You hear hissing gas.";
-*/
- const char *msgtxt = "\82µ\82ã\81[\82Á\82Æ\82¢\82¤\83K\83X\82Ì\89¹\82ð\95·\82¢\82½\81D";
+#if 0 /*JP*/
+ const char *msgtxt = (!Deaf)
+ ? "You hear hissing gas." /* Deaf-aware */
+ : (type >= 0)
+ ? "That seemed remarkably uneventful."
+ : (const char *) 0;
+#else
+ const char *msgtxt = (!Deaf)
+ ? "\82µ\82ã\81[\82Á\82Æ\82¢\82¤\83K\83X\82Ì\89¹\82ð\95·\82¢\82½\81D" /* Deaf-aware */
+ : (type >= 0)
+ ? "\82±\82ê\82Í\8bÁ\82\82Ù\82Ç\82È\82ñ\82Å\82à\82È\82³\82»\82¤\82¾\81D"
+ : (const char *) 0;
+#endif
if (lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP */
if (see_it)
msgtxt = "\82·\82±\82µ\90\85\82ª\8fö\94\82µ\82½\81D";
} else {
rangemod -= 3;
- lev->typ = ROOM;
+ lev->typ = ROOM, lev->flags = 0;
t = maketrap(x, y, PIT);
if (t)
t->tseen = 1;
*/
msgtxt = "\90\85\82ª\8fö\94\82µ\82½\81D";
}
- Norep("%s", msgtxt);
+ if (msgtxt)
+ Norep("%s", msgtxt);
if (lev->typ == ROOM)
newsym(x, y);
} else if (IS_FOUNTAIN(lev->typ)) {
lev->drawbridgemask &= ~DB_UNDER; /* clear lava */
lev->drawbridgemask |= (lava ? DB_FLOOR : DB_ICE);
} else {
- if (!lava)
- lev->icedpool = (lev->typ == POOL) ? ICED_POOL
- : ICED_MOAT;
+ lev->icedpool = lava ? 0
+ : (lev->typ == POOL) ? ICED_POOL
+ : ICED_MOAT;
lev->typ = lava ? ROOM : ICE;
}
bury_objs(x, y);
vision_full_recalc = 1;
} else if (u.utrap && u.utraptype == TT_LAVA) {
if (Passes_walls) {
- u.utrap = 0;
/*JP
You("pass through the now-solid rock.");
*/
You("\82¢\82Ü\8cÅ\82\82È\82Á\82½\82Î\82©\82è\82Ì\90Î\82Ì\92\86\82ð\82·\82è\94²\82¯\82½\81D");
+ reset_utrap(TRUE);
} else {
- u.utrap = rn1(50, 20);
- u.utraptype = TT_INFLOOR;
+ set_utrap(rn1(50, 20), TT_INFLOOR);
/*JP
You("are firmly stuck in the cooling rock.");
*/
Norep("%s\82ª\97n\82¯\82½\81D", defsyms[S_bars].explanation);
if (*in_rooms(x, y, SHOPBASE)) {
/* in case we ever have a shop bounded by bars */
- lev->typ = ROOM;
+ lev->typ = ROOM, lev->flags = 0;
if (see_it)
newsym(x, y);
add_damage(x, y, (type >= 0) ? SHOP_BARS_COST : 0L);
if (type >= 0)
*shopdamage = TRUE;
} else {
- lev->typ = DOOR;
- lev->doormask = D_NODOOR;
+ lev->typ = DOOR, lev->doormask = D_NODOOR;
if (see_it)
newsym(x, y);
}
/* set up zap text for possible door feedback; for exploding wand, we
want "the blast" rather than "your blast" even if hero caused it */
yourzap = (type >= 0 && !exploding_wand_typ);
-#if 0 /*JP*/
+#if 0 /*JP:T*/
zapverb = "blast"; /* breath attack or wand explosion */
#else
zapverb = "\8fÕ\8c\82"; /* breath attack or wand explosion */
#endif
if (!exploding_wand_typ) {
if (abs(type) < ZT_SPELL(0))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
zapverb = "bolt"; /* wand zap */
#else
zapverb = "\8cõ\90ü"; /* wand zap */
(except on rogue level) */
newsym(x, y);
if (see_it)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s %s reveals a secret door.",
yourzap ? "Your" : "The", zapverb);
#else
hear_txt = "\83s\83L\83s\83L\82Æ\82¢\82¤\89¹\82ð\95·\82¢\82½\81D";
break;
default:
- def_case:
+ def_case:
if (exploding_wand_typ > 0) {
/* Magical explosion from misc exploding wand */
if (exploding_wand_typ == WAN_STRIKING) {
*/
pline_The("\94à\82Í\96³\8f\9d\82¾\81D");
else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline_The("door absorbs %s %s!", yourzap ? "your" : "the",
zapverb);
#else
{ "\82Î\82ç\82Î\82ç\82É\89ó\82ê\82Ä\94\9a\94\82µ\82½", "\82Î\82ç\82Î\82ç\82É\89ó\82ê\82Ä\94\9a\94\82µ\82½", "\8fñ\82Ì\94\9a\94\82Å" },
};
-void
-destroy_item(osym, dmgtyp)
-register int osym, dmgtyp;
+/* guts of destroy_item(), which ought to be called maybe_destroy_items();
+ caller must decide whether obj is eligible */
+STATIC_OVL void
+destroy_one_item(obj, osym, dmgtyp)
+struct obj *obj;
+int osym, dmgtyp;
{
- register struct obj *obj, *obj2;
- int dmg, xresist, skip;
long i, cnt, quan;
- int dindx;
+ int dmg, xresist, skip, dindx;
const char *mult;
boolean physical_damage;
- for (obj = invent; obj; obj = obj2) {
- obj2 = obj->nobj;
- physical_damage = FALSE;
- if (obj->oclass != osym)
- continue; /* test only objs of type osym */
- if (obj->oartifact)
- continue; /* don't destroy artifacts */
- if (obj->in_use && obj->quan == 1L)
- continue; /* not available */
- xresist = skip = 0;
- /* lint suppression */
- dmg = dindx = 0;
- quan = 0L;
+ physical_damage = FALSE;
+ xresist = skip = 0;
+ /* lint suppression */
+ dmg = dindx = 0;
+ quan = 0L;
- switch (dmgtyp) {
- case AD_COLD:
- if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
- quan = obj->quan;
- dindx = 0;
- dmg = rnd(4);
- } else
- skip++;
- break;
- case AD_FIRE:
- xresist = (Fire_resistance && obj->oclass != POTION_CLASS
- && obj->otyp != GLOB_OF_GREEN_SLIME);
-
- if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
- skip++;
- if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
- skip++;
- if (!Blind)
+ switch (dmgtyp) {
+ case AD_COLD:
+ if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
+ quan = obj->quan;
+ dindx = 0;
+ dmg = rnd(4);
+ } else
+ skip++;
+ break;
+ case AD_FIRE:
+ xresist = (Fire_resistance && obj->oclass != POTION_CLASS
+ && obj->otyp != GLOB_OF_GREEN_SLIME);
+ if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
+ skip++;
+ if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
+ skip++;
+ if (!Blind)
#if 0 /*JP:T*/
- pline("%s glows a strange %s, but remains intact.",
- The(xname(obj)), hcolor("dark red"));
+ pline("%s glows a strange %s, but remains intact.",
+ The(xname(obj)), hcolor("dark red"));
#else
- pline("%s\82Í\8aï\96\82É%s\8bP\82¢\82½\82ª\89½\82à\95Ï\89»\82µ\82È\82©\82Á\82½\81D",
- xname(obj), jconj_adj(hcolor("\88Ã\8a\8c\90F\82Ì")));
+ pline("%s\82Í\8aï\96\82É%s\8bP\82¢\82½\82ª\89½\82à\95Ï\89»\82µ\82È\82©\82Á\82½\81D",
+ xname(obj), hcolor_adv("\88Ã\8a\8c\90F\82Ì"));
#endif
-
+ }
+ quan = obj->quan;
+ switch (osym) {
+ case POTION_CLASS:
+ dindx = (obj->otyp != POT_OIL) ? 1 : 2;
+ dmg = rnd(6);
+ break;
+ case SCROLL_CLASS:
+ dindx = 3;
+ dmg = 1;
+ break;
+ case SPBOOK_CLASS:
+ dindx = 4;
+ dmg = 1;
+ break;
+ case FOOD_CLASS:
+ if (obj->otyp == GLOB_OF_GREEN_SLIME) {
+ dindx = 1; /* boil and explode */
+ dmg = (obj->owt + 19) / 20;
+ } else {
+ skip++;
}
- quan = obj->quan;
- switch (osym) {
- case POTION_CLASS:
- dindx = (obj->otyp != POT_OIL) ? 1 : 2;
- dmg = rnd(6);
- break;
- case SCROLL_CLASS:
- dindx = 3;
- dmg = 1;
- break;
- case SPBOOK_CLASS:
- dindx = 4;
- dmg = 1;
- break;
- case FOOD_CLASS:
- if (obj->otyp == GLOB_OF_GREEN_SLIME) {
- dindx = 1; /* boil and explode */
- dmg = (obj->owt + 19) / 20;
- } else {
- skip++;
- }
- break;
- default:
+ break;
+ default:
+ skip++;
+ break;
+ }
+ break;
+ case AD_ELEC:
+ xresist = (Shock_resistance && obj->oclass != RING_CLASS);
+ quan = obj->quan;
+ switch (osym) {
+ case RING_CLASS:
+ if (obj->otyp == RIN_SHOCK_RESISTANCE) {
skip++;
break;
}
+ dindx = 5;
+ dmg = 0;
break;
- case AD_ELEC:
- xresist = (Shock_resistance && obj->oclass != RING_CLASS);
- quan = obj->quan;
- switch (osym) {
- case RING_CLASS:
- if (obj->otyp == RIN_SHOCK_RESISTANCE) {
- skip++;
- break;
- }
- dindx = 5;
- dmg = 0;
- break;
- case WAND_CLASS:
- if (obj->otyp == WAN_LIGHTNING) {
- skip++;
- break;
- }
-#if 0
- if (obj == current_wand) { skip++; break; }
-#endif
- dindx = 6;
- dmg = rnd(10);
- break;
- default:
+ case WAND_CLASS:
+ if (obj->otyp == WAN_LIGHTNING) {
skip++;
break;
}
+#if 0
+ if (obj == current_wand) { skip++; break; }
+#endif
+ dindx = 6;
+ dmg = rnd(10);
break;
default:
skip++;
break;
}
- if (!skip) {
- if (obj->in_use)
- --quan; /* one will be used up elsewhere */
- for (i = cnt = 0L; i < quan; i++)
- if (!rn2(3))
- cnt++;
+ break;
+ default:
+ skip++;
+ break;
+ }
- if (!cnt)
- continue;
-#if 0 /*JP*/
- mult = (cnt == 1L)
- ? (quan == 1L) ? "Your" /* 1 of 1 */
- : "One of your" /* 1 of N */
- : (cnt < quan) ? "Some of your" /* n of N */
- : (quan == 2L) ? "Both of your" /* 2 of 2 */
- : "All of your"; /* N of N */
-#else
- mult = (cnt == quan)
- ? ""
- : (cnt == 1L) ? "\82Ì\82Ð\82Æ\82Â" : "\82Ì\82¢\82\82Â\82©";
-#endif
-#if 0 /*JP*/
- pline("%s %s %s!", mult, xname(obj),
- destroy_strings[dindx][(cnt > 1L)]);
+ if (!skip) {
+ if (obj->in_use)
+ --quan; /* one will be used up elsewhere */
+ for (i = cnt = 0L; i < quan; i++)
+ if (!rn2(3))
+ cnt++;
+
+ if (!cnt)
+ return;
+#if 0 /*JP:T*/
+ mult = (cnt == 1L)
+ ? ((quan == 1L) ? "Your" /* 1 of 1 */
+ : "One of your") /* 1 of N */
+ : ((cnt < quan) ? "Some of your" /* n of N */
+ : (quan == 2L) ? "Both of your" /* 2 of 2 */
+ : "All of your"); /* N of N */
+ pline("%s %s %s!", mult, xname(obj),
+ destroy_strings[dindx][(cnt > 1L)]);
#else
- pline("\82 \82È\82½\82Ì%s%s\82Í%s\81I", xname(obj), mult,
- destroy_strings[dindx][(cnt > 1L)]);
+ mult = (cnt == 1L)
+ ? ((quan == 1L) ? "" /* 1 of 1 */
+ : "\82Ì\82Ð\82Æ\82Â") /* 1 of N */
+ : ((cnt < quan) ? "\82Ì\82¢\82\82Â\82©" /* n of N */
+ : "\82Ì\91S\82Ä"); /* N of N */
+ pline("\82 \82È\82½\82Ì%s%s\82Í%s\81I", xname(obj), mult,
+ destroy_strings[dindx][(cnt > 1L)]);
#endif
- if (osym == POTION_CLASS && dmgtyp != AD_COLD) {
- if (!breathless(youmonst.data) || haseyes(youmonst.data))
- potionbreathe(obj);
- }
- if (obj->owornmask) {
- if (obj->owornmask & W_RING) /* ring being worn */
- Ring_gone(obj);
- else
- setnotworn(obj);
- }
- if (obj == current_wand)
- current_wand = 0; /* destroyed */
- for (i = 0; i < cnt; i++)
- useup(obj);
- if (dmg) {
- if (xresist)
+ if (osym == POTION_CLASS && dmgtyp != AD_COLD) {
+ if (!breathless(youmonst.data) || haseyes(youmonst.data))
+ potionbreathe(obj);
+ }
+ if (obj->owornmask) {
+ if (obj->owornmask & W_RING) /* ring being worn */
+ Ring_gone(obj);
+ else
+ setnotworn(obj);
+ }
+ if (obj == current_wand)
+ current_wand = 0; /* destroyed */
+ for (i = 0; i < cnt; i++)
+ useup(obj);
+ if (dmg) {
+ if (xresist) {
/*JP
- You("aren't hurt!");
+ You("aren't hurt!");
*/
- You("\8f\9d\82Â\82©\82È\82¢\81I");
- else {
- const char *how = destroy_strings[dindx][2];
- boolean one = (cnt == 1L);
+ You("\8f\9d\82Â\82©\82È\82¢\81I");
+ } else {
+ const char *how = destroy_strings[dindx][2];
+ boolean one = (cnt == 1L);
- if (dmgtyp == AD_FIRE && osym == FOOD_CLASS)
+ if (dmgtyp == AD_FIRE && osym == FOOD_CLASS)
/*JP
- how = "exploding glob of slime";
+ how = "exploding glob of slime";
*/
- how = "\83X\83\89\83C\83\80\82Ì\82Ë\82Î\82Ë\82Î\82Ì\94\9a\94\82Å";
- if (physical_damage)
- dmg = Maybe_Half_Phys(dmg);
- losehp(dmg, one ? how : (const char *) makeplural(how),
- one ? KILLED_BY_AN : KILLED_BY);
- exercise(A_STR, FALSE);
- }
+ how = "\83X\83\89\83C\83\80\82Ì\82Ë\82Î\82Ë\82Î\82Ì\94\9a\94\82Å";
+ if (physical_damage)
+ dmg = Maybe_Half_Phys(dmg);
+ losehp(dmg, one ? how : (const char *) makeplural(how),
+ one ? KILLED_BY_AN : KILLED_BY);
+ exercise(A_STR, FALSE);
}
}
}
+}
+
+/* target items of specified class for possible destruction */
+void
+destroy_item(osym, dmgtyp)
+int osym, dmgtyp;
+{
+ register struct obj *obj;
+ int i, deferral_indx = 0;
+ /* 1+52+1: try to handle a full inventory; it doesn't matter if
+ inventory actually has more, even if everything should be deferred */
+ unsigned short deferrals[1 + 52 + 1]; /* +1: gold, overflow */
+
+ (void) memset((genericptr_t) deferrals, 0, sizeof deferrals);
+ /*
+ * Sometimes destroying an item can change inventory aside from
+ * the item itself (cited case was a potion of unholy water; when
+ * boiled, potionbreathe() caused hero to transform into were-beast
+ * form and that resulted in dropping or destroying some worn armor).
+ *
+ * Unlike other uses of the object bybass mechanism, destroy_item()
+ * can be called multiple times for the same event. So we have to
+ * explicitly clear it before each use and hope no other section of
+ * code expects it to retain previous value.
+ *
+ * Destruction of a ring of levitation or form change which pushes
+ * off levitation boots could drop hero onto a fire trap that
+ * could destroy other items and we'll get called recursively. Or
+ * onto a trap which transports hero elsewhere, which won't disrupt
+ * traversal but could yield message sequencing issues. So we
+ * defer handling such things until after rest of inventory has
+ * been processed. If some other combination of items and events
+ * triggers a recursive call, rest of inventory after the triggering
+ * item will be skipped by the outer call since the inner one will
+ * have set the bypass bits of the whole list.
+ *
+ * [Unfortunately, death while poly'd into flyer and subsequent
+ * rehumanization could also drop hero onto a trap, and there's no
+ * straightforward way to defer that. Things could be improved by
+ * redoing this to use two passes, first to collect a list or array
+ * of o_id and quantity of what is targetted for destruction,
+ * second pass to handle the destruction.]
+ */
+ bypass_objlist(invent, FALSE); /* clear bypass bit for invent */
+
+ while ((obj = nxt_unbypassed_obj(invent)) != 0) {
+ if (obj->oclass != osym)
+ continue; /* test only objs of type osym */
+ if (obj->oartifact)
+ continue; /* don't destroy artifacts */
+ if (obj->in_use && obj->quan == 1L)
+ continue; /* not available */
+
+ /* if loss of this item might dump us onto a trap, hold off
+ until later because potential recursive destroy_item() will
+ result in setting bypass bits on whole chain--we would skip
+ the rest as already processed once control returns here */
+ if (deferral_indx < SIZE(deferrals)
+ && ((obj->owornmask != 0L
+ && (objects[obj->otyp].oc_oprop == LEVITATION
+ || objects[obj->otyp].oc_oprop == FLYING))
+ /* destroyed wands and potions of polymorph don't trigger
+ polymorph so don't need to be deferred */
+ || (obj->otyp == POT_WATER && u.ulycn >= LOW_PM
+ && (Upolyd ? obj->blessed : obj->cursed)))) {
+ deferrals[deferral_indx++] = obj->o_id;
+ continue;
+ }
+ /* obj is eligible; maybe destroy it */
+ destroy_one_item(obj, osym, dmgtyp);
+ }
+ /* if we saved some items for later (most likely just a worn ring
+ of levitation) and they're still in inventory, handle them now */
+ for (i = 0; i < deferral_indx; ++i) {
+ /* note: obj->nobj is only referenced when obj is skipped;
+ having obj be dropped or destroyed won't affect traversal */
+ for (obj = invent; obj; obj = obj->nobj)
+ if (obj->o_id == deferrals[i]) {
+ destroy_one_item(obj, osym, dmgtyp);
+ break;
+ }
+ }
return;
}
struct monst *mtmp;
int osym, dmgtyp;
{
- struct obj *obj, *obj2;
+ struct obj *obj;
int skip, tmp = 0;
long i, cnt, quan;
int dindx;
}
vis = canseemon(mtmp);
- for (obj = mtmp->minvent; obj; obj = obj2) {
- obj2 = obj->nobj;
+
+ /* see destroy_item(); object destruction could disrupt inventory list */
+ bypass_objlist(mtmp->minvent, FALSE); /* clear bypass bit for minvent */
+
+ while ((obj = nxt_unbypassed_obj(mtmp->minvent)) != 0) {
if (obj->oclass != osym)
continue; /* test only objs of type osym */
skip = 0;
if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
skip++;
if (vis)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s glows a strange %s, but remains intact.",
The(distant_name(obj, xname)), hcolor("dark red"));
#else
pline("%s\82Í\8aï\96\82É%s\8bP\82¢\82½\82ª\89½\82à\95Ï\89»\82µ\82È\82©\82Á\82½\81D",
- The(distant_name(obj, xname)), jconj_adj(hcolor("\88Ã\8a\8c\90F\82Ì")));
+ The(distant_name(obj, xname)), hcolor_adv("\88Ã\8a\8c\90F\82Ì"));
#endif
}
quan = obj->quan;
if (!cnt)
continue;
if (vis)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
pline("%s%s %s!",
(cnt == obj->quan) ? "" : (cnt > 1L) ? "Some of "
: "One of ",
int resisted;
int alev, dlev;
+ /* fake players always pass resistance test against Conflict
+ (this doesn't guarantee that they're never affected by it) */
+ if (oclass == RING_CLASS && !damage && !tell && is_mplayer(mtmp->data))
+ return 1;
+
/* attack level */
switch (oclass) {
case WAND_CLASS:
if (damage) {
mtmp->mhp -= damage;
- if (mtmp->mhp < 1) {
+ if (DEADMONSTER(mtmp)) {
if (m_using)
monkilled(mtmp, "", AD_RBRE);
else
void
makewish()
{
- static char buf[BUFSZ] = DUMMY;
+ char buf[BUFSZ] = DUMMY;
char promptbuf[BUFSZ];
struct obj *otmp, nothing;
int tries = 0;
You("may wish for an object.");
*/
You("\96]\82Ý\82Ì\82à\82Ì\82ð\8eè\82É\93ü\82ê\82ç\82ê\82é\81D");
-retry:
+ retry:
/*JP
Strcpy(promptbuf, "For what do you wish");
*/
buf[0] = '\0';
} else if (!strcmpi(buf, "help")) {
wishcmdassist(MAXWISHTRY - tries);
+ buf[0] = '\0'; /* for EDIT_GETLIN */
goto retry;
}
/*