Merge branch 'NetHack-3.6.2'

This commit is contained in:
nhmall
2018-12-07 23:37:58 -05:00
32 changed files with 397 additions and 267 deletions

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 botl.c $NHDT-Date: 1527042178 2018/05/23 02:22:58 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.101 $ */
/* NetHack 3.6 botl.c $NHDT-Date: 1544229439 2018/12/08 00:37:19 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.129 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2006. */
/* NetHack may be freely redistributed. See license for details. */
@@ -732,17 +732,15 @@ boolean *valsetlist;
* NNNN = the glyph portion
* 25 = the gold amount
*
* Setting 'chg = 2' is enough to render the field properly, but
* not to honor an initial highlight, so force 'update_all = TRUE'.
*/
if (fld == BL_GOLD) {
if (context.rndencode != oldrndencode) {
chg = 2;
oldrndencode = context.rndencode;
}
if (oldgoldsym != showsyms[COIN_CLASS + SYM_OFF_O]) {
chg = 2;
oldgoldsym = showsyms[COIN_CLASS + SYM_OFF_O];
}
if (fld == BL_GOLD
&& (context.rndencode != oldrndencode
|| showsyms[COIN_CLASS + SYM_OFF_O] != oldgoldsym)) {
update_all = TRUE; /* chg = 2; */
oldrndencode = context.rndencode;
oldgoldsym = showsyms[COIN_CLASS + SYM_OFF_O];
}
reset = FALSE;

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 cmd.c $NHDT-Date: 1543972186 2018/12/05 01:09:46 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.313 $ */
/* NetHack 3.6 cmd.c $NHDT-Date: 1544050555 2018/12/05 22:55:55 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.314 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
@@ -2740,7 +2740,12 @@ int final;
}
if (Polymorph_control)
you_have("polymorph control", from_what(POLYMORPH_CONTROL));
if (Upolyd && u.umonnum != u.ulycn) {
if (Upolyd && u.umonnum != u.ulycn
/* if we've died from turning into slime, we're polymorphed
right now but don't want to list it as a temporary attribute
[we need a more reliable way to detect this situation] */
&& !(final == ENL_GAMEOVERDEAD
&& u.umonnum == PM_GREEN_SLIME && !Unchanging)) {
/* foreign shape (except were-form which is handled below) */
Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname));
if (wizard)

View File

@@ -57,7 +57,9 @@ static const char *const gloc_descr[NUM_GLOCS][4] = {
{ "any unexplored areas", "unexplored area", "unexplored location",
"unexplored locations" },
{ "anything interesting", "interesting thing", "anything interesting",
"anything interesting" }
"anything interesting" },
{ "any valid locations", "valid location", "valid location",
"valid locations" }
};
static const char *const gloc_filtertxt[NUM_GFILTER] = {
@@ -378,6 +380,10 @@ int x, y, gloc;
|| IS_UNEXPLORED_LOC(x - 1, y)
|| IS_UNEXPLORED_LOC(x, y + 1)
|| IS_UNEXPLORED_LOC(x, y - 1)));
case GLOC_VALID:
if (getpos_getvalid)
return (getpos_getvalid(x,y));
/*FALLTHRU*/
case GLOC_INTERESTING:
return gather_locs_interesting(x,y, GLOC_DOOR)
|| !(glyph_is_cmap(glyph)
@@ -395,8 +401,6 @@ int x, y, gloc;
|| glyph_to_cmap(glyph) == S_darkroom
|| glyph_to_cmap(glyph) == S_corr
|| glyph_to_cmap(glyph) == S_litcorr));
case GLOC_VALID:
return (getpos_getvalid && getpos_getvalid(x,y));
}
/*NOTREACHED*/
return FALSE;

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 end.c $NHDT-Date: 1542798619 2018/11/21 11:10:19 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.152 $ */
/* NetHack 3.6 end.c $NHDT-Date: 1544003110 2018/12/05 09:45:10 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.156 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2012. */
/* NetHack may be freely redistributed. See license for details. */
@@ -1184,7 +1184,12 @@ int how;
u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
else if (how == STONING)
u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
else if (how == TURNED_SLIME)
else if (how == TURNED_SLIME
/* it's possible to turn into slime even though green slimes
have been genocided: genocide could occur after hero is
already infected or hero could eat a glob of one created
before genocide; don't try to arise as one if they're gone */
&& !(mvitals[PM_GREEN_SLIME].mvflags & G_GENOD))
u.ugrave_arise = PM_GREEN_SLIME;
if (how == QUIT) {
@@ -1310,11 +1315,14 @@ int how;
}
}
if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) {
if (u.ugrave_arise >= LOW_PM) {
/* give this feedback even if bones aren't going to be created,
so that its presence or absence doesn't tip off the player to
new bones or their lack; it might be a lie if makemon fails */
Your("body rises from the dead as %s...",
Your("%s as %s...",
(u.ugrave_arise != PM_GREEN_SLIME)
? "body rises from the dead"
: "revenant persists",
an(mons[u.ugrave_arise].mname));
display_nhwindow(WIN_MESSAGE, FALSE);
}

View File

@@ -18,15 +18,15 @@ STATIC_DCL boolean FDECL(uncommon, (int));
STATIC_DCL int FDECL(align_shift, (struct permonst *));
STATIC_DCL boolean FDECL(mk_gen_ok, (int, int, int));
STATIC_DCL boolean FDECL(wrong_elem_type, (struct permonst *));
STATIC_DCL void FDECL(m_initgrp, (struct monst *, int, int, int));
STATIC_DCL void FDECL(m_initgrp, (struct monst *, int, int, int, int));
STATIC_DCL void FDECL(m_initthrow, (struct monst *, int, int));
STATIC_DCL void FDECL(m_initweap, (struct monst *));
STATIC_DCL void FDECL(m_initinv, (struct monst *));
STATIC_DCL boolean FDECL(makemon_rnd_goodpos, (struct monst *,
unsigned, coord *));
#define m_initsgrp(mtmp, x, y) m_initgrp(mtmp, x, y, 3)
#define m_initlgrp(mtmp, x, y) m_initgrp(mtmp, x, y, 10)
#define m_initsgrp(mtmp, x, y, mmf) m_initgrp(mtmp, x, y, 3, mmf)
#define m_initlgrp(mtmp, x, y, mmf) m_initgrp(mtmp, x, y, 10, mmf)
#define toostrong(monindx, lev) (mons[monindx].difficulty > lev)
#define tooweak(monindx, lev) (mons[monindx].difficulty < lev)
@@ -76,9 +76,9 @@ struct permonst *ptr;
/* make a group just like mtmp */
STATIC_OVL void
m_initgrp(mtmp, x, y, n)
register struct monst *mtmp;
register int x, y, n;
m_initgrp(mtmp, x, y, n, mmflags)
struct monst *mtmp;
int x, y, n, mmflags;
{
coord mm;
register int cnt = rnd(n);
@@ -129,7 +129,7 @@ register int x, y, n;
* smaller group.
*/
if (enexto(&mm, mm.x, mm.y, mtmp->data)) {
mon = makemon(mtmp->data, mm.x, mm.y, NO_MM_FLAGS);
mon = makemon(mtmp->data, mm.x, mm.y, (mmflags | MM_NOGRP));
if (mon) {
mon->mpeaceful = FALSE;
mon->mavenge = 0;
@@ -1193,7 +1193,8 @@ int mmflags;
newemin(mtmp);
if (mmflags & MM_EDOG)
newedog(mtmp);
if (mmflags & MM_ASLEEP)
mtmp->msleeping = 1;
mtmp->nmon = fmon;
fmon = mtmp;
mtmp->m_id = context.ident++;
@@ -1364,14 +1365,14 @@ int mmflags;
: eminp->renegade;
}
set_malign(mtmp); /* having finished peaceful changes */
if (anymon) {
if (anymon && !(mmflags & MM_NOGRP)) {
if ((ptr->geno & G_SGROUP) && rn2(2)) {
m_initsgrp(mtmp, mtmp->mx, mtmp->my);
m_initsgrp(mtmp, mtmp->mx, mtmp->my, mmflags);
} else if (ptr->geno & G_LGROUP) {
if (rn2(3))
m_initlgrp(mtmp, mtmp->mx, mtmp->my);
m_initlgrp(mtmp, mtmp->mx, mtmp->my, mmflags);
else
m_initsgrp(mtmp, mtmp->mx, mtmp->my);
m_initsgrp(mtmp, mtmp->mx, mtmp->my, mmflags);
}
}

View File

@@ -806,7 +806,7 @@ skip0:
if (u.uhave.amulet || !rn2(3)) {
x = somex(croom);
y = somey(croom);
tmonst = makemon((struct permonst *) 0, x, y, NO_MM_FLAGS);
tmonst = makemon((struct permonst *) 0, x, y, (MM_NOGRP | MM_ASLEEP));
if (tmonst && tmonst->data == &mons[PM_GIANT_SPIDER]
&& !occupied(x, y))
(void) maketrap(x, y, WEB);

View File

@@ -341,7 +341,7 @@ struct mkroom *sroom;
: (type == ANTHOLE)
? antholemon()
: (struct permonst *) 0,
sx, sy, NO_MM_FLAGS);
sx, sy, MM_ASLEEP);
if (mon) {
mon->msleeping = 1;
if (type == COURT && mon->mpeaceful) {

View File

@@ -14,7 +14,6 @@ STATIC_DCL int FDECL(disturb, (struct monst *));
STATIC_DCL void FDECL(release_hero, (struct monst *));
STATIC_DCL void FDECL(distfleeck, (struct monst *, int *, int *, int *));
STATIC_DCL int FDECL(m_arrival, (struct monst *));
STATIC_DCL int FDECL(count_webbing_walls, (XCHAR_P, XCHAR_P));
STATIC_DCL boolean FDECL(stuff_prevents_passage, (struct monst *));
STATIC_DCL int FDECL(vamp_shift, (struct monst *, struct permonst *,
BOOLEAN_P));
@@ -759,24 +758,6 @@ xchar nix,niy;
return FALSE;
}
/* returns the number of walls in the four cardinal directions that could
hold up a web */
STATIC_OVL int
count_webbing_walls(x, y)
xchar x, y;
{
#define holds_up_web(X, Y) ((!isok((X), (Y)) \
|| IS_ROCK(levl[X][Y].typ) \
|| (levl[X][Y].typ == STAIRS \
&& (X) == xupstair && (Y) == yupstair) \
|| (levl[X][Y].typ == LADDER \
&& (X) == xupladder && (Y) == yupladder) \
|| levl[X][Y].typ == IRONBARS) ? 1 : 0)
return (holds_up_web(x, y - 1) + holds_up_web(x + 1, y)
+ holds_up_web(x, y + 1) + holds_up_web(x - 1, y));
#undef holds_up_web
}
/* Return values:
* 0: did not move, but can still attack and do other stuff.
* 1: moved, possibly can attack.
@@ -1472,27 +1453,6 @@ postmov:
}
}
/* maybe spin a web -- this needs work; if the spider is far away,
it might spin a lot of webs before hero encounters it */
if (webmaker(ptr) && !mtmp->mspec_used && !t_at(mtmp->mx, mtmp->my)) {
struct trap *trap;
int prob = ((ptr == &mons[PM_GIANT_SPIDER]) ? 15 : 5)
* (count_webbing_walls(mtmp->mx, mtmp->my) + 1);
if (rn2(1000) < prob
&& (trap = maketrap(mtmp->mx, mtmp->my, WEB)) != 0) {
mtmp->mspec_used = d(4, 4); /* 4..16 */
if (cansee(mtmp->mx, mtmp->my)) {
char mbuf[BUFSZ];
Strcpy(mbuf,
canspotmon(mtmp) ? y_monnam(mtmp) : something);
pline("%s spins a web.", upstart(mbuf));
trap->tseen = 1;
}
}
}
if (hides_under(ptr) || ptr->mlet == S_EEL) {
/* Always set--or reset--mundetected if it's already hidden
(just in case the object it was hiding under went away);

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 options.c $NHDT-Date: 1543972192 2018/12/05 01:09:52 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.335 $ */
/* NetHack 3.6 options.c $NHDT-Date: 1544174413 2018/12/07 09:20:13 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.339 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Michael Allison, 2008. */
/* NetHack may be freely redistributed. See license for details. */
@@ -106,7 +106,7 @@ static struct Bool_Opt {
#endif
{ "clicklook", &iflags.clicklook, FALSE, SET_IN_GAME },
{ "cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME },
#if defined(MICRO) || defined(WIN32)
#if defined(MICRO) || defined(WIN32) || defined(CURSES_GRAPHICS)
{ "color", &iflags.wc_color, TRUE, SET_IN_GAME }, /*WC*/
#else /* systems that support multiple terminals, many monochrome */
{ "color", &iflags.wc_color, FALSE, SET_IN_GAME }, /*WC*/
@@ -4283,7 +4283,7 @@ static struct other_opts {
int NDECL((*othr_count_func));
} othropt[] = {
{ "autopickup exceptions", SET_IN_GAME, OPT_OTHER_APEXC, count_apes },
{ "menucolors", SET_IN_GAME, OPT_OTHER_MENUCOLOR, count_menucolors },
{ "menu colors", SET_IN_GAME, OPT_OTHER_MENUCOLOR, count_menucolors },
{ "message types", SET_IN_GAME, OPT_OTHER_MSGTYPE, msgtype_count },
#ifdef STATUS_HILITES
{ "status hilite rules", SET_IN_GAME, OPT_OTHER_STATHILITE,
@@ -4457,7 +4457,7 @@ doset() /* changing options via menu by Per Liboriussen */
}
#endif
} else if (opt_indx == OPT_OTHER_MENUCOLOR) {
(void) special_handling("menucolors", setinitial,
(void) special_handling("menu_colors", setinitial,
fromfile);
} else if (opt_indx == OPT_OTHER_MSGTYPE) {
(void) special_handling("msgtype", setinitial, fromfile);
@@ -5036,7 +5036,7 @@ boolean setinitial, setfromfile;
if (pick_cnt >= 0)
goto msgtypes_again;
}
} else if (!strcmp("menucolors", optname)) {
} else if (!strcmp("menu_colors", optname)) {
int opt_idx, nmc, mcclr, mcattr;
char mcbuf[BUFSZ] = DUMMY;
@@ -5044,11 +5044,15 @@ boolean setinitial, setfromfile;
nmc = count_menucolors();
opt_idx = handle_add_list_remove("menucolor", nmc);
if (opt_idx == 3) { /* done */
menucolors_done:
if (nmc > 0 && !iflags.use_menu_color)
pline(
"To have menu colors become active, toggle 'menucolors' option to True.");
return TRUE;
} else if (opt_idx == 0) { /* add new */
getlin("What new menucolor pattern?", mcbuf);
if (*mcbuf == '\033')
return TRUE;
goto menucolors_done;
if (*mcbuf
&& test_regex_pattern(mcbuf, (const char *)0)
&& (mcclr = query_color((char *) 0)) != -1

View File

@@ -187,6 +187,8 @@ struct obj **obj_p;
otmp->quan = 2L; /* to force pluralization */
else if (otmp->otyp == SLIME_MOLD)
otmp->spe = context.current_fruit; /* give it a type */
else if (otmp->otyp == LEASH)
otmp->leashmon = 0;
if (mtmp && has_mcorpsenm(mtmp)) /* mimic as corpse/statue */
otmp->corpsenm = MCORPSENM(mtmp);
else if (otmp->otyp == CORPSE && glyph_is_body(glyph))

View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 timeout.c $NHDT-Date: 1541902953 2018/11/11 02:22:33 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.84 $ */
/* NetHack 3.6 timeout.c $NHDT-Date: 1544050558 2018/12/05 22:55:58 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.88 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2018. */
/* NetHack may be freely redistributed. See license for details. */
@@ -11,6 +11,7 @@ STATIC_DCL void NDECL(vomiting_dialogue);
STATIC_DCL void NDECL(choke_dialogue);
STATIC_DCL void NDECL(levitation_dialogue);
STATIC_DCL void NDECL(slime_dialogue);
STATIC_DCL void FDECL(slimed_to_death, (struct kinfo *));
STATIC_DCL void NDECL(slip_or_trip);
STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *));
STATIC_DCL void FDECL(lantern_message, (struct obj *));
@@ -310,6 +311,13 @@ slime_dialogue()
{
register long i = (Slimed & TIMEOUT) / 2L;
if (i == 1L) {
/* display as green slime during "You have become green slime."
but don't worry about not being able to see self; if already
mimicking something else at the time, implicitly be revealed */
youmonst.m_ap_type = M_AP_MONSTER;
youmonst.mappearance = PM_GREEN_SLIME;
}
if (((Slimed & TIMEOUT) % 2L) && i >= 0L && i < SIZE(slime_texts)) {
char buf[BUFSZ];
@@ -357,6 +365,62 @@ burn_away_slime()
}
}
/* countdown timer for turning into green slime has run out; kill our hero */
STATIC_OVL void
slimed_to_death(kptr)
struct kinfo *kptr;
{
uchar save_mvflags;
/* redundant: polymon() cures sliming when polying into green slime */
if (Upolyd && youmonst.data == &mons[PM_GREEN_SLIME]) {
dealloc_killer(kptr);
return;
}
/* more sure killer reason is set up */
if (kptr && kptr->name[0]) {
killer.format = kptr->format;
Strcpy(killer.name, kptr->name);
} else {
killer.format = NO_KILLER_PREFIX;
Strcpy(killer.name, "turned into green slime");
}
dealloc_killer(kptr);
/*
* Polymorph into a green slime, which might destroy some worn armor
* (potentially affecting bones) and dismount from steed.
* Can't be Unchanging; wouldn't have turned into slime if we were.
* Despite lack of Unchanging, neither done() nor savelife() calls
* rehumanize() if hero dies while polymorphed.
* polymon() undoes the slime countdown's mimick-green-slime hack
* but does not perform polyself()'s light source bookkeeping.
* No longer need to manually increment uconduct.polyselfs to reflect
* [formerly implicit] change of form; polymon() takes care of that.
* Temporarily ungenocide if necessary.
*/
if (emits_light(youmonst.data))
del_light_source(LS_MONSTER, monst_to_any(&youmonst));
save_mvflags = mvitals[PM_GREEN_SLIME].mvflags;
mvitals[PM_GREEN_SLIME].mvflags = save_mvflags & ~G_GENOD;
(void) polymon(PM_GREEN_SLIME);
mvitals[PM_GREEN_SLIME].mvflags = save_mvflags;
done(TURNED_SLIME);
/* life-saved; even so, hero still has turned into green slime;
player may have genocided green slimes after being infected */
if ((mvitals[PM_GREEN_SLIME].mvflags & G_GENOD) != 0) {
killer.format = KILLED_BY;
Strcpy(killer.name, "slimicide");
/* immediately follows "OK, so you don't die." */
pline("Yes, you do. Green slime has been genocided...");
done(GENOCIDED);
/* could be life-saved again (only in explore or wizard mode)
but green slimes are gone; just stay in current form */
}
return;
}
/* Intrinsic Passes_walls is temporary when your god is trying to fix
all troubles and then TROUBLE_STUCK_IN_WALL calls safe_teleds() but
it can't find anywhere to place you. If that happens you get a small
@@ -469,17 +533,7 @@ nh_timeout()
done(STONING);
break;
case SLIMED:
if (kptr && kptr->name[0]) {
killer.format = kptr->format;
Strcpy(killer.name, kptr->name);
} else {
killer.format = NO_KILLER_PREFIX;
Strcpy(killer.name, "turned into green slime");
}
dealloc_killer(kptr);
/* involuntarily break "never changed form" conduct */
u.uconduct.polyselfs++;
done(TURNED_SLIME);
slimed_to_death(kptr); /* done(TURNED_SLIME) */
break;
case VOMITING:
make_vomiting(0L, TRUE);

358
src/zap.c
View File

@@ -1,4 +1,4 @@
/* NetHack 3.6 zap.c $NHDT-Date: 1543744276 2018/12/02 09:51:16 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.299 $ */
/* NetHack 3.6 zap.c $NHDT-Date: 1544230271 2018/12/08 00:51:11 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.301 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2013. */
/* NetHack may be freely redistributed. See license for details. */
@@ -28,11 +28,12 @@ STATIC_DCL void FDECL(zhitu, (int, int, const char *, XCHAR_P, XCHAR_P));
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)
@@ -1846,6 +1847,10 @@ struct obj *obj, *otmp;
* 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.
@@ -4715,160 +4720,234 @@ const char *const destroy_strings[][3] = {
{ "breaks apart and explodes", "", "exploding wand" },
};
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)
pline("%s glows a strange %s, but remains intact.",
The(xname(obj)), hcolor("dark red"));
}
switch (dmgtyp) {
case AD_COLD:
if (osym == POTION_CLASS && obj->otyp != POT_OIL) {
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:
skip++;
break;
}
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)
pline("%s glows a strange %s, but remains intact.",
The(xname(obj)), hcolor("dark red"));
}
quan = obj->quan;
switch (osym) {
case POTION_CLASS:
dindx = (obj->otyp != POT_OIL) ? 1 : 2;
dmg = rnd(6);
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 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;
}
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++;
if (!cnt)
continue;
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)]);
if (osym == POTION_CLASS && dmgtyp != AD_COLD) {
if (!breathless(youmonst.data) || haseyes(youmonst.data))
potionbreathe(obj);
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;
}
if (obj->owornmask) {
if (obj->owornmask & W_RING) /* ring being worn */
Ring_gone(obj);
else
setnotworn(obj);
dindx = 5;
dmg = 0;
break;
case WAND_CLASS:
if (obj->otyp == WAN_LIGHTNING) {
skip++;
break;
}
if (obj == current_wand)
current_wand = 0; /* destroyed */
for (i = 0; i < cnt; i++)
useup(obj);
if (dmg) {
if (xresist)
You("aren't hurt!");
else {
const char *how = destroy_strings[dindx][2];
boolean one = (cnt == 1L);
#if 0
if (obj == current_wand) { skip++; break; }
#endif
dindx = 6;
dmg = rnd(10);
break;
default:
skip++;
break;
}
break;
default:
skip++;
break;
}
if (dmgtyp == AD_FIRE && osym == FOOD_CLASS)
how = "exploding glob of slime";
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);
}
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;
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)]);
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) {
You("aren't hurt!");
} else {
const char *how = destroy_strings[dindx][2];
boolean one = (cnt == 1L);
if (dmgtyp == AD_FIRE && osym == FOOD_CLASS)
how = "exploding glob of slime";
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;
}
@@ -4877,7 +4956,7 @@ destroy_mitem(mtmp, osym, dmgtyp)
struct monst *mtmp;
int osym, dmgtyp;
{
struct obj *obj, *obj2;
struct obj *obj;
int skip, tmp = 0;
long i, cnt, quan;
int dindx;
@@ -4889,8 +4968,11 @@ int osym, dmgtyp;
}
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;