From 480e682454dec0772391f48df4cfdf3f6102a28f Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 2 Jan 2019 13:42:45 -0800 Subject: [PATCH 1/2] create_particular long worm tail vs mkclass Similar to ^G of 'I' triggering impossible "mkclass found no class 35 monsters", using a leading substring of "long worm tail" (other than "l" and "long worm") would trigger impossible "mkclass found no class 59 monsters and kill the fuzzer when it escalates impossible to panic. Tighten up the substring matching. ^G of '~' wasn't affected; it deliberately creates a long worm rather than the tail of one. But it was possible to ask for "long worm tail" as a specific monster type and then override the switch to long worm when prompted about whether to force the originally specified critter. I've added a check to prevent that opportunity to override even though a tail without a head seemed to be harmless. --- doc/fixes36.2 | 12 +++++++----- src/mondata.c | 16 +++++++++++----- src/read.c | 9 +++++++-- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index fe8568a2d..320d6288e 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.219 $ $NHDT-Date: 1546212616 2018/12/30 23:30:16 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.220 $ $NHDT-Date: 1546465283 2019/01/02 21:41:23 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -360,10 +360,12 @@ when built with STATUS_HILITES enabled (the default), gold on status line was missing '$' prefix for symset:Blank wizard mode ^G, creating a monster of class 'I' yielded impossible "mkclass found no class 35 monsters" -in some unknown circumstance, examining something on the map could match bogus - monster class #0 and trigger impossible "Alphabet soup: 'an("")'." - (fix avoids the warning but underlying cause is a mystery; noticed - with the fuzzer, which swaps symbol sets in and out at random) +wizard mode ^G, creating a monster via class name using "lo" through "long wor" + or "long worm t" through "long worm tail" yielded impossible "mkclass + found no class 59 monsters" (class '~' creates a long worm as intended) +if bouldersym bug (via 'O', above) put a ('\0') on the map, examining + that spot matched placeholder monster class #0 and triggered impossible + "Alphabet soup: 'an("")'." 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 diff --git a/src/mondata.c b/src/mondata.c index 62dea9a10..9feeeae4e 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mondata.c $NHDT-Date: 1543545188 2018/11/30 02:33:08 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.69 $ */ +/* NetHack 3.6 mondata.c $NHDT-Date: 1546465283 2019/01/02 21:41:23 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.70 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -781,11 +781,12 @@ const char *in_str; } for (len = 0, i = LOW_PM; i < NUMMONS; i++) { - register int m_i_len = strlen(mons[i].mname); + register int m_i_len = (int) strlen(mons[i].mname); if (m_i_len > len && !strncmpi(mons[i].mname, str, m_i_len)) { if (m_i_len == slen) { - return i; /* exact match */ + mntmp = i; + break; /* exact match */ } else if (slen > m_i_len && (str[m_i_len] == ' ' || !strcmpi(&str[m_i_len], "s") @@ -841,7 +842,7 @@ int *mndx_p; { 0, NON_PM } }; const char *p, *x; - int i; + int i, len; if (mndx_p) *mndx_p = NON_PM; /* haven't [yet] matched a specific type */ @@ -863,6 +864,8 @@ int *mndx_p; return i; } else { /* multiple characters */ + if (!strcmpi(in_str, "long")) /* not enough to match "long worm" */ + return 0; /* avoid false whole-word match with "long worm tail" */ in_str = makesingular(in_str); /* check for special cases */ for (i = 0; falsematch[i]; i++) @@ -878,9 +881,12 @@ int *mndx_p; return mons[i].mlet; } /* check monster class descriptions */ + len = (int) strlen(in_str); for (i = 1; i < MAXMCLASSES; i++) { x = def_monsyms[i].explain; - if ((p = strstri(x, in_str)) != 0 && (p == x || *(p - 1) == ' ')) + if ((p = strstri(x, in_str)) != 0 && (p == x || *(p - 1) == ' ') + && ((int) strlen(p) >= len + && (p[len] == '\0' || p[len] == ' '))) return i; } /* check individual species names */ diff --git a/src/read.c b/src/read.c index a52a0317b..562b52bf1 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 read.c $NHDT-Date: 1546053040 2018/12/29 03:10:40 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.163 $ */ +/* NetHack 3.6 read.c $NHDT-Date: 1546465285 2019/01/02 21:41:25 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.164 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2498,6 +2498,10 @@ struct _create_particular_data *d; d->which = PM_STALKER; d->monclass = MAXMCLASSES; return TRUE; + } else if (d->monclass == S_WORM_TAIL) { /* empty monster class */ + d->which = PM_LONG_WORM; + d->monclass = MAXMCLASSES; + return TRUE; } else if (d->monclass > 0) { d->which = urole.malenum; /* reset from NON_PM */ return TRUE; @@ -2516,7 +2520,8 @@ struct _create_particular_data *d; if (!d->randmonst) { firstchoice = d->which; - if (cant_revive(&d->which, FALSE, (struct obj *) 0)) { + if (cant_revive(&d->which, FALSE, (struct obj *) 0) + && firstchoice != PM_LONG_WORM_TAIL) { /* wizard mode can override handling of special monsters */ char buf[BUFSZ]; From b2ad4651f34a29c29fa46fddb94eef1b775351e3 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 2 Jan 2019 14:20:53 -0800 Subject: [PATCH 2/2] sortloot vs gems Some object classes (such as armor and weapons) are split into "subclasses" when sortloot applies an ordering (for armor, all helms, then all gloves, then all boots, and so on). Give gem class subsets. Simple (1) valueable gem, (2) worthless glass, (3) gray stone, (4) rock would give away information; instead, factor in discovery state and use (1) unseen gems and glass ("gem") (2) seen but undiscovered gems and glass ("blue gem"), (3) discovered gems ("sapphire"), (4) discovered glass ("worthless pieced of blue glass"), (5) unseen gray stones and rocks ("stone"), (6) seen but undiscovered gray stones ("gray stone"), (7) discovered gray stones ("touchstone"), (8) seen rocks ("rock"). If everything happens to be identified, the simpler ordering happens (via 3, 4, 7, and 8) because the other subsets will be empty. --- doc/fixes36.2 | 7 ++++++- src/invent.c | 43 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 320d6288e..beeed33a7 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.220 $ $NHDT-Date: 1546465283 2019/01/02 21:41:23 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.221 $ $NHDT-Date: 1546467443 2019/01/02 22:17:23 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -488,6 +488,11 @@ early level traps are sometimes covered by the remains of fake players fake player characters resist Conflict when inside a shop, far-look now includes shop prices for items marked as having been seen up close +when sortloot is enabled, gems are grouped in subsets (1) unseen gems and + glass, (2) seen but unidentified gems and glass, (3) identified gems, + (4) identified glass, (5) unseen stones (includes unseen rocks), + (6) seen but unidentified gray stones, (7) identified gray stones, + and (8) seen rocks (IDed/unIDed not applicable) NetHack Community Patches (or Variation) Included diff --git a/src/invent.c b/src/invent.c index 7b1c15378..c28a2120d 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 invent.c $NHDT-Date: 1545946249 2018/12/27 21:30:49 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.247 $ */ +/* NetHack 3.6 invent.c $NHDT-Date: 1546467443 2019/01/02 22:17:23 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.248 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -52,7 +52,7 @@ static int lastinvnr = 51; /* 0 ... 51 (never saved&restored) */ */ static char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */ -/* sortloot() classification; called at most once for each object sorted */ +/* sortloot() classification; called at most once [per sort] for each object */ STATIC_OVL void loot_classify(sort_item, obj) Loot *sort_item; @@ -71,6 +71,7 @@ struct obj *obj; const char *classorder; char *p; int k, otyp = obj->otyp, oclass = obj->oclass; + boolean seen, discovered = objects[otyp].oc_name_known ? TRUE : FALSE; /* * For the value types assigned by this classification, sortloot() @@ -78,6 +79,7 @@ struct obj *obj; */ if (!Blind) obj->dknown = 1; /* xname(obj) does this; we want it sooner */ + seen = obj->dknown ? TRUE : FALSE, /* class order */ classorder = flags.sortpack ? flags.inv_order : def_srt_order; p = index(classorder, oclass); @@ -120,7 +122,7 @@ struct obj *obj; : !is_pole(obj) ? 5 : 6); break; case TOOL_CLASS: - if (obj->dknown && objects[otyp].oc_name_known + if (seen && discovered && (otyp == BAG_OF_TRICKS || otyp == HORN_OF_PLENTY)) k = 2; /* known pseudo-container */ else if (Is_container(obj)) @@ -164,6 +166,35 @@ struct obj *obj; break; } break; + case GEM_CLASS: + /* + * Normally subclass takes priority over discovery status, but + * that would give away information for gems (assuming we'll + * group them as valuable gems, next glass, then gray stones, + * and finally rocks once they're all fully identified). + * + * Order: + * 1) unseen gems and glass ("gem") + * 2) seen but undiscovered gems and glass ("blue gem"), + * 3) discovered gems ("sapphire"), + * 4) discovered glass ("worthless pieced of blue glass"), + * 5) unseen gray stones and rocks ("stone"), + * 6) seen but undiscovered gray stones ("gray stone"), + * 7) discovered gray stones ("touchstone"), + * 8) seen rocks ("rock"). + */ + switch (objects[obj->otyp].oc_material) { + case GEMSTONE: + k = !seen ? 1 : !discovered ? 2 : 3; + break; + case GLASS: + k = !seen ? 1 : !discovered ? 2 : 4; + break; + default: /* MINERAL */ + k = !seen ? 5 : (obj->otyp != ROCK) ? (!discovered ? 6 : 7) : 8; + break; + } + break; default: /* other classes don't have subclasses; we assign a nonzero value because sortloot() uses 0 to mean 'not yet classified' */ @@ -172,9 +203,9 @@ struct obj *obj; } sort_item->subclass = (xchar) k; /* discovery status */ - k = !obj->dknown ? 1 /* unseen */ - : (objects[otyp].oc_name_known || !OBJ_DESCR(objects[otyp])) ? 4 - : (objects[otyp].oc_uname)? 3 /* named (partially discovered) */ + k = !seen ? 1 /* unseen */ + : (discovered || !OBJ_DESCR(objects[otyp])) ? 4 + : (objects[otyp].oc_uname) ? 3 /* named (partially discovered) */ : 2; /* undiscovered */ sort_item->disco = (xchar) k; }