From 8e4ce066c90ded17ba0b03c5a0e9124570be45ee Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 21 Sep 2018 23:34:00 -0400 Subject: [PATCH 01/28] fix C343-331 mirrors & sleeping mon This outstanding bug was complicated slightly because the same code was used for a sleeping mon as for a paralyzed mon so message phrasing was called into question. Just flip the phrasing to be about what you are able to discern under those circumstances, which is very little, and don't have the sleeping or paralyzed monster react to the mirror. --- doc/fixes36.2 | 2 ++ src/apply.c | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 443dfec50..24ed7f624 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -128,6 +128,8 @@ cancelled shapeshifter hit by polymorph magic will become uncancelled polymorph zap which creates a new long worm (or retains an old one via wizard mode monpolycontrol) can hit that worm multiple times (tail segments) wishing for "orange" could yield orange or orange colored gem/potion/spellbook +a sleeping or paralyzed mon would be frightened by its reflection when + applying a mirror Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/apply.c b/src/apply.c index 07269a358..61a9db19d 100644 --- a/src/apply.c +++ b/src/apply.c @@ -956,9 +956,20 @@ struct obj *obj; (void) rloc(mtmp, TRUE); } else if (!is_unicorn(mtmp->data) && !humanoid(mtmp->data) && (!mtmp->minvis || perceives(mtmp->data)) && rn2(5)) { - if (vis) - pline("%s is frightened by its reflection.", Monnam(mtmp)); - monflee(mtmp, d(2, 4), FALSE, FALSE); + boolean do_react = TRUE; + + if (mtmp->mfrozen) { + if (vis) + You("discern no obvious reaction from %s.", mon_nam(mtmp)); + else + You_feel("a bit silly gesturing the mirror in that direction."); + do_react = FALSE; + } + if (do_react) { + if (vis) + pline("%s is frightened by its reflection.", Monnam(mtmp)); + monflee(mtmp, d(2, 4), FALSE, FALSE); + } } else if (!Blind) { if (mtmp->minvis && !See_invisible) ; From 39d10d96df8dc15abba32b8e4a32e81bb8121edd Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Sat, 22 Sep 2018 15:55:26 +0300 Subject: [PATCH 02/28] Prevent leash showing unseen monster as "it" --- doc/fixes36.2 | 1 + src/objnam.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 24ed7f624..3475892bd 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -130,6 +130,7 @@ polymorph zap which creates a new long worm (or retains an old one via wizard wishing for "orange" could yield orange or orange colored gem/potion/spellbook a sleeping or paralyzed mon would be frightened by its reflection when applying a mirror +prevent leash showing unseen monster as "attached to it" Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/objnam.c b/src/objnam.c index 692291417..a3688b7a5 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1052,7 +1052,7 @@ unsigned doname_flags; obj->leashmon = 0; } else { Sprintf(eos(bp), " (attached to %s)", - a_monnam(mlsh)); + noit_mon_nam(mlsh)); } break; } From 0a52543076ea3f7d7127240e91067c96a03740c1 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Sep 2018 08:58:38 -0400 Subject: [PATCH 03/28] avoid illegal array indexes now that the enum treads beyond BL_FLUSH --- src/windows.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/windows.c b/src/windows.c index 96d9a218d..db0d53104 100644 --- a/src/windows.c +++ b/src/windows.c @@ -930,7 +930,7 @@ unsigned long *colormasks UNUSED; is buffered so final BL_FLUSH is needed to produce output) */ windowprocs.wincap2 |= WC2_FLUSH_STATUS; - if (idx != BL_FLUSH) { + if (idx >= 0) { if (!status_activefields[idx]) return; switch (idx) { @@ -972,11 +972,15 @@ unsigned long *colormasks UNUSED; break; } return; /* processed one field other than BL_FLUSH */ - } /* (idx != BL_FLUSH) */ + } /* (idx >= 0, thus not BL_FLUSH, BL_RESET, BL_CHARACTERISTICS) */ /* We've received BL_FLUSH; time to output the gathered data */ nb = newbot1; *nb = '\0'; + /* BL_FLUSH is the only pseudo-index value we need to check for + in the loop below because it is the only entry used to pad the + end of the fieldorder array. We could stop on any + negative (illegal) index, but this should be fine */ for (i = 0; (idx1 = fieldorder[0][i]) != BL_FLUSH; ++i) { if (status_activefields[idx1]) Strcpy(nb = eos(nb), status_vals[idx1]); From fe6892510046acf63baa0b239a2d7c7d4db5b723 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Sep 2018 09:11:51 -0400 Subject: [PATCH 04/28] another BL_CHARACTERISTICS fix With the code as it stood, receipt of BL_CHARACTERISTICS would trigger a flush of output which may not have been the intention. Ensure the flush code is only on BL_FLUSH (or BL_RESET). --- src/windows.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/windows.c b/src/windows.c index db0d53104..a26fc7e07 100644 --- a/src/windows.c +++ b/src/windows.c @@ -974,6 +974,10 @@ unsigned long *colormasks UNUSED; return; /* processed one field other than BL_FLUSH */ } /* (idx >= 0, thus not BL_FLUSH, BL_RESET, BL_CHARACTERISTICS) */ + if (idx != BL_FLUSH && idx != BL_RESET) + return; + /* does BL_RESET require any additional code to ensure all fields ? */ + /* We've received BL_FLUSH; time to output the gathered data */ nb = newbot1; *nb = '\0'; From faf1a83fd06818a8aba4cca1bb2c6474336a8df4 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Sep 2018 09:24:20 -0400 Subject: [PATCH 05/28] follow-up bit --- src/windows.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/windows.c b/src/windows.c index a26fc7e07..c87e4ca43 100644 --- a/src/windows.c +++ b/src/windows.c @@ -974,9 +974,10 @@ unsigned long *colormasks UNUSED; return; /* processed one field other than BL_FLUSH */ } /* (idx >= 0, thus not BL_FLUSH, BL_RESET, BL_CHARACTERISTICS) */ - if (idx != BL_FLUSH && idx != BL_RESET) + /* does BL_RESET require any specific code to ensure all fields ? */ + + if (!(idx == BL_FLUSH || idx == BL_RESET)) return; - /* does BL_RESET require any additional code to ensure all fields ? */ /* We've received BL_FLUSH; time to output the gathered data */ nb = newbot1; From 6a86cafa90ac236fde1a7bca7836590d001b6a77 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Sep 2018 10:20:08 -0400 Subject: [PATCH 06/28] X11 exposed a genl_status_update negative index issue --- doc/fixes36.2 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 3475892bd..29280ce7a 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -144,6 +144,8 @@ tty: turn off an optimization that is the suspected cause of Windows reported tty: ensure that current status fields are always copied to prior status values so that comparisons are correct orctown: prevent Bad fruit #0 and some minor tuning +X11: its use of genl_status_update exposed a negative index use that could + lead to a segfault Platform- and/or Interface-Specific Fixes From bbb81700f543c85f8209e6c05ce77d7e5b3e5f46 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Sep 2018 14:08:28 -0400 Subject: [PATCH 07/28] sunsword vs gremlin The original report complained that gremlins seemed impervious to Sunsword's light yet a flash from a camera caused them to cry out in pain despite "The long sword named Sunsword begins to shine brilliantly!" This commit does two things: 1. A dmg bonus is applied against gremlins using a lit Sunsword. 2. Gremlins will generally avoid the light emitted by Sunsword. There's a few minor flavor bits thrown in also. It is understood that this effectively makes Sunsword provide "gremlin-proofing", but the gremlin myth and Sunsword's characteristic feature pretty much demand it. bug 42 --- doc/fixes36.2 | 2 ++ include/extern.h | 1 + include/mondata.h | 2 ++ src/mondata.c | 8 ++++++++ src/monmove.c | 23 ++++++++++++++++++++--- src/uhitm.c | 31 ++++++++++++++++++++++++++++++- src/weapon.c | 2 ++ 7 files changed, 65 insertions(+), 4 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 29280ce7a..ecb6baa93 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -131,6 +131,8 @@ wishing for "orange" could yield orange or orange colored gem/potion/spellbook a sleeping or paralyzed mon would be frightened by its reflection when applying a mirror prevent leash showing unseen monster as "attached to it" +gremlins seemed impervious to Sunsword's light yet a flash from a camera + caused them to cry out in pain Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 7e1fb1270..b241ac074 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1438,6 +1438,7 @@ FDECL(can_blnd, (struct monst *, struct monst *, UCHAR_P, struct obj *)); E boolean FDECL(ranged_attk, (struct permonst *)); E boolean FDECL(hates_silver, (struct permonst *)); E boolean FDECL(mon_hates_silver, (struct monst *)); +E boolean FDECL(mon_hates_light, (struct monst *)); E boolean FDECL(passes_bars, (struct permonst *)); E boolean FDECL(can_blow, (struct monst *)); E boolean FDECL(can_chant, (struct monst *)); diff --git a/include/mondata.h b/include/mondata.h index 95153ce5d..0b1df5ccc 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -175,6 +175,8 @@ #define is_vampire(ptr) ((ptr)->mlet == S_VAMPIRE) +#define hates_light(ptr) ((ptr) == &mons[PM_GREMLIN]) + /* used to vary a few messages */ #define weirdnonliving(ptr) (is_golem(ptr) || (ptr)->mlet == S_VORTEX) #define nonliving(ptr) \ diff --git a/src/mondata.c b/src/mondata.c index 2a6134863..9a65c1480 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -312,6 +312,14 @@ register struct permonst *ptr; || (ptr->mlet == S_IMP && ptr != &mons[PM_TENGU])); } +/* True if specific monster is especially affected by light-emitting weapons */ +boolean +mon_hates_light(mon) +struct monst *mon; +{ + return (boolean) (hates_light(mon->data)); +} + /* True iff the type of monster pass through iron bars */ boolean passes_bars(mptr) diff --git a/src/monmove.c b/src/monmove.c index 90b4f33c1..762b4ecb9 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -255,6 +255,14 @@ struct monst *mon; } } +#define flees_light(mon) ((mon)->data == &mons[PM_GREMLIN] && \ + (uwep && artifact_light(uwep) && uwep->lamplit)) + +/* we could include this in the above macro, but probably overkill/overhead */ +/* (!((which_armor((mon), W_ARMC) != 0) && ((which_armor((mon), W_ARMH) != 0))) && */ + + + /* monster begins fleeing for the specified time, 0 means untimed flee * if first, only adds fleetime if monster isn't already fleeing * if fleemsg, prints a message about new flight, otherwise, caller should */ @@ -289,9 +297,15 @@ boolean fleemsg; /* unfortunately we can't distinguish between temporary sleep and temporary paralysis, so both conditions receive the same alternate message */ - if (!mtmp->mcanmove || !mtmp->data->mmove) + if (!mtmp->mcanmove || !mtmp->data->mmove) { pline("%s seems to flinch.", Adjmonnam(mtmp, "immobile")); - else + } else if (flees_light(mtmp)) { + if (rn2(10) || Deaf) + pline("%s flees from the painful light of %s.", + Monnam(mtmp), bare_artifactname(uwep)); + else + verbalize("Bright light!"); + } else pline("%s turns to flee.", Monnam(mtmp)); } mtmp->mflee = 1; @@ -306,7 +320,7 @@ register struct monst *mtmp; int *inrange, *nearby, *scared; { int seescaryx, seescaryy; - boolean sawscary = FALSE; + boolean sawscary = FALSE, bravegremlin = (rn2(5) == 0); *inrange = (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= (BOLT_LIM * BOLT_LIM)); @@ -329,6 +343,7 @@ int *inrange, *nearby, *scared; sawscary = onscary(seescaryx, seescaryy, mtmp); if (*nearby && (sawscary + || (flees_light(mtmp) && !bravegremlin) || (!mtmp->mpeaceful && in_your_sanctuary(mtmp, 0, 0)))) { *scared = 1; monflee(mtmp, rnd(rn2(7) ? 10 : 100), TRUE, TRUE); @@ -336,6 +351,8 @@ int *inrange, *nearby, *scared; *scared = 0; } +#undef flees_light + /* perform a special one-time action for a monster; returns -1 if nothing special happened, 0 if monster uses up its turn, 1 if monster is killed */ STATIC_OVL int diff --git a/src/uhitm.c b/src/uhitm.c index b240eef26..5dd5b8623 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -658,6 +658,7 @@ int dieroll; boolean ispoisoned = FALSE, needpoismsg = FALSE, poiskilled = FALSE, unpoisonmsg = FALSE; boolean silvermsg = FALSE, silverobj = FALSE; + boolean lightobj = FALSE; boolean valid_weapon_attack = FALSE; boolean unarmed = !uwep && !uarm && !uarms; boolean hand_to_hand = (thrown == HMON_MELEE @@ -699,7 +700,10 @@ int dieroll; } } } else { - Strcpy(saved_oname, cxname(obj)); + if (!(artifact_light(obj) && obj->lamplit)) + Strcpy(saved_oname, cxname(obj)); + else + Strcpy(saved_oname, bare_artifactname(obj)); if (obj->oclass == WEAPON_CLASS || is_weptool(obj) || obj->oclass == GEM_CLASS) { /* is it not a melee weapon? */ @@ -803,6 +807,8 @@ int dieroll; silvermsg = TRUE; silverobj = TRUE; } + if (artifact_light(obj) && obj->lamplit && mon_hates_light(mon)) + lightobj = TRUE; if (u.usteed && !thrown && tmp > 0 && weapon_type(obj) == P_LANCE && mon != u.ustuck) { jousting = joust(mon, obj); @@ -1251,6 +1257,29 @@ int dieroll; whom = strcat(s_suffix(whom), " flesh"); pline(fmt, whom); } + if (lightobj) { + const char *fmt; + char *whom = mon_nam(mon); + char emitlightobjbuf[BUFSZ]; + + if (canspotmon(mon)) { + if (saved_oname[0]) { + Sprintf(emitlightobjbuf, + "%s radiance penetrates deep into", + s_suffix(saved_oname)); + Strcat(emitlightobjbuf, " %s!"); + fmt = emitlightobjbuf; + } else + fmt = "The light sears %s!"; + } else { + *whom = highc(*whom); /* "it" -> "It" */ + fmt = "%s is seared!"; + } + /* note: s_suffix returns a modifiable buffer */ + if (!noncorporeal(mdat) && !amorphous(mdat)) + whom = strcat(s_suffix(whom), " flesh"); + pline(fmt, whom); + } /* if a "no longer poisoned" message is coming, it will be last; obj->opoisoned was cleared above and any message referring to "poisoned " has now been given; we want just "" for diff --git a/src/weapon.c b/src/weapon.c index 2b7b3bf0a..b680f864c 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -329,6 +329,8 @@ struct monst *mon; bonus += rnd(4); if (objects[otyp].oc_material == SILVER && mon_hates_silver(mon)) bonus += rnd(20); + if (artifact_light(otmp) && otmp->lamplit && hates_light(ptr)) + bonus += rnd(8); /* if the weapon is going to get a double damage bonus, adjust this bonus so that effectively it's added after the doubling */ From b6e3f0185562685c8895c87608a74b7c863f4127 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 22 Sep 2018 14:57:55 -0700 Subject: [PATCH 08/28] shiny object probabilities The wishing code uses 'oc_prob + 1' so that probability 0 (never random) objects are eligible to be selected if their name matches a wish; collecting 'shiny' objects shouldn't do that. (No effect on play since there aren't any shiny objects with 0% random chance.) rn2() takes int, and total oc_prob for entire objects[] array is 15000, so don't accumulate the target probability in a long. --- src/objnam.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/objnam.c b/src/objnam.c index a3688b7a5..a232d7860 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -12,7 +12,7 @@ STATIC_DCL char *FDECL(strprepend, (char *, const char *)); STATIC_DCL short FDECL(rnd_otyp_by_wpnskill, (SCHAR_P)); -STATIC_DCL short FDECL(rnd_otyp_by_namedesc, (char *, CHAR_P)); +STATIC_DCL short FDECL(rnd_otyp_by_namedesc, (const char *, CHAR_P)); STATIC_DCL boolean FDECL(wishymatch, (const char *, const char *, BOOLEAN_P)); STATIC_DCL char *NDECL(nextobuf); STATIC_DCL void FDECL(releaseobuf, (char *)); @@ -2693,6 +2693,7 @@ schar skill; { int i, n = 0; short otyp = STRANGE_OBJECT; + for (i = bases[WEAPON_CLASS]; i < NUM_OBJECTS && objects[i].oc_class == WEAPON_CLASS; i++) if (objects[i].oc_skill == skill) { @@ -2712,15 +2713,15 @@ schar skill; STATIC_OVL short rnd_otyp_by_namedesc(name, oclass) -char *name; +const char *name; char oclass; { int i, n = 0; short validobjs[NUM_OBJECTS]; register const char *zn; - long maxprob = 0; + int prob, maxprob = 0; - if (!name) + if (!name || !*name) return STRANGE_OBJECT; memset((genericptr_t) validobjs, 0, sizeof validobjs); @@ -2737,17 +2738,15 @@ char oclass; || ((zn = objects[i].oc_uname) != 0 && wishymatch(name, zn, FALSE))) { validobjs[n++] = (short) i; - maxprob += (objects[i].oc_prob + 1); + maxprob += objects[i].oc_prob; } } if (n > 0 && maxprob) { - long prob = rn2(maxprob); - - i = 0; - while (i < n - 1 - && (prob -= (objects[validobjs[i]].oc_prob + 1)) >= 0) - i++; + prob = rn2(maxprob); + for (i = 0; i < n - 1; i++) + if ((prob -= objects[validobjs[i]].oc_prob) < 0) + break; return validobjs[i]; } return STRANGE_OBJECT; From 4f61e9697cc95ebc0d556c9de7747807574cb868 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 22 Sep 2018 15:18:38 -0700 Subject: [PATCH 09/28] fix github issue #134 - display of migrated objects Fixes #134 An invisible hero (who can't see invisible and doesn't have autopickup enabled) going down stairs to an object which fell down those stairs will see the stairs instead of the object on them. Missing newsym() in obj_delivery() when objects aren't being passed through scatter(). --- doc/fixes36.2 | 2 ++ src/dokick.c | 2 ++ src/explode.c | 1 + 3 files changed, 5 insertions(+) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index ecb6baa93..914a3469e 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -133,6 +133,8 @@ a sleeping or paralyzed mon would be frightened by its reflection when prevent leash showing unseen monster as "attached to it" gremlins seemed impervious to Sunsword's light yet a flash from a camera caused them to cry out in pain +when objects migrate (fall down stairs) and invisible hero (w/o see invisible, + no-autopickup) descends, stairs get shown instead of object(s) on them Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/dokick.c b/src/dokick.c index 4a4ac6b93..99858a62f 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -1658,6 +1658,8 @@ boolean near_hero; stackobj(otmp); if (!noscatter) (void) scatter(nx, ny, rnd(2), 0, otmp); + else + newsym(nx, ny); } else { /* random location */ /* set dummy coordinates because there's no current position for rloco() to update */ diff --git a/src/explode.c b/src/explode.c index c48f9ffb1..fc2855c1d 100644 --- a/src/explode.c +++ b/src/explode.c @@ -657,6 +657,7 @@ struct obj *obj; /* only scatter this obj */ (void) break_statue(otmp); place_object(otmp, sx, sy); /* put fragments on floor */ } + newsym(sx, sy); /* in case it's beyond radius of 'farthest' */ used_up = TRUE; /* 1 in 10 chance of destruction of obj; glass, egg destruction */ From 18d56724f91f3014269cf8b20eae83b9726bb3d9 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 22 Sep 2018 16:46:27 -0700 Subject: [PATCH 10/28] 'unbreak' wishing probabilities Fixing rnd_otyp_by_namedesc() for use by get_shiny() broke its use by readobjnam(). Make the chance for 0% generation objects to have non-zero chance of being selected be a parameter. --- src/objnam.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/objnam.c b/src/objnam.c index a232d7860..46fc3d493 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 objnam.c $NHDT-Date: 1537313446 2018/09/18 23:30:46 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.208 $ */ +/* NetHack 3.6 objnam.c $NHDT-Date: 1537659941 2018/09/22 23:45:41 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.212 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -12,7 +12,7 @@ STATIC_DCL char *FDECL(strprepend, (char *, const char *)); STATIC_DCL short FDECL(rnd_otyp_by_wpnskill, (SCHAR_P)); -STATIC_DCL short FDECL(rnd_otyp_by_namedesc, (const char *, CHAR_P)); +STATIC_DCL short FDECL(rnd_otyp_by_namedesc, (const char *, CHAR_P, int)); STATIC_DCL boolean FDECL(wishymatch, (const char *, const char *, BOOLEAN_P)); STATIC_DCL char *NDECL(nextobuf); STATIC_DCL void FDECL(releaseobuf, (char *)); @@ -2712,9 +2712,10 @@ schar skill; } STATIC_OVL short -rnd_otyp_by_namedesc(name, oclass) +rnd_otyp_by_namedesc(name, oclass, xtra_prob) const char *name; char oclass; +int xtra_prob; /* to force 0% random generation items to also be considered */ { int i, n = 0; short validobjs[NUM_OBJECTS]; @@ -2726,6 +2727,14 @@ char oclass; memset((genericptr_t) validobjs, 0, sizeof validobjs); + /* FIXME: + * When this spans classes (the !oclass case), the item + * probabilities are not very useful because they don't take + * the class generation probability into account. [If 10% + * of spellbooks were blank and 1% of scrolls were blank, + * "blank" would have 10/11 chance to yield a blook even though + * scrolls are supposed to be much more common than books.] + */ for (i = oclass ? bases[(int) oclass] : STRANGE_OBJECT + 1; i < NUM_OBJECTS && (!oclass || objects[i].oc_class == oclass); ++i) { @@ -2738,14 +2747,14 @@ char oclass; || ((zn = objects[i].oc_uname) != 0 && wishymatch(name, zn, FALSE))) { validobjs[n++] = (short) i; - maxprob += objects[i].oc_prob; + maxprob += (objects[i].oc_prob + xtra_prob); } } if (n > 0 && maxprob) { prob = rn2(maxprob); for (i = 0; i < n - 1; i++) - if ((prob -= objects[validobjs[i]].oc_prob) < 0) + if ((prob -= (objects[validobjs[i]].oc_prob + xtra_prob)) < 0) break; return validobjs[i]; } @@ -2756,7 +2765,7 @@ int shiny_obj(oclass) char oclass; { - return (int) rnd_otyp_by_namedesc("shiny", oclass); + return (int) rnd_otyp_by_namedesc("shiny", oclass, 0); } /* @@ -3389,10 +3398,10 @@ srch: } } - if (((typ = rnd_otyp_by_namedesc(actualn, oclass)) != STRANGE_OBJECT) - || ((typ = rnd_otyp_by_namedesc(dn, oclass)) != STRANGE_OBJECT) - || ((typ = rnd_otyp_by_namedesc(un, oclass)) != STRANGE_OBJECT) - || ((typ = rnd_otyp_by_namedesc(origbp, oclass)) != STRANGE_OBJECT)) + if (((typ = rnd_otyp_by_namedesc(actualn, oclass, 1)) != STRANGE_OBJECT) + || ((typ = rnd_otyp_by_namedesc(dn, oclass, 1)) != STRANGE_OBJECT) + || ((typ = rnd_otyp_by_namedesc(un, oclass, 1)) != STRANGE_OBJECT) + || ((typ = rnd_otyp_by_namedesc(origbp, oclass, 1)) != STRANGE_OBJECT)) goto typfnd; typ = 0; From 21a81d0294fd694889fcda7b457a5bfd001e7abd Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Sep 2018 22:41:02 -0400 Subject: [PATCH 11/28] BL_RESET usage for window port status line updating Like BL_FLUSH, only send BL_RESET if the window port has indicated it wants them via setting the appropriate WC2 bits in its window_procs structure. Update documentation. --- doc/window.doc | 15 +++++++++++++++ include/winprocs.h | 4 +++- src/botl.c | 3 ++- win/X11/winX.c | 6 +++++- win/tty/wintty.c | 1 + win/win32/mswproc.c | 2 +- 6 files changed, 27 insertions(+), 4 deletions(-) diff --git a/doc/window.doc b/doc/window.doc index a7141d448..04a74d7af 100644 --- a/doc/window.doc +++ b/doc/window.doc @@ -774,8 +774,23 @@ to support: | wraptext | WC2_WRAPTEXT | wc2_wraptext |boolean | | selectsaved | WC2_SELECTSAVED | wc2_selectsaved |boolean | | hitpointbar | WC2_HITPOINTBAR | wc2_hitpointbar |boolean | + | hitpointbar | WC2_HITPOINTBAR | wc2_hitpointbar |boolean | +--------------------+--------------------+--------------------+--------+ + more wincap2 for STATUS_HILITES support and control + +--------------------------------- +---------------------------+ + | To inform the game engine | | + | that the window port is equipped | bit to set in wincap mask | + | to receive the following in its | | + | x_status_update() routine | | + |----------------------------------+---------------------------+ + | BL_FLUSH to render buffered | WC2_FLUSH_STATUS | + | field changes now | | + |----------------------------------+---------------------------+ + | BL_RESET to indicate that all | WC2_RESET_STATUS | + | fields should be redone | | + +----------------------------------+---------------------------+ + align_message -- where to place message window (top, bottom, left, right) align_status -- where to place status display (top, bottom, left, right). ascii_map -- port should display an ascii map if it can. diff --git a/include/winprocs.h b/include/winprocs.h index 3c19d4f03..a0c490f11 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -214,7 +214,9 @@ extern #define WC2_HITPOINTBAR 0x0040L /* 07 show bar representing hit points */ #define WC2_FLUSH_STATUS 0x0080L /* 08 call status_update(BL_FLUSH) after updating status window fields */ - /* 24 free bits */ +#define WC2_RESET_STATUS 0x0100L /* 09 call status_update(BL_RESET) to indicate + draw everything */ + /* 23 free bits */ #define ALIGN_LEFT 1 #define ALIGN_RIGHT 2 diff --git a/src/botl.c b/src/botl.c index 8ffe73c5e..721ad2316 100644 --- a/src/botl.c +++ b/src/botl.c @@ -810,7 +810,8 @@ boolean *valsetlist; * the display, call status_update() with BL_FLUSH. * */ - if (context.botlx) + if (context.botlx && + (windowprocs.wincap2 & WC2_RESET_STATUS) != 0L) status_update(BL_RESET, (genericptr_t) 0, 0, 0, NO_COLOR, &cond_hilites[0]); else if ((windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L) diff --git a/win/X11/winX.c b/win/X11/winX.c index 5c1de3c12..891a76fa3 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -100,7 +100,11 @@ struct window_procs X11_procs = { "X11", (WC_COLOR | WC_HILITE_PET | WC_ASCII_MAP | WC_TILED_MAP | WC_PLAYER_SELECTION | WC_PERM_INVENT | WC_MOUSE_SUPPORT), - 0L, /* WC2 flag mask */ + (0 +#if defined(STATUS_HILITES) + | WC2_FLUSH_STATUS | WC2_RESET_STATUS +#endif + ), X11_init_nhwindows, X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows, X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow, diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 5c98c0299..7657b1186 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -67,6 +67,7 @@ struct window_procs tty_procs = { #endif #if defined(STATUS_HILITES) | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS + | WC2_RESET_STATUS #endif | WC2_DARKGRAY), tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event, diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 77432f751..fea75b177 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -88,7 +88,7 @@ struct window_procs mswin_procs = { | WC_VARY_MSGCOUNT | WC_WINDOWCOLORS | WC_PLAYER_SELECTION | WC_SPLASH_SCREEN | WC_POPUP_DIALOG | WC_MOUSE_SUPPORT, #ifdef STATUS_HILITES - WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_HILITE_STATUS | + WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_RESET_STATUS | WC2_HILITE_STATUS | #endif 0L, mswin_init_nhwindows, mswin_player_selection, mswin_askname, mswin_get_nh_event, mswin_exit_nhwindows, mswin_suspend_nhwindows, From 84c17d2e21b1dde05284d4b2b027a2ae9707db16 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Sep 2018 23:01:39 -0400 Subject: [PATCH 12/28] two typo/follow-up bits --- doc/window.doc | 1 - win/X11/winX.c | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/window.doc b/doc/window.doc index 04a74d7af..a564d5b7b 100644 --- a/doc/window.doc +++ b/doc/window.doc @@ -774,7 +774,6 @@ to support: | wraptext | WC2_WRAPTEXT | wc2_wraptext |boolean | | selectsaved | WC2_SELECTSAVED | wc2_selectsaved |boolean | | hitpointbar | WC2_HITPOINTBAR | wc2_hitpointbar |boolean | - | hitpointbar | WC2_HITPOINTBAR | wc2_hitpointbar |boolean | +--------------------+--------------------+--------------------+--------+ more wincap2 for STATUS_HILITES support and control diff --git a/win/X11/winX.c b/win/X11/winX.c index 891a76fa3..5da69a6ec 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -100,11 +100,10 @@ struct window_procs X11_procs = { "X11", (WC_COLOR | WC_HILITE_PET | WC_ASCII_MAP | WC_TILED_MAP | WC_PLAYER_SELECTION | WC_PERM_INVENT | WC_MOUSE_SUPPORT), - (0 #if defined(STATUS_HILITES) - | WC2_FLUSH_STATUS | WC2_RESET_STATUS + WC2_FLUSH_STATUS | WC2_RESET_STATUS | #endif - ), + 0L, X11_init_nhwindows, X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows, X11_suspend_nhwindows, X11_resume_nhwindows, X11_create_nhwindow, From 76bffe5ba47336c002c8faf51e158a490c1282af Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 23 Sep 2018 10:11:02 -0400 Subject: [PATCH 13/28] drum of earthquake causing deafness inappropriately Address a drum of earthquake inconsistency reported 2017-03-23: "Drum of earthquake does not make you deaf. Leather drum or depleted drum of earthquake does." bug 1099 --- doc/fixes36.2 | 1 + src/music.c | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 914a3469e..a76f1bb3b 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -135,6 +135,7 @@ gremlins seemed impervious to Sunsword's light yet a flash from a camera caused them to cry out in pain when objects migrate (fall down stairs) and invisible hero (w/o see invisible, no-autopickup) descends, stairs get shown instead of object(s) on them +drum of earthquake was causing deafness but oddly enough only when used up Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/music.c b/src/music.c index 71253f6e1..cb63bde90 100644 --- a/src/music.c +++ b/src/music.c @@ -445,6 +445,11 @@ generic_lvl_desc() return "dungeon"; } +const char *beats[] = { + "stepper", "one drop", "slow two", "triple stroke roll", + "double shuffle", "half-time shuffle", "second line", "train" +}; + /* * The player is trying to extract something from his/her instrument. */ @@ -454,6 +459,7 @@ struct obj *instr; { int damage, mode, do_spec = !(Stunned || Confusion); struct obj itmp; + boolean mundane = FALSE; itmp = *instr; itmp.oextra = (struct oextra *) 0; /* ok on this copy as instr maintains @@ -462,8 +468,10 @@ struct obj *instr; /* if won't yield special effect, make sound of mundane counterpart */ if (!do_spec || instr->spe <= 0) - while (objects[itmp.otyp].oc_magic) + while (objects[itmp.otyp].oc_magic) { itmp.otyp -= 1; + mundane = TRUE; + } #ifdef MAC mac_speaker(&itmp, "C"); #endif @@ -579,6 +587,10 @@ struct obj *instr; exercise(A_DEX, TRUE); break; case DRUM_OF_EARTHQUAKE: /* create several pits */ + /* a drum of earthquake does cause not cause deafness + while still magically functional, nor afterwards + when it invokes the LEATHER_DRUM case instead and + mundane is flagged */ consume_obj_charge(instr, TRUE); You("produce a heavy, thunderous rolling!"); @@ -589,10 +601,15 @@ struct obj *instr; makeknown(DRUM_OF_EARTHQUAKE); break; case LEATHER_DRUM: /* Awaken monsters */ - You("beat a deafening row!"); - awaken_monsters(u.ulevel * 40); - incr_itimeout(&HDeaf, rn1(20, 30)); - exercise(A_WIS, FALSE); + if (!mundane) { + You("beat a deafening row!"); + incr_itimeout(&HDeaf, rn1(20, 30)); + exercise(A_WIS, FALSE); + } else + You("%s %s.", + rn2(2) ? "butcher" : rn2(2) ? "manage" : "pull off", + an(beats[rn2(SIZE(beats))])); + awaken_monsters(u.ulevel * (mundane ? 5 : 40)); context.botl = TRUE; break; default: From 2813e42b22eef20b33ce2acd95875e11f54b810c Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 23 Sep 2018 11:16:09 -0400 Subject: [PATCH 14/28] known bear trap forgotton by player polymorphed into a flyer The original report stated: "I located a bear trap as a human and just ignored it for the time. I polymporphed into a Vampire Lord, then went to #untrap the bear trap. On the first attempt, I stood beside the trap and attempted to #untrap. I received the 'Whoops!' message and automatically moved onto the trap square as a result. The bear trap vanished! I obviously wasn't trapped since I'm polymorphed into a flying monster, but the trap glyph was no longer present. The glyph looked like regular floor - as if I had untrapped the bear trap and taken the trap with me." The trap was actually still there but became hidden intentionally for other valid scenarios, but was an unintended side-effect for this scenario. Fix it by failing the #untrap operation for a Flyer earlier on, and in a more benign manner, since the Flyer ultimately doesn't end up in the trap anyway. You'll still get the "Whoops!", followed by a message, but that's as far as the "failed" #untrap attempt will go under the circumstances. --- doc/fixes36.2 | 3 +++ src/trap.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index a76f1bb3b..c02837500 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -136,6 +136,9 @@ gremlins seemed impervious to Sunsword's light yet a flash from a camera when objects migrate (fall down stairs) and invisible hero (w/o see invisible, no-autopickup) descends, stairs get shown instead of object(s) on them drum of earthquake was causing deafness but oddly enough only when used up +known bear trap was being forgotten about by a player polymorphed into a + flying monster if the player unsuccessfully tried to #untrap it and + moved onto the trap square as a result Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/trap.c b/src/trap.c index f7d1d0bc9..99554c326 100644 --- a/src/trap.c +++ b/src/trap.c @@ -3991,6 +3991,16 @@ boolean force_failure; } } else if (under_u) { dotrap(ttmp, 0); + } else if (ttype == BEAR_TRAP && (Levitation || Flying)) { + /* There was a report of oddities of the trap + vanishing from view due to tseen being cleared + (which was deliberate to work around a check_here() + issue). Since you won't actually end up in the trap + during the #untrap operation anyway due to + Levitation and Flying checks further along, + just avoid the whole "vanishing trap" scenario + by failing the #untrap operation right here. */ + You("couldn't reach it from your vantage point."); } else { move_into_trap(ttmp); } From 02b1ce2d9a9e95fd87dcd1982cc0a76fcc193d59 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 23 Sep 2018 14:06:18 -0400 Subject: [PATCH 15/28] no leash-related message is given when leashed pet yellow light explodes mondead() -> m_detach() -> m_unleash() suppresses the m_unleash() slack message, so deliver it in the caller explmm() in those circumstances. (The issue of whether it should be possible to leash light is side-stepped.) bug H7406, 1548 --- doc/fixes36.2 | 1 + src/mhitm.c | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index c02837500..97e7698d0 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -139,6 +139,7 @@ drum of earthquake was causing deafness but oddly enough only when used up known bear trap was being forgotten about by a player polymorphed into a flying monster if the player unsuccessfully tried to #untrap it and moved onto the trap square as a result +no leash-related message is given when a leashed pet yellow light explodes Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/mhitm.c b/src/mhitm.c index 95413843c..87ee54e21 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -781,10 +781,17 @@ struct attack *mattk; /* Kill off aggressor if it didn't die. */ if (!(result & MM_AGR_DIED)) { + boolean was_leashed = (magr->mleashed); + mondead(magr); if (!DEADMONSTER(magr)) return result; /* life saved */ result |= MM_AGR_DIED; + + /* mondead() -> m_detach() -> m_unleash() always suppresses + the m_unleash() slack message, so deliver it here instead */ + if (was_leashed) + Your("leash falls slack."); } if (magr->mtame) /* give this one even if it was visible */ You(brief_feeling, "melancholy"); From 22d69d242495d8a5f2d4a8d6abcfe5c178ea2f41 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 24 Sep 2018 15:59:38 -0400 Subject: [PATCH 16/28] update tournament reference --- dat/history | 10 ++++++---- doc/Guidebook.mn | 2 +- doc/Guidebook.tex | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/dat/history b/dat/history index c6ff13b31..b201c094d 100644 --- a/dat/history +++ b/dat/history @@ -215,10 +215,12 @@ necessary updates to the community at large. The official NetHack web site is maintained by Ken Lorber at http://www.nethack.org/. -On behalf of the NetHack community, thank you very much once again to -M. Drew Streib, Pasi Kallinen and Robin Bandy for providing public -NetHack servers at nethack.alt.org and devnull.net and/or for hosting annual -NetHack tournaments. +On behalf of the NetHack community, thank you very much once again to +M. Drew Streib and Pasi Kallinen for providing a public NetHack server +at nethack.alt.org. Thanks to Keith Simpson and Andy Thomson for +hardfought.org. Thanks to all those unnamed dungeoneers who invest their +time and effort into annual NetHack tournaments such as Junehack +and in days past, devnull.net (gone for now, but not forgotten). - - - - - - - - - - diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 866857333..c4e59ecb2 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4593,7 +4593,7 @@ The official NetHack web site is maintained by \fBKen Lorber\fP at http://www.ne SPECIAL THANKS .pg On behalf of the NetHack community, thank you very much once -again to \fBM. Drew Streib\fP, \fBPasi Kallinen\fP for providing a +again to \fBM. Drew Streib\fP and \fBPasi Kallinen\fP for providing a public NetHack server at nethack.alt.org. Thanks to \fBKeith Simpson\fP and \fBAndy Thomson\fP for hardfought.org. Thanks to all those unnamed dungeoneers who invest their time and effort into annual diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index d8ecc0705..2eb3bbf78 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -5377,7 +5377,7 @@ http:{\tt /}{\tt /}www.nethack.org{\tt /}. \subsection*{Special Thanks} \nd On behalf of the {\it NetHack\/} community, thank you very much once -again to {\it M. Drew Streib}, {\it Pasi Kallinen} for providing a +again to {\it M. Drew Streib} and {\it Pasi Kallinen} for providing a public NetHack server at nethack.alt.org. Thanks to {\it Keith Simpson} and {\it Andy Thomson} for hardfought.org. Thanks to all those unnamed dungeoneers who invest their time and effort into annual From 3f5ef9bdf3e8ecbd833ffb5aef38eb5de780d9ee Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 24 Sep 2018 16:45:38 -0400 Subject: [PATCH 17/28] make long extended commands list be more navigable --- doc/fixes36.2 | 1 + src/cmd.c | 155 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 130 insertions(+), 26 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 97e7698d0..727a067ea 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -155,6 +155,7 @@ tty: ensure that current status fields are always copied to prior status orctown: prevent Bad fruit #0 and some minor tuning X11: its use of genl_status_update exposed a negative index use that could lead to a segfault +make long extended commands list be more navigable Platform- and/or Interface-Specific Fixes diff --git a/src/cmd.c b/src/cmd.c index b5c6ee1a2..c698b0647 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -361,39 +361,142 @@ doextcmd(VOID_ARGS) return retval; } -/* here after #? - now list all full-word commands */ +/* here after #? - now list all full-word commands and provid + some navigation capability through the long list */ int doextlist(VOID_ARGS) { register const struct ext_func_tab *efp; - char buf[BUFSZ]; - winid datawin; - char ch = cmd_from_func(doextcmd); + char buf[BUFSZ], searchbuf[BUFSZ], promptbuf[QBUFSZ]; + winid menuwin; + anything any; + menu_item *selected; + int n, pass, maxpass = wizard ? 2 : 1; + int menumode = 0, menushown[2] = {0,0}, onelist = 0; + boolean redisplay = TRUE, search = FALSE; + const char *headings[] = {"Extended commands", + "Debugging Extended Commands"}; - datawin = create_nhwindow(NHW_TEXT); - putstr(datawin, 0, ""); - putstr(datawin, 0, " Extended Commands List"); - putstr(datawin, 0, ""); - if (ch) { - Sprintf(buf, " Press '%s', then type:", - visctrl(ch)); - putstr(datawin, 0, buf); - putstr(datawin, 0, ""); - } + searchbuf[0] = '\0'; + menuwin = create_nhwindow(NHW_MENU); - for (efp = extcmdlist; efp->ef_txt; efp++) { - if (!wizard && (efp->flags & WIZMODECMD)) - continue; - Sprintf(buf, " %-15s %c %s.", - efp->ef_txt, - (efp->flags & AUTOCOMPLETE) ? '*' : ' ', - efp->ef_desc); - putstr(datawin, 0, buf); + while (redisplay) { + redisplay = FALSE; + any = zeroany; + start_menu(menuwin); + add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + "Extended Commands List", MENU_UNSELECTED); + + add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + "", MENU_UNSELECTED); + + Strcpy(buf, menumode ? "Show" : "Hide"); + Strcat(buf, " commands that don't autocomplete"); + if (!menumode) + Strcat(buf, " (those not marked with [A] below)"); + any.a_int = 1; + add_menu(menuwin, NO_GLYPH, &any, 'a', 0, ATR_NONE, buf, MENU_UNSELECTED); + + if (strlen(searchbuf) == 0) { + any.a_int = 2; + add_menu(menuwin, NO_GLYPH, &any, 's', 0, ATR_NONE, + "Search extended commands", MENU_UNSELECTED); + } else { + Strcpy(buf, "Show all, clear search"); + if ((strlen(buf) + strlen(searchbuf) + strlen(" (\"\")")) < QBUFSZ) + Sprintf(eos(buf), " (\"%s\")", searchbuf); + any.a_int = 3; + add_menu(menuwin, NO_GLYPH, &any, 's', 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + if (wizard) { + any.a_int = 4; + add_menu(menuwin, NO_GLYPH, &any, 'z', 0, ATR_NONE, + onelist ? "Show debugging commands in separate section" : + "Show all alphabetically, including debugging commands", + MENU_UNSELECTED); + } + any = zeroany; + add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + "", MENU_UNSELECTED); + menushown[0] = menushown[1] = 0; + for (pass = 0; pass < maxpass; ++pass) { + for (efp = extcmdlist; efp->ef_txt; efp++) { + boolean showit = (onelist || + (!menumode || + (menumode && (efp->flags & AUTOCOMPLETE))) && + ((!pass && !(efp->flags & WIZMODECMD)) || + (pass && (efp->flags & WIZMODECMD)))); + + if (strlen(searchbuf) > 0) { + if (!((strstri(efp->ef_txt, searchbuf) != 0) || + (strstri(efp->ef_desc, searchbuf) != 0))) + showit = FALSE; + } + if (showit) { + /* We're about to show an item, have we shown the menu yet? + Doing menu in inner loop like this on demand avoids a + heading with no subordinate entries on the search + results menu */ + if (!menushown[pass]) { + Strcpy(buf, headings[pass]); + add_menu(menuwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + buf, MENU_UNSELECTED); + menushown[pass] = 1; + } + Sprintf(buf, " %-14s %-3s %s", + efp->ef_txt, + (efp->flags & AUTOCOMPLETE) ? "[A]" : " ", + efp->ef_desc); + add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + } + add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + "", MENU_UNSELECTED); + } + end_menu(menuwin, (char *) 0); + n = select_menu(menuwin, PICK_ONE, &selected); + if (n > 0) { + switch(selected[0].item.a_int) { + case 1: + menumode = 1 - menumode; /* toggle 0 -> 1, 1 -> 0 */ + redisplay = TRUE; + break; + case 2: + search = TRUE; + break; + case 3: + search = FALSE; + searchbuf[0] = '\0'; + redisplay = TRUE; + break; + case 4: + search = FALSE; + searchbuf[0] = '\0'; + onelist = 1 - onelist; /* toggle 0 -> 1, 1 -> 0 */ + maxpass = onelist ? 1 : wizard ? 2 : 1; + redisplay = TRUE; + break; + } + free((genericptr_t) selected); + } else { + search = FALSE; + searchbuf[0] = '\0'; + } + if (search) { + Strcpy(promptbuf, "Extended command list search phrase"); + Strcat(promptbuf, "?"); + getlin(promptbuf, searchbuf); + (void) mungspaces(searchbuf); + if (searchbuf[0] == '\033') + searchbuf[0] = '\0'; + if (strlen(searchbuf) > 0) + redisplay = TRUE; + search = FALSE; + } } - putstr(datawin, 0, ""); - putstr(datawin, 0, " Commands marked with a * will be autocompleted."); - display_nhwindow(datawin, FALSE); - destroy_nhwindow(datawin); + destroy_nhwindow(menuwin); return 0; } From 8ce81a27ef26e8d741946616edbd2db40c1fc6cb Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 24 Sep 2018 15:15:04 -0700 Subject: [PATCH 18/28] fix #H7397 - pronoun for unseen shopkeeper Most shop messages accurately identify the shopkeeper even when he or she can't be seen, but some also include a pronoun reference that ended up as "it" or "its" when not seen. Extend pronoun selection so that visibility can be ignored: noit_mhe(mon), noit_mhim(mon), and noit_mhis(mon). Note that despite being called noit_foo(), those will still return "it" if mon is neuter. "Accurately identify shopkeeper" is misleading if the hero is hallucinating; a random shopkeeper name is used then. noit_foo() yields the pronoun applicable to the actual shopkeeper and might not match the gender of a hallucinatory name. That could be fixed in a couple of ways (add shk_mhe()/shk_mhim()/shk_mhis() and either pass them the randomly chosen name so that they can figure out the appropriate gender, or just have them use a random gender whenever hallucinating) but I don't think that's worth bothering with. A bunch of shop messages needed noit_foo(); only a couple of those have actually been tested. A bunch more were using shkname() at the beginning of a sentence where Shknam() should be used instead. (All the existing shk names are already capitalized so there's no noticeable difference.) The three places outside shk.c and vault.c which directly use pronoun_gender() have been successfully tested. --- doc/fixes36.2 | 2 + include/extern.h | 2 +- include/you.h | 12 +++-- src/apply.c | 9 ++-- src/dothrow.c | 6 +-- src/mhitm.c | 7 +-- src/mhitu.c | 2 +- src/mondata.c | 7 ++- src/shk.c | 130 +++++++++++++++++++++++------------------------ src/vault.c | 7 +-- 10 files changed, 94 insertions(+), 90 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 727a067ea..479c7287b 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -140,6 +140,8 @@ known bear trap was being forgotten about by a player polymorphed into a flying monster if the player unsuccessfully tried to #untrap it and moved onto the trap square as a result no leash-related message is given when a leashed pet yellow light explodes +shop messages refer to shk by name even when shk is not visible but some + used pronoun "it" or "its" in same sentence; ditto for vault guards Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index b241ac074..bf3f801bd 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1458,7 +1458,7 @@ E int FDECL(monsndx, (struct permonst *)); E int FDECL(name_to_mon, (const char *)); E int FDECL(name_to_monclass, (const char *, int *)); E int FDECL(gender, (struct monst *)); -E int FDECL(pronoun_gender, (struct monst *)); +E int FDECL(pronoun_gender, (struct monst *, BOOLEAN_P)); E boolean FDECL(levl_follower, (struct monst *)); E int FDECL(little_to_big, (int)); E int FDECL(big_to_little, (int)); diff --git a/include/you.h b/include/you.h index 15fb5f9b3..461b9656f 100644 --- a/include/you.h +++ b/include/you.h @@ -236,12 +236,18 @@ struct Gender { increment to 3 if you allow neuter roles */ extern const struct Gender genders[]; /* table of available genders */ +/* pronouns for the hero */ #define uhe() (genders[flags.female ? 1 : 0].he) #define uhim() (genders[flags.female ? 1 : 0].him) #define uhis() (genders[flags.female ? 1 : 0].his) -#define mhe(mtmp) (genders[pronoun_gender(mtmp)].he) -#define mhim(mtmp) (genders[pronoun_gender(mtmp)].him) -#define mhis(mtmp) (genders[pronoun_gender(mtmp)].his) +/* corresponding pronouns for monsters; yields "it" when mtmp can't be seen */ +#define mhe(mtmp) (genders[pronoun_gender(mtmp, FALSE)].he) +#define mhim(mtmp) (genders[pronoun_gender(mtmp, FALSE)].him) +#define mhis(mtmp) (genders[pronoun_gender(mtmp, FALSE)].his) +/* override "it" if reason is lack of visibility rather than neuter species */ +#define noit_mhe(mtmp) (genders[pronoun_gender(mtmp, TRUE)].he) +#define noit_mhim(mtmp) (genders[pronoun_gender(mtmp, TRUE)].him) +#define noit_mhis(mtmp) (genders[pronoun_gender(mtmp, TRUE)].his) /*** Unified structure specifying alignment information ***/ struct Align { diff --git a/src/apply.c b/src/apply.c index 61a9db19d..0fa842636 100644 --- a/src/apply.c +++ b/src/apply.c @@ -215,12 +215,9 @@ int rx, ry, *resp; /* (most corpses don't retain the monster's sex, so we're usually forced to use generic pronoun here) */ if (mtmp) { - mptr = &mons[mtmp->mnum]; - /* can't use mhe() here; it calls pronoun_gender() which - expects monster to be on the map (visibility check) */ - if ((humanoid(mptr) || (mptr->geno & G_UNIQ) - || type_is_pname(mptr)) && !is_neuter(mptr)) - gndr = (int) mtmp->female; + mptr = mtmp->data = &mons[mtmp->mnum]; + /* TRUE: override visibility check--it's not on the map */ + gndr = pronoun_gender(mtmp, TRUE); } else { mptr = &mons[corpse->corpsenm]; if (is_female(mptr)) diff --git a/src/dothrow.c b/src/dothrow.c index 00978165f..5a559ae4e 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -664,12 +664,8 @@ int x, y; mon->mundetected = 0; /* wakeup() will handle mimic */ mnam = a_monnam(mon); /* after unhiding */ - pronoun = mhim(mon); + pronoun = noit_mhim(mon); if (!strcmp(mnam, "it")) { - /* mhim() uses pronoun_gender() which forces neuter if monster - can't be seen; we want him/her for humanoid sensed by touch */ - if (!strcmp(pronoun, "it") && humanoid(mon->data)) - pronoun = genders[mon->female].him; mnam = !strcmp(pronoun, "it") ? "something" : "someone"; } if (!glyph_is_monster(glyph) && !glyph_is_invisible(glyph)) diff --git a/src/mhitm.c b/src/mhitm.c index 87ee54e21..dfff12b26 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -47,9 +47,10 @@ mon_nam_too(outbuf, mon, other_mon) char *outbuf; struct monst *mon, *other_mon; { - Strcpy(outbuf, mon_nam(mon)); - if (mon == other_mon) - switch (pronoun_gender(mon)) { + if (mon != other_mon) + Strcpy(outbuf, mon_nam(mon)); + else + switch (pronoun_gender(mon, FALSE)) { case 0: Strcpy(outbuf, "himself"); break; diff --git a/src/mhitu.c b/src/mhitu.c index 97229ba60..7ae3ff103 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -2613,7 +2613,7 @@ struct monst *mon; ; } else if (rn2(20) < ACURR(A_CHA)) { pline("%s demands that you pay %s, but you refuse...", - noit_Monnam(mon), Blind ? (fem ? "her" : "him") : mhim(mon)); + noit_Monnam(mon), noit_mhim(mon)); } else if (u.umonnum == PM_LEPRECHAUN) { pline("%s tries to take your money, but fails...", noit_Monnam(mon)); } else { diff --git a/src/mondata.c b/src/mondata.c index 9a65c1480..ffffb9e85 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -900,10 +900,13 @@ register struct monst *mtmp; /* Like gender(), but lower animals and such are still "it". This is the one we want to use when printing messages. */ int -pronoun_gender(mtmp) +pronoun_gender(mtmp, override_vis) register struct monst *mtmp; +boolean override_vis; /* if True then 'no it' unless neuter */ { - if (is_neuter(mtmp->data) || !canspotmon(mtmp)) + if (!override_vis && !canspotmon(mtmp)) + return 2; + if (is_neuter(mtmp->data)) return 2; return (humanoid(mtmp->data) || (mtmp->data->geno & G_UNIQ) || type_is_pname(mtmp->data)) ? (int) mtmp->female : 2; diff --git a/src/shk.c b/src/shk.c index e0b7cae8b..6eb4ab71a 100644 --- a/src/shk.c +++ b/src/shk.c @@ -435,9 +435,9 @@ boolean newlev; plname); else pline("%s %s that you need to pay before leaving%s", - Shknam(shkp), - NOTANGRY(shkp) ? "points out" : "makes it clear", - NOTANGRY(shkp) ? "." : "!"); + Shknam(shkp), + NOTANGRY(shkp) ? "points out" : "makes it clear", + NOTANGRY(shkp) ? "." : "!"); return; } @@ -580,12 +580,12 @@ char *enterstring; return; /* no dialog */ if (Invis) { - pline("%s senses your presence.", shkname(shkp)); + pline("%s senses your presence.", Shknam(shkp)); if (!Deaf && !muteshk(shkp)) verbalize("Invisible customers are not welcome!"); else pline("%s stands firm as if %s knows you are there.", - Shknam(shkp), mhe(shkp)); + Shknam(shkp), noit_mhe(shkp)); return; } @@ -594,21 +594,18 @@ char *enterstring; if (ANGRY(shkp)) { if (!Deaf && !muteshk(shkp)) verbalize("So, %s, you dare return to %s %s?!", plname, - s_suffix(shkname(shkp)), shtypes[rt - SHOPBASE].name); + s_suffix(shkname(shkp)), shtypes[rt - SHOPBASE].name); else pline("%s seems %s over your return to %s %s!", - Shknam(shkp), - angrytexts[rn2(SIZE(angrytexts))], - mhis(shkp), - shtypes[rt - SHOPBASE].name); + Shknam(shkp), angrytexts[rn2(SIZE(angrytexts))], + noit_mhis(shkp), shtypes[rt - SHOPBASE].name); } else if (eshkp->robbed) { if (!Deaf) pline("%s mutters imprecations against shoplifters.", - shkname(shkp)); + Shknam(shkp)); else pline("%s is combing through %s inventory list.", - Shknam(shkp), - mhis(shkp)); + Shknam(shkp), noit_mhis(shkp)); } else { if (!Deaf && !muteshk(shkp)) verbalize("%s, %s! Welcome%s to %s %s!", Hello(shkp), plname, @@ -655,20 +652,20 @@ char *enterstring; tool, plur(cnt)); else pline("%s %s to let you in with your %s%s.", - Shknam(shkp), - NOTANGRY(shkp) ? "is hesitant" : "refuses", - tool, plur(cnt)); + Shknam(shkp), + NOTANGRY(shkp) ? "is hesitant" : "refuses", + tool, plur(cnt)); should_block = TRUE; } else if (u.usteed) { if (!Deaf && !muteshk(shkp)) verbalize(NOTANGRY(shkp) ? "Will you please leave %s outside?" : "Leave %s outside.", - y_monnam(u.usteed)); + y_monnam(u.usteed)); else pline("%s %s to let you in while you're riding %s.", - Shknam(shkp), - NOTANGRY(shkp) ? "doesn't want" : "refuses", - y_monnam(u.usteed)); + Shknam(shkp), + NOTANGRY(shkp) ? "doesn't want" : "refuses", + y_monnam(u.usteed)); should_block = TRUE; } else { should_block = @@ -1343,7 +1340,7 @@ proceed: } else { if (umoney > ltmp) { You("give %s the %ld gold piece%s %s asked for.", - shkname(shkp), ltmp, plur(ltmp), mhe(shkp)); + shkname(shkp), ltmp, plur(ltmp), noit_mhe(shkp)); pay(ltmp, shkp); } else { You("give %s all your%s gold.", shkname(shkp), @@ -1353,7 +1350,8 @@ proceed: pline("But you have hidden gold!"); } if ((umoney < ltmp / 2L) || (umoney < ltmp && stashed_gold)) - pline("Unfortunately, %s doesn't look satisfied.", mhe(shkp)); + pline("Unfortunately, %s doesn't look satisfied.", + noit_mhe(shkp)); else make_happy_shk(shkp, FALSE); } @@ -1373,13 +1371,14 @@ proceed: if (!umoney) pline(no_money, stashed_gold ? " seem to" : ""); else - pline(not_enough_money, mhim(shkp)); + pline(not_enough_money, noit_mhim(shkp)); return 1; } - pline("But since %s shop has been robbed recently,", mhis(shkp)); + pline("But since %s shop has been robbed recently,", + noit_mhis(shkp)); pline("you %scompensate %s for %s losses.", (umoney < ltmp) ? "partially " : "", shkname(shkp), - mhis(shkp)); + noit_mhis(shkp)); pay(umoney < ltmp ? umoney : ltmp, shkp); make_happy_shk(shkp, FALSE); } else { @@ -1390,11 +1389,14 @@ proceed: if (!umoney) pline(no_money, stashed_gold ? " seem to" : ""); else - pline(not_enough_money, mhim(shkp)); + pline(not_enough_money, noit_mhim(shkp)); return 1; } You("try to appease %s by giving %s 1000 gold pieces.", - x_monnam(shkp, ARTICLE_THE, "angry", 0, FALSE), mhim(shkp)); + canspotmon(shkp) + ? x_monnam(shkp, ARTICLE_THE, "angry", 0, FALSE) + : shkname(shkp), + noit_mhim(shkp)); pay(1000L, shkp); if (strncmp(eshkp->customer, plname, PL_NSIZ) || rn2(3)) make_happy_shk(shkp, FALSE); @@ -1538,13 +1540,13 @@ proceed: } if (!ANGRY(shkp) && paid) { if (!Deaf && !muteshk(shkp)) - verbalize("Thank you for shopping in %s %s!", s_suffix(shkname(shkp)), - shtypes[eshkp->shoptype - SHOPBASE].name); + verbalize("Thank you for shopping in %s %s!", + s_suffix(shkname(shkp)), + shtypes[eshkp->shoptype - SHOPBASE].name); else pline("%s nods appreciatively at you for shopping in %s %s!", - Shknam(shkp), - mhis(shkp), - shtypes[eshkp->shoptype - SHOPBASE].name); + Shknam(shkp), noit_mhis(shkp), + shtypes[eshkp->shoptype - SHOPBASE].name); } return 1; } @@ -1778,7 +1780,7 @@ int croaked; if (cansee(shkp->mx, shkp->my) && croaked) { takes[0] = '\0'; if (has_head(shkp->data) && !rn2(2)) - Sprintf(takes, ", shakes %s %s,", mhis(shkp), + Sprintf(takes, ", shakes %s %s,", noit_mhis(shkp), mbodypart(shkp, HEAD)); pline("%s %slooks at your corpse%s and %s.", Shknam(shkp), (!shkp->mcanmove || shkp->msleeping) ? "wakes up, " : "", @@ -1797,7 +1799,7 @@ int croaked; taken = (invent != 0); if (taken) pline("%s gratefully inherits all your possessions.", - shkname(shkp)); + Shknam(shkp)); set_repo_loc(shkp); goto clear; } @@ -1828,7 +1830,7 @@ int croaked; if (umoney > 0) money2mon(shkp, umoney); context.botl = 1; - pline("%s %s all your possessions.", shkname(shkp), takes); + pline("%s %s all your possessions.", Shknam(shkp), takes); taken = TRUE; /* where to put player's invent (after disclosure) */ set_repo_loc(shkp); @@ -1838,7 +1840,7 @@ int croaked; pline("%s %s the %ld %s %sowed %s.", Shknam(shkp), takes, loss, currency(loss), strncmp(eshkp->customer, plname, PL_NSIZ) ? "" : "you ", - mhim(shkp)); + noit_mhim(shkp)); /* shopkeeper has now been paid in full */ pacify_shk(shkp); eshkp->following = 0; @@ -2218,9 +2220,8 @@ boolean quietly; verbalize("I won't stock that. Take it out of here!"); else pline("%s shakes %s %s in refusal.", - Shknam(shkp), - mhis(shkp), - mbodypart(shkp, HEAD)); + Shknam(shkp), noit_mhis(shkp), + mbodypart(shkp, HEAD)); } } return TRUE; @@ -3010,7 +3011,7 @@ xchar x, y; if (sell_how == SELL_NORMAL || auto_credit) { c = sell_response = 'y'; } else if (sell_response != 'n') { - pline("%s cannot pay you at present.", shkname(shkp)); + pline("%s cannot pay you at present.", Shknam(shkp)); Sprintf(qbuf, "Will you accept %ld %s in credit for ", tmpcr, currency(tmpcr)); c = ynaq(safe_qbuf(qbuf, qbuf, "?", obj, doname, thesimpleoname, @@ -3079,7 +3080,7 @@ xchar x, y; "... your items in the . Sell them?" */ Sprintf(qbuf, "%s offers%s %ld gold piece%s for %s%s ", - shkname(shkp), short_funds ? " only" : "", offer, + Shknam(shkp), short_funds ? " only" : "", offer, plur(offer), (cltmp && !ltmp) ? ((yourc == 1L) ? "your item in " : "your items in ") @@ -3650,8 +3651,7 @@ struct monst *shkp; Hello(shkp), plname); else pline("%s holds out %s upturned %s.", - Shknam(shkp), - mhis(shkp), + Shknam(shkp), noit_mhis(shkp), mbodypart(shkp, HAND)); followmsg = moves; if (!rn2(9)) { @@ -3805,7 +3805,7 @@ register int fall; */ if (lang == 2) pline("%s curses %s inability to grab your backpack!", - shkname(shkp), mhim(shkp)); + Shknam(shkp), noit_mhim(shkp)); rile_shk(shkp); return; #endif @@ -3816,16 +3816,16 @@ register int fall; if (distu(shkp->mx, shkp->my) > 2) { if (lang == 2) pline("%s curses you in anger and frustration!", - shkname(shkp)); + Shknam(shkp)); else if (lang == 1) growl(shkp); rile_shk(shkp); return; } else - pline("%s %s, and %s your backpack!", shkname(shkp), + pline("%s %s, and %s your backpack!", Shknam(shkp), makeplural(locomotion(shkp->data, "leap")), grabs); } else - pline("%s %s your backpack!", shkname(shkp), grabs); + pline("%s %s your backpack!", Shknam(shkp), grabs); for (obj = invent; obj; obj = obj2) { obj2 = obj->nobj; @@ -3957,7 +3957,7 @@ boolean cant_mollify; if (uinshp) { if (um_dist(shkp->mx, shkp->my, 1) && !um_dist(shkp->mx, shkp->my, 3)) { - pline("%s leaps towards you!", shkname(shkp)); + pline("%s leaps towards you!", Shknam(shkp)); mnexto(shkp); } pursue = um_dist(shkp->mx, shkp->my, 1); @@ -4004,16 +4004,16 @@ boolean cant_mollify; else pline("%s is %s that you decided to %s %s %s!", Shknam(shkp), angrytexts[rn2(SIZE(angrytexts))], - dmgstr, mhis(shkp), dugwall ? "shop" : "door"); + dmgstr, noit_mhis(shkp), dugwall ? "shop" : "door"); } else { if (!Deaf) { - pline("%s shouts:", shkname(shkp)); + pline("%s shouts:", Shknam(shkp)); verbalize("Who dared %s my %s?", dmgstr, dugwall ? "shop" : "door"); } else { pline("%s is %s that someone decided to %s %s %s!", Shknam(shkp), angrytexts[rn2(SIZE(angrytexts))], - dmgstr, mhis(shkp), dugwall ? "shop" : "door"); + dmgstr, noit_mhis(shkp), dugwall ? "shop" : "door"); } } hot_pursuit(shkp); @@ -4039,10 +4039,8 @@ boolean cant_mollify; verbalize("Oh, yes! You'll pay!"); else pline("%s lunges %s %s toward your %s!", - Shknam(shkp), - mhis(shkp), - mbodypart(shkp, HAND), - body_part(NECK)); + Shknam(shkp), noit_mhis(shkp), + mbodypart(shkp, HAND), body_part(NECK)); } else growl(shkp); hot_pursuit(shkp); @@ -4221,9 +4219,9 @@ struct monst *shkp; eshk = ESHK(shkp); if (ANGRY(shkp)) { pline("%s %s how much %s dislikes %s customers.", - shkname(shkp), + Shknam(shkp), (!Deaf && !muteshk(shkp)) ? "mentions" : "indicates", - mhe(shkp), eshk->robbed ? "non-paying" : "rude"); + noit_mhe(shkp), eshk->robbed ? "non-paying" : "rude"); } else if (eshk->following) { if (strncmp(eshk->customer, plname, PL_NSIZ)) { if (!Deaf && !muteshk(shkp)) @@ -4242,35 +4240,35 @@ struct monst *shkp; register long total = addupbill(shkp) + eshk->debit; pline("%s %s that your bill comes to %ld %s.", - shkname(shkp), + Shknam(shkp), (!Deaf && !muteshk(shkp)) ? "says" : "indicates", total, currency(total)); } else if (eshk->debit) { pline("%s %s that you owe %s %ld %s.", - shkname(shkp), + Shknam(shkp), (!Deaf && !muteshk(shkp)) ? "reminds you" : "indicates", - mhim(shkp), eshk->debit, currency(eshk->debit)); + noit_mhim(shkp), eshk->debit, currency(eshk->debit)); } else if (eshk->credit) { pline("%s encourages you to use your %ld %s of credit.", - shkname(shkp), eshk->credit, currency(eshk->credit)); + Shknam(shkp), eshk->credit, currency(eshk->credit)); } else if (eshk->robbed) { pline("%s %s about a recent robbery.", Shknam(shkp), (!Deaf && !muteshk(shkp)) ? "complains" : "indicates concern"); } else if ((shkmoney = money_cnt(shkp->minvent)) < 50L) { pline("%s %s that business is bad.", - shkname(shkp), + Shknam(shkp), (!Deaf && !muteshk(shkp)) ? "complains" : "indicates"); } else if (shkmoney > 4000) { pline("%s %s that business is good.", - shkname(shkp), + Shknam(shkp), (!Deaf && !muteshk(shkp)) ? "says" : "indicates"); } else if (is_izchak(shkp, FALSE)) { if (!Deaf && !muteshk(shkp)) pline(Izchak_speaks[rn2(SIZE(Izchak_speaks))], shkname(shkp)); } else { if (!Deaf && !muteshk(shkp)) - pline("%s talks about the problem of shoplifters.", shkname(shkp)); + pline("%s talks about the problem of shoplifters.", Shknam(shkp)); } } @@ -4472,7 +4470,7 @@ register xchar x, y; && ESHK(shkp)->shd.y == y && shkp->mcanmove && !shkp->msleeping && (ESHK(shkp)->debit || ESHK(shkp)->billct || ESHK(shkp)->robbed)) { - pline("%s%s blocks your way!", shkname(shkp), + pline("%s%s blocks your way!", Shknam(shkp), Invis ? " senses your motion and" : ""); return TRUE; } @@ -4509,7 +4507,7 @@ register xchar x, y; && (x == sx - 1 || x == sx + 1 || y == sy - 1 || y == sy + 1) && (Invis || carrying(PICK_AXE) || carrying(DWARVISH_MATTOCK) || u.usteed)) { - pline("%s%s blocks your way!", shkname(shkp), + pline("%s%s blocks your way!", Shknam(shkp), Invis ? " senses your motion and" : ""); return TRUE; } diff --git a/src/vault.c b/src/vault.c index 22bf15e9c..eea24737d 100644 --- a/src/vault.c +++ b/src/vault.c @@ -452,7 +452,8 @@ invault() if (!Blind) pline( "%s holds out %s palm and beckons with %s other hand.", - noit_Monnam(guard), mhis(guard), mhis(guard)); + noit_Monnam(guard), noit_mhis(guard), + noit_mhis(guard)); } else { verbalize( "Most likely all your gold was stolen from this vault."); @@ -707,7 +708,7 @@ register struct monst *grd; if (Deaf) { if (!Blind) pline("%s holds out %s palm demandingly!", - noit_Monnam(grd), mhis(grd)); + noit_Monnam(grd), noit_mhis(grd)); } else { verbalize("Drop all your gold, scoundrel!"); } @@ -716,7 +717,7 @@ register struct monst *grd; if (Deaf) { if (!Blind) pline("%s rubs %s hands with enraged delight!", - noit_Monnam(grd), mhis(grd)); + noit_Monnam(grd), noit_mhis(grd)); } else { verbalize("So be it, rogue!"); } From d119eca297c1818745c2462af4ffa5b78e9d1807 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 24 Sep 2018 17:06:04 -0700 Subject: [PATCH 19/28] simplify #wizidentify Get rid of bold/non-bold distinction in #wizidentify inventory menu by only showing items which aren't yet fully identified instead of full inventory with bold for unID'd. Support for bold text might be lacking. I was considering this even before the report that X11 menus ignore attribute. The "_ - (use ^I for all)" menu entry is still present, but it could be discarded in favor of '.' to pick everything via ordinary menu selection. --- doc/fixes36.2 | 5 +++-- src/cmd.c | 9 +++------ src/invent.c | 44 ++++++++++++++++++++++---------------------- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 479c7287b..51e27582f 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -150,14 +150,15 @@ fix access violation when --debug:xxxx has no other args after it setting the inverse attribute for gold had the space before "$:" getting highlighted along with the gold field sortloot segfaulted when filtering a subset of items (seen with 'A' command) +orctown: prevent Bad fruit #0 and some minor tuning +make long extended commands list be more navigable +simplify #wizidentify; don't rely on having bold menu entries tty: turn off an optimization that is the suspected cause of Windows reported partial status lines following level changes tty: ensure that current status fields are always copied to prior status values so that comparisons are correct -orctown: prevent Bad fruit #0 and some minor tuning X11: its use of genl_status_update exposed a negative index use that could lead to a segfault -make long extended commands list be more navigable Platform- and/or Interface-Specific Fixes diff --git a/src/cmd.c b/src/cmd.c index c698b0647..d52535667 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -747,14 +747,11 @@ wiz_identify(VOID_ARGS) /* command remapping might leave #wizidentify as the only way to invoke us, in which case cmd_from_func() will yield NUL; it won't matter to display_inventory()/display_pickinv() - if ^I invokes some other command--what matters is that it - is never an inventory letter */ + if ^I invokes some other command--what matters is that + display_pickinv() and xname() see override_ID as nonzero */ if (!iflags.override_ID) iflags.override_ID = C('I'); - /* C('I') == ^I == default keystroke for wiz_identify; - it doesn't matter whether the command has been remapped */ - if (display_inventory((char *) 0, TRUE) == C('I')) - identify_pack(0, FALSE); + (void) display_inventory((char *) 0, FALSE); iflags.override_ID = 0; } else pline("Unavailable command '%s'.", diff --git a/src/invent.c b/src/invent.c index 4262a5153..84e6367df 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2593,24 +2593,27 @@ long *out_cnt; char prompt[QBUFSZ]; unid_cnt = count_unidentified(invent); - add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, - "Debug Identify", - MENU_UNSELECTED); + Sprintf(prompt, "Debug Identify"); /* 'title' rather than 'prompt' */ + if (unid_cnt) + Sprintf(eos(prompt), + " -- unidentified or partially identified item%s", + plur(unid_cnt)); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt, MENU_UNSELECTED); if (!unid_cnt) { add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "(all items are permanently identified already)", MENU_UNSELECTED); } else { any.a_obj = &wizid_fakeobj; + Sprintf(prompt, "select %s to permanently identify", + (unid_cnt == 1) ? "it": "any or all of them"); /* wiz_identify stuffed the wiz_identify command character (^I) into iflags.override_ID for our use as an accelerator; - it could be ambiguous as a selector but the only time it - is wanted is in case where no item is being selected */ - Sprintf(prompt, - "Select %sthe %d bolded item%s to permanently identify (%s for all)", - (unid_cnt == 1) ? "": "any of ", unid_cnt, - (unid_cnt > 1) ? "s" : "", - visctrl(iflags.override_ID)); + it could be ambiguous if player has assigned a letter to + the #wizidentify command */ + if (unid_cnt > 1) + Sprintf(eos(prompt), " (%s for all)", + visctrl(iflags.override_ID)); add_menu(win, NO_GLYPH, &any, '_', iflags.override_ID, ATR_NONE, prompt, MENU_UNSELECTED); wizid = TRUE; @@ -2630,6 +2633,8 @@ nextclass: if (lets && !index(lets, otmp->invlet)) continue; if (!flags.sortpack || otmp->oclass == *invlet) { + if (wizid && !not_fully_identified(otmp)) + continue; any = zeroany; /* all bits zero */ ilet = otmp->invlet; if (flags.sortpack && !classcount) { @@ -2643,9 +2648,7 @@ nextclass: any.a_obj = otmp; else any.a_char = ilet; - add_menu(win, obj_to_glyph(otmp), &any, ilet, 0, - (wizid && not_fully_identified(otmp)) ? - ATR_BOLD : ATR_NONE, + add_menu(win, obj_to_glyph(otmp), &any, ilet, 0, ATR_NONE, doname(otmp), MENU_UNSELECTED); } } @@ -2678,21 +2681,18 @@ nextclass: } end_menu(win, query && *query ? query : (char *) 0); - n = select_menu(win, wizid ? PICK_ANY : - want_reply ? PICK_ONE : PICK_NONE, &selected); + n = select_menu(win, + wizid ? PICK_ANY : want_reply ? PICK_ONE : PICK_NONE, + &selected); if (n > 0) { if (wizid) { - int i = n; + int i; ret = '\0'; - while (--i >= 0) { + for (i = 0; i < n; ++i) { otmp = selected[i].item.a_obj; if (otmp == &wizid_fakeobj) { - /* C('I') == ^I == default keystroke for wiz_identify; - it is guaranteed not to be in use as an inventory letter - (wiz_identify might be remapped to an ordinary letter, - making iflags.override_ID ambiguous as a return value) */ - ret = C('I'); + identify_pack(0, FALSE); } else { if (not_fully_identified(otmp)) (void) identify(otmp); From d86e9eaec405049d1e5e931f5aceb6ad6da5e307 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 25 Sep 2018 03:44:24 -0700 Subject: [PATCH 20/28] extended commands revision Reorganize the logic for showing or suppressing an extended command to avoid a slightly hairy 'foo || bar && quux' expression. When searching and not finding anything, report "no matches" rather just waiting for another menu selection. Plus miscellaneous reformatting. --- src/cmd.c | 138 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 77 insertions(+), 61 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index d52535667..1c65ec71f 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -371,11 +371,11 @@ doextlist(VOID_ARGS) winid menuwin; anything any; menu_item *selected; - int n, pass, maxpass = wizard ? 2 : 1; - int menumode = 0, menushown[2] = {0,0}, onelist = 0; + int n, pass; + int menumode = 0, menushown[2], onelist = 0; boolean redisplay = TRUE, search = FALSE; - const char *headings[] = {"Extended commands", - "Debugging Extended Commands"}; + static const char *headings[] = { "Extended commands", + "Debugging Extended Commands" }; searchbuf[0] = '\0'; menuwin = create_nhwindow(NHW_MENU); @@ -386,24 +386,24 @@ doextlist(VOID_ARGS) start_menu(menuwin); add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "Extended Commands List", MENU_UNSELECTED); - add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); Strcpy(buf, menumode ? "Show" : "Hide"); Strcat(buf, " commands that don't autocomplete"); if (!menumode) - Strcat(buf, " (those not marked with [A] below)"); + Strcat(buf, " (those not marked with [A])"); any.a_int = 1; - add_menu(menuwin, NO_GLYPH, &any, 'a', 0, ATR_NONE, buf, MENU_UNSELECTED); + add_menu(menuwin, NO_GLYPH, &any, 'a', 0, ATR_NONE, buf, + MENU_UNSELECTED); - if (strlen(searchbuf) == 0) { + if (!*searchbuf) { any.a_int = 2; add_menu(menuwin, NO_GLYPH, &any, 's', 0, ATR_NONE, "Search extended commands", MENU_UNSELECTED); } else { Strcpy(buf, "Show all, clear search"); - if ((strlen(buf) + strlen(searchbuf) + strlen(" (\"\")")) < QBUFSZ) + if (strlen(buf) + strlen(searchbuf) + strlen(" (\"\")") < QBUFSZ) Sprintf(eos(buf), " (\"%s\")", searchbuf); any.a_int = 3; add_menu(menuwin, NO_GLYPH, &any, 's', 0, ATR_NONE, @@ -412,72 +412,88 @@ doextlist(VOID_ARGS) if (wizard) { any.a_int = 4; add_menu(menuwin, NO_GLYPH, &any, 'z', 0, ATR_NONE, - onelist ? "Show debugging commands in separate section" : - "Show all alphabetically, including debugging commands", + onelist ? "Show debugging commands in separate section" + : "Show all alphabetically, including debugging commands", MENU_UNSELECTED); } any = zeroany; add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); menushown[0] = menushown[1] = 0; - for (pass = 0; pass < maxpass; ++pass) { + n = 0; + for (pass = 0; pass <= 1; ++pass) { + /* skip second pass if not in wizard mode or wizard mode + commands are being integrated into a single list */ + if (pass == 1 && (onelist || !wizard)) + break; for (efp = extcmdlist; efp->ef_txt; efp++) { - boolean showit = (onelist || - (!menumode || - (menumode && (efp->flags & AUTOCOMPLETE))) && - ((!pass && !(efp->flags & WIZMODECMD)) || - (pass && (efp->flags & WIZMODECMD)))); + int wizc; - if (strlen(searchbuf) > 0) { - if (!((strstri(efp->ef_txt, searchbuf) != 0) || - (strstri(efp->ef_desc, searchbuf) != 0))) - showit = FALSE; + /* if hiding non-autocomplete commands, skip such */ + if (menumode == 1 && (efp->flags & AUTOCOMPLETE) == 0) + continue; + /* if searching, skip this command if it doesn't match */ + if (*searchbuf + && !strstri(efp->ef_txt, searchbuf) + && !strstri(efp->ef_desc, searchbuf)) + continue; + /* skip wizard mode commands if not in wizard mode; + when showing two sections, skip wizard mode commands + in pass==0 and skip other commands in pass==1 */ + wizc = (efp->flags & WIZMODECMD) != 0; + if (wizc && !wizard) + continue; + if (!onelist && pass != wizc) + continue; + + /* We're about to show an item, have we shown the menu yet? + Doing menu in inner loop like this on demand avoids a + heading with no subordinate entries on the search + results menu. */ + if (!menushown[pass]) { + Strcpy(buf, headings[pass]); + add_menu(menuwin, NO_GLYPH, &any, 0, 0, + iflags.menu_headings, buf, MENU_UNSELECTED); + menushown[pass] = 1; } - if (showit) { - /* We're about to show an item, have we shown the menu yet? - Doing menu in inner loop like this on demand avoids a - heading with no subordinate entries on the search - results menu */ - if (!menushown[pass]) { - Strcpy(buf, headings[pass]); - add_menu(menuwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, - buf, MENU_UNSELECTED); - menushown[pass] = 1; - } - Sprintf(buf, " %-14s %-3s %s", - efp->ef_txt, - (efp->flags & AUTOCOMPLETE) ? "[A]" : " ", - efp->ef_desc); - add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + Sprintf(buf, " %-14s %-3s %s", + efp->ef_txt, + (efp->flags & AUTOCOMPLETE) ? "[A]" : " ", + efp->ef_desc); + add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); - } + ++n; } - add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - "", MENU_UNSELECTED); + if (n) + add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + "", MENU_UNSELECTED); } + if (*searchbuf && !n) + add_menu(menuwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + "no matches", MENU_UNSELECTED); + end_menu(menuwin, (char *) 0); n = select_menu(menuwin, PICK_ONE, &selected); if (n > 0) { - switch(selected[0].item.a_int) { - case 1: - menumode = 1 - menumode; /* toggle 0 -> 1, 1 -> 0 */ - redisplay = TRUE; - break; - case 2: - search = TRUE; - break; - case 3: - search = FALSE; - searchbuf[0] = '\0'; - redisplay = TRUE; - break; - case 4: - search = FALSE; - searchbuf[0] = '\0'; - onelist = 1 - onelist; /* toggle 0 -> 1, 1 -> 0 */ - maxpass = onelist ? 1 : wizard ? 2 : 1; - redisplay = TRUE; - break; + switch (selected[0].item.a_int) { + case 1: /* 'a': toggle show/hide non-autocomplete */ + menumode = 1 - menumode; /* toggle 0 -> 1, 1 -> 0 */ + redisplay = TRUE; + break; + case 2: /* 's' when not searching yet: enable search */ + search = TRUE; + break; + case 3: /* 's' when already searching: disable search */ + search = FALSE; + searchbuf[0] = '\0'; + redisplay = TRUE; + break; + case 4: /* 'z': toggle showing wizard mode commands separately */ + search = FALSE; + searchbuf[0] = '\0'; + onelist = 1 - onelist; /* toggle 0 -> 1, 1 -> 0 */ + redisplay = TRUE; + break; } free((genericptr_t) selected); } else { @@ -491,7 +507,7 @@ doextlist(VOID_ARGS) (void) mungspaces(searchbuf); if (searchbuf[0] == '\033') searchbuf[0] = '\0'; - if (strlen(searchbuf) > 0) + if (*searchbuf) redisplay = TRUE; search = FALSE; } From 3a62075070d4560b013ddd08f20aa7da859e6de6 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 25 Sep 2018 16:43:06 -0700 Subject: [PATCH 21/28] fix #H7136 - iron bars vs non-diggable walls Iron bars can be destroyed in some circumstances (hit by yellow dragon breath or thrown potion of acid, being eaten by rust monser or black pudding, or by poly'd hero in those forms) and should act like walls for diggable/non-diggable purposes. But they aren't walls, so the non-diggable flag was not being set for them by the special level loader. Even once that was changed, they weren't being handled consistently. Some places checked for non-diggable directly (zap_over_floor of acid breath, potion of acid hitting bars) and started working as intended, others used may_dig() to check non-diggable (poly'd hero attempting to eat iron bars) but it doesn't handle iron bars, and still others didn't check at all (bars-eating monster who moved onto bars location in expectation of eating those next). --- doc/fixes36.2 | 2 ++ include/rm.h | 2 +- src/hack.c | 9 ++++++--- src/mon.c | 6 +++++- src/monmove.c | 3 ++- src/sp_lev.c | 19 +++++++++++++++---- 6 files changed, 31 insertions(+), 10 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 51e27582f..ef5053811 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -142,6 +142,8 @@ known bear trap was being forgotten about by a player polymorphed into a no leash-related message is given when a leashed pet yellow light explodes shop messages refer to shk by name even when shk is not visible but some used pronoun "it" or "its" in same sentence; ditto for vault guards +poly'd hero and monsters could eat through iron bars in areas where walls + were flagged as non-diggable Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/rm.h b/include/rm.h index 9d80551db..7fd41c47c 100644 --- a/include/rm.h +++ b/include/rm.h @@ -381,7 +381,7 @@ extern struct symsetentry symset[NUM_GRAPHICS]; /* from drawing.c */ #define DB_UNDER 28 /* mask for underneath */ /* - * Wall information. + * Wall information. Nondiggable also applies to iron bars. */ #define WM_MASK 0x07 /* wall mode (bottom three bits) */ #define W_NONDIGGABLE 0x08 diff --git a/src/hack.c b/src/hack.c index 70671b45f..fabd7e17f 100644 --- a/src/hack.c +++ b/src/hack.c @@ -364,8 +364,8 @@ moverock() /* * still_chewing() * - * Chew on a wall, door, or boulder. Returns TRUE if still eating, FALSE - * when done. + * Chew on a wall, door, or boulder. [What about statues?] + * Returns TRUE if still eating, FALSE when done. */ STATIC_OVL int still_chewing(x, y) @@ -379,7 +379,10 @@ xchar x, y; (void) memset((genericptr_t) &context.digging, 0, sizeof (struct dig_info)); - if (!boulder && IS_ROCK(lev->typ) && !may_dig(x, y)) { + if (!boulder + && ((IS_ROCK(lev->typ) && !may_dig(x, y)) + /* may_dig() checks W_NONDIGGABLE but doesn't handle iron bars */ + || (lev->typ == IRONBARS && (lev->wall_info & W_NONDIGGABLE)))) { You("hurt your teeth on the %s.", (lev->typ == IRONBARS) ? "bars" diff --git a/src/mon.c b/src/mon.c index 9cdf9b6d9..72f3df1a6 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1320,7 +1320,11 @@ nexttry: /* eels prefer the water, but if there is no water nearby, && !((IS_TREE(ntyp) ? treeok : rockok) && may_dig(nx, ny))) continue; /* KMH -- Added iron bars */ - if (ntyp == IRONBARS && !(flag & ALLOW_BARS)) + if (ntyp == IRONBARS + && (!(flag & ALLOW_BARS) + || ((levl[nx][ny].wall_info & W_NONDIGGABLE) + && (dmgtype(mdat, AD_RUST) + || dmgtype(mdat, AD_CORR))))) continue; if (IS_DOOR(ntyp) && !(amorphous(mdat) || can_fog(mon)) && (((levl[nx][ny].doormask & D_CLOSED) && !(flag & OPENDOOR)) diff --git a/src/monmove.c b/src/monmove.c index 762b4ecb9..fc32ebfb8 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1388,7 +1388,8 @@ postmov: add_damage(mtmp->mx, mtmp->my, 0L); } } else if (levl[mtmp->mx][mtmp->my].typ == IRONBARS) { - if (may_dig(mtmp->mx, mtmp->my) + /* 3.6.2: was using may_dig() but it doesn't handle bars */ + if (!(levl[mtmp->mx][mtmp->my].wall_info & W_NONDIGGABLE) && (dmgtype(ptr, AD_RUST) || dmgtype(ptr, AD_CORR))) { if (canseemon(mtmp)) pline("%s eats through the iron bars.", Monnam(mtmp)); diff --git a/src/sp_lev.c b/src/sp_lev.c index 1d6b0cd3a..11d692501 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -621,6 +621,7 @@ schar filling; schar lit; { int x, y; + for (x = 2; x <= x_maze_max; x++) for (y = 0; y <= y_maze_max; y++) { SET_TYPLIT(x, y, filling, lit); @@ -636,11 +637,21 @@ xchar x1, y1, x2, y2; int prop; { register xchar x, y; + struct rm *lev; - for (y = max(y1, 0); y <= min(y2, ROWNO - 1); y++) - for (x = max(x1, 0); x <= min(x2, COLNO - 1); x++) - if (IS_STWALL(levl[x][y].typ) || IS_TREE(levl[x][y].typ)) - levl[x][y].wall_info |= prop; + x1 = max(x1, 1); + x2 = min(x2, COLNO - 1); + y1 = max(y1, 0); + y2 = min(y2, ROWNO - 1); + for (y = y1; y <= y2; y++) + for (x = x1; x <= x2; x++) { + lev = &levl[x][y]; + if (IS_STWALL(lev->typ) || IS_TREE(lev->typ) + /* 3.6.2: made iron bars eligible to be flagged nondiggable + (checked by chewing(hack.c) and zap_over_floor(zap.c)) */ + || lev->typ == IRONBARS) + lev->wall_info |= prop; + } } STATIC_OVL void From 64ebb9cebad63bc826736083f929675fdb9ca599 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 25 Sep 2018 18:01:44 -0700 Subject: [PATCH 22/28] fix #7414 - bug with Japanese names for Samurai Description for use when an item hasn't been seen up close yet falls back to real name if there is no separate description, but was doing so before real name substitution for samurai. actualn = foo; dn = description ? description : actualn; if (Samurai) actualn = bar; So player saw a flail (via 'dn') until dknown bit got set, then nunchaku (via 'actualn' after it got set to samurai-specific value). Wait until after substitution of Japanese real names before falling back to real name when there's no description. --- doc/fixes36.2 | 2 ++ src/objnam.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index ef5053811..ec4b0fb95 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -144,6 +144,8 @@ shop messages refer to shk by name even when shk is not visible but some used pronoun "it" or "its" in same sentence; ditto for vault guards poly'd hero and monsters could eat through iron bars in areas where walls were flagged as non-diggable +Samurai seeing items at a distance could have them be described by their + ordinary names rather than by their Japanese names Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/objnam.c b/src/objnam.c index 46fc3d493..ce952c36f 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -403,7 +403,7 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */ register struct objclass *ocl = &objects[typ]; int nn = ocl->oc_name_known, omndx = obj->corpsenm; const char *actualn = OBJ_NAME(*ocl); - const char *dn = OBJ_DESCR(*ocl) ? OBJ_DESCR(*ocl) : actualn; + const char *dn = OBJ_DESCR(*ocl); const char *un = ocl->oc_uname; boolean pluralize = (obj->quan != 1L) && !(cxn_flags & CXN_SINGULAR); boolean known, dknown, bknown; @@ -411,6 +411,10 @@ unsigned cxn_flags; /* bitmask of CXN_xxx values */ buf = nextobuf() + PREFIX; /* leave room for "17 -3 " */ if (Role_if(PM_SAMURAI) && Japanese_item_name(typ)) actualn = Japanese_item_name(typ); + /* 3.6.2: this used to be part of 'dn's initialization, but it + needs to come after possibly overriding 'actualn' */ + if (!dn) + dn = actualn; buf[0] = '\0'; /* From 039ad51660dc8f1080b3ffa68e12e01fae30eb2c Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 25 Sep 2018 23:08:09 -0400 Subject: [PATCH 23/28] don't impact player stats with wizard mode ^T --- doc/fixes36.2 | 4 +++- include/extern.h | 3 ++- src/cmd.c | 4 ++-- src/dig.c | 2 +- src/teleport.c | 23 ++++++++++++++++------- src/trap.c | 2 +- 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index ec4b0fb95..79656ce1f 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -146,7 +146,9 @@ poly'd hero and monsters could eat through iron bars in areas where walls were flagged as non-diggable Samurai seeing items at a distance could have them be described by their ordinary names rather than by their Japanese names - +wizard mode ^T shouldn't have been diminishing player power but it was + and hilite_status:power settings really drew attention to that + Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository ------------------------------------------------------------------ diff --git a/include/extern.h b/include/extern.h index bf3f801bd..345442345 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2359,7 +2359,8 @@ E boolean FDECL(safe_teleds, (BOOLEAN_P)); E boolean FDECL(teleport_pet, (struct monst *, BOOLEAN_P)); E void NDECL(tele); E boolean FDECL(scrolltele, (struct obj *)); -E int NDECL(dotele); +E int NDECL(dotelecmd); +E int FDECL(dotele, (BOOLEAN_P)); E void NDECL(level_tele); E void FDECL(domagicportal, (struct trap *)); E void FDECL(tele_trap, (struct trap *)); diff --git a/src/cmd.c b/src/cmd.c index 1c65ec71f..74ee87fec 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -116,7 +116,7 @@ extern int NDECL(dosit); /**/ extern int NDECL(dotalk); /**/ extern int NDECL(docast); /**/ extern int NDECL(dovspell); /**/ -extern int NDECL(dotele); /**/ +extern int NDECL(dotelecmd); /**/ extern int NDECL(dountrap); /**/ extern int NDECL(doversion); /**/ extern int NDECL(doextversion); /**/ @@ -3133,7 +3133,7 @@ struct ext_func_tab extcmdlist[] = { { 'x', "swap", "swap wielded and secondary weapons", doswapweapon }, { 'T', "takeoff", "take off one piece of armor", dotakeoff }, { 'A', "takeoffall", "remove all armor", doddoremarm }, - { C('t'), "teleport", "teleport around the level", dotele, IFBURIED }, + { C('t'), "teleport", "teleport around the level", dotelecmd, IFBURIED }, { '\0', "terrain", "show map without obstructions", doterrain, IFBURIED | AUTOCOMPLETE }, { '\0', "therecmdmenu", diff --git a/src/dig.c b/src/dig.c index 0a1669813..a89b354be 100644 --- a/src/dig.c +++ b/src/dig.c @@ -2086,7 +2086,7 @@ escape_tomb() if ((Teleportation || can_teleport(youmonst.data)) && (Teleport_control || rn2(3) < Luck+2)) { You("attempt a teleport spell."); - (void) dotele(); /* calls unearth_you() */ + (void) dotele(FALSE); /* calls unearth_you() */ } else if (u.uburied) { /* still buried after 'port attempt */ boolean good; diff --git a/src/teleport.c b/src/teleport.c index 5003ed82d..615292d5d 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -492,7 +492,14 @@ struct obj *scroll; } int -dotele() +dotelecmd() +{ + return dotele((wizard) ? TRUE : FALSE); +} + +int +dotele(break_the_rules) +boolean break_the_rules; { struct trap *trap; boolean trap_once = FALSE; @@ -529,7 +536,7 @@ dotele() castit = TRUE; break; } - if (!wizard) { + if (!break_the_rules) { if (!castit) { if (!Teleportation) You("don't know that spell."); @@ -541,7 +548,7 @@ dotele() } if (u.uhunger <= 100 || ACURR(A_STR) < 6) { - if (!wizard) { + if (!break_the_rules) { You("lack the strength %s.", castit ? "for a teleport spell" : "to teleport"); return 1; @@ -550,7 +557,7 @@ dotele() energy = objects[SPE_TELEPORT_AWAY].oc_level * 7 / 2 - 2; if (u.uen <= energy) { - if (wizard) + if (break_the_rules) energy = u.uen; else { You("lack the energy %s.", @@ -567,11 +574,13 @@ dotele() exercise(A_WIS, TRUE); if (spelleffects(sp_no, TRUE)) return 1; - else if (!wizard) + else if (!break_the_rules) return 0; } else { - u.uen -= energy; - context.botl = 1; + if (!break_the_rules) { + u.uen -= energy; + context.botl = 1; + } } } diff --git a/src/trap.c b/src/trap.c index 99554c326..b9252d18e 100644 --- a/src/trap.c +++ b/src/trap.c @@ -3693,7 +3693,7 @@ drown() && (Teleport_control || rn2(3) < Luck + 2)) { You("attempt a teleport spell."); /* utcsri!carroll */ if (!level.flags.noteleport) { - (void) dotele(); + (void) dotele(FALSE); if (!is_pool(u.ux, u.uy)) return TRUE; } else From bab350f355befe9bedce559d24a5d03cf1418336 Mon Sep 17 00:00:00 2001 From: copperwater Date: Tue, 25 Sep 2018 23:14:45 -0400 Subject: [PATCH 24/28] Fix "would flyif you weren't levitating" - missing space --- src/cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index 1c65ec71f..abce5be26 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -494,7 +494,7 @@ doextlist(VOID_ARGS) onelist = 1 - onelist; /* toggle 0 -> 1, 1 -> 0 */ redisplay = TRUE; break; - } + } free((genericptr_t) selected); } else { search = FALSE; @@ -2468,7 +2468,7 @@ int final; if (Flying) enl_msg(You_, "would fly", "would have flown", Levitation - ? "if you weren't levitating" + ? " if you weren't levitating" : (save_BFly == FROMOUTSIDE) ? if_surroundings_permitted /* both surroundings and [latent] levitation */ From 2a921112231357b8ad5082266787c8350bdf8915 Mon Sep 17 00:00:00 2001 From: nhmall Date: Tue, 25 Sep 2018 23:28:20 -0400 Subject: [PATCH 25/28] fix missing space in "would flyif you weren't levitating" Fixes #140 --- doc/fixes36.2 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 79656ce1f..23bde02da 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -148,7 +148,8 @@ Samurai seeing items at a distance could have them be described by their ordinary names rather than by their Japanese names wizard mode ^T shouldn't have been diminishing player power but it was and hilite_status:power settings really drew attention to that - +fix missing space in "would flyif you weren't levitating" + Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository ------------------------------------------------------------------ From 0fecf197897907403d1fe0ba9bee27585c8a09ff Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 26 Sep 2018 01:10:27 -0400 Subject: [PATCH 26/28] wand of polymorph engrave for blind writers Don't provide any discerning feedback on the blind player's turn but do allow the wand to exercise its magic in a manner reflective of blind writers --- doc/fixes36.2 | 3 +++ src/engrave.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 23bde02da..0a384683f 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -149,6 +149,9 @@ Samurai seeing items at a distance could have them be described by their wizard mode ^T shouldn't have been diminishing player power but it was and hilite_status:power settings really drew attention to that fix missing space in "would flyif you weren't levitating" +a wand of polymorph lost its magical ability for the turn just because the + player using it to engrave happened to be blind, which didn't make + a much sense Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/engrave.c b/src/engrave.c index 48dc2bd40..05023c153 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -7,6 +7,7 @@ #include "lev.h" STATIC_VAR NEARDATA struct engr *head_engr; +STATIC_DCL const char *NDECL(blengr); char * random_engraving(outbuf) @@ -472,6 +473,7 @@ doengrave() boolean teleengr = FALSE; /* TRUE if we move the old engraving */ boolean zapwand = FALSE; /* TRUE if we remove a wand charge */ xchar type = DUST; /* Type of engraving made */ + xchar oetype = 0; /* will be set to type of current engraving */ char buf[BUFSZ]; /* Buffer for final/poly engraving text */ char ebuf[BUFSZ]; /* Buffer for initial engraving text */ char fbuf[BUFSZ]; /* Buffer for "your fingers" */ @@ -494,6 +496,8 @@ doengrave() ebuf[0] = (char) 0; post_engr_text[0] = (char) 0; maxelen = BUFSZ - 1; + if (oep) + oetype = oep->engr_type; if (is_demon(youmonst.data) || youmonst.data->mlet == S_VAMPIRE) type = ENGR_BLOOD; @@ -676,9 +680,17 @@ doengrave() if (!Blind) { type = (xchar) 0; /* random */ (void) random_engraving(buf); + } else { + /* keep the same type so that feels don't + change and only the text is altered, + but you won't know anyway because + you're a _blind writer_ */ + if (oetype) + type = oetype; + xcrypt(blengr(), buf); } dengr = TRUE; - } + } break; case WAN_NOTHING: case WAN_UNDEAD_TURNING: @@ -890,7 +902,8 @@ doengrave() /* Something has changed the engraving here */ if (*buf) { make_engr_at(u.ux, u.uy, buf, moves, type); - pline_The("engraving now reads: \"%s\".", buf); + if (!Blind) + pline_The("engraving now reads: \"%s\".", buf); ptext = FALSE; } if (zapwand && (otmp->spe < 0)) { @@ -1286,4 +1299,33 @@ const char *str; return; } +static const char blind_writing[][21] = { + {0x44, 0x66, 0x6d, 0x69, 0x62, 0x65, 0x22, 0x45, 0x7b, 0x71, + 0x65, 0x6d, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + {0x51, 0x67, 0x60, 0x7a, 0x7f, 0x21, 0x40, 0x71, 0x6b, 0x71, + 0x6f, 0x67, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x49, 0x6d, 0x73, 0x69, 0x62, 0x65, 0x22, 0x4c, 0x61, 0x7c, + 0x6d, 0x67, 0x24, 0x42, 0x7f, 0x69, 0x6c, 0x77, 0x67, 0x7e, 0x00}, + {0x4b, 0x6d, 0x6c, 0x66, 0x30, 0x4c, 0x6b, 0x68, 0x7c, 0x7f, + 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x51, 0x67, 0x70, 0x7a, 0x7f, 0x6f, 0x67, 0x68, 0x64, 0x71, + 0x21, 0x4f, 0x6b, 0x6d, 0x7e, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x4c, 0x63, 0x76, 0x61, 0x71, 0x21, 0x48, 0x6b, 0x7b, 0x75, + 0x67, 0x63, 0x24, 0x45, 0x65, 0x6b, 0x6b, 0x65, 0x00, 0x00, 0x00}, + {0x4c, 0x67, 0x68, 0x6b, 0x78, 0x68, 0x6d, 0x76, 0x7a, 0x75, + 0x21, 0x4f, 0x71, 0x7a, 0x75, 0x6f, 0x77, 0x00, 0x00, 0x00, 0x00}, + {0x44, 0x66, 0x6d, 0x7c, 0x78, 0x21, 0x50, 0x65, 0x66, 0x65, + 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x44, 0x66, 0x73, 0x69, 0x62, 0x65, 0x22, 0x56, 0x7d, 0x63, + 0x69, 0x76, 0x6b, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x52, 0x77, 0x61, 0x28, 0x44, 0x6e, 0x75, 0x6a, 0x7b, 0x75, + 0x6f, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +STATIC_OVL const char * +blengr(VOID_ARGS) +{ + return blind_writing[rn2(SIZE(blind_writing))]; +} + /*engrave.c*/ From 74b7829957b3ba44cb288534f8be46698756b9a5 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 26 Sep 2018 01:35:48 -0400 Subject: [PATCH 27/28] engr follow-up bit --- src/engrave.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/engrave.c b/src/engrave.c index 05023c153..abd531abf 100644 --- a/src/engrave.c +++ b/src/engrave.c @@ -1318,8 +1318,6 @@ static const char blind_writing[][21] = { 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x44, 0x66, 0x73, 0x69, 0x62, 0x65, 0x22, 0x56, 0x7d, 0x63, 0x69, 0x76, 0x6b, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x52, 0x77, 0x61, 0x28, 0x44, 0x6e, 0x75, 0x6a, 0x7b, 0x75, - 0x6f, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, }; STATIC_OVL const char * From c5d0f6dd9d69ad1a495a809035ab7510f691e036 Mon Sep 17 00:00:00 2001 From: nhmall Date: Wed, 26 Sep 2018 17:18:09 -0400 Subject: [PATCH 28/28] fix out of bounds error in tty_status_update() for BL_HUNGER case The pointer could go out of bounds when decremented if it was pointing at the start of the status_vals[BL_HUNGER] (empty string). Also, guard tty_status_update() from an out of range index being passed to it (botl shouldn't do that, but...). The legal 1st parameter values for tty_status_update() in 3.6.2 are BL_RESET (-2) BL_FLUSH (-1) BL_TITLE ( 0) ...though to... BL_CONDITION (22) count MAXBLSTATS = (BL_CONDITION + 1) There's a BL_CHARACTERISTIC (-3) defined in the botl.h header file, but it is not used in wintty.c and is now screened out along with everything lower and everything MAXBLSTATS and above. closes #142 fixes #141 --- doc/fixes36.2 | 1 + win/tty/wintty.c | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 0a384683f..67f487211 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -167,6 +167,7 @@ tty: turn off an optimization that is the suspected cause of Windows reported partial status lines following level changes tty: ensure that current status fields are always copied to prior status values so that comparisons are correct +tty: fix an out of bounds error in tty_status_update() for BL_HUNGER case X11: its use of genl_status_update exposed a negative index use that could lead to a segfault diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 7657b1186..34cc45f5d 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -3666,6 +3666,9 @@ unsigned long *colormasks; char *fval = (char *) 0; boolean reset_state = NO_RESET; + if ((fldidx < BL_RESET) || (fldidx >= MAXBLSTATS)) + return; + if ((fldidx >= 0 && fldidx < MAXBLSTATS) && !status_activefields[fldidx]) return; @@ -3727,11 +3730,13 @@ unsigned long *colormasks; case BL_HUNGER: /* The core sends trailing blanks for some fields. Let's suppress the trailing blanks */ - lastchar = eos(status_vals[fldidx]); - lastchar--; - while (*lastchar == ' ' && lastchar >= status_vals[fldidx]) { - *lastchar-- = '\0'; - tty_status[NOW][fldidx].lth--; + if (tty_status[NOW][fldidx].lth > 0) { + lastchar = eos(status_vals[fldidx]); + lastchar--; + while (lastchar >= status_vals[fldidx] && *lastchar == ' ') { + *lastchar-- = '\0'; + tty_status[NOW][fldidx].lth--; + } } break; case BL_TITLE: