From 9415797c6585eef66f0185ae1eaf2bfdbaa8c721 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 19 Jun 2018 17:21:21 -0700 Subject: [PATCH 1/4] fix githib issue #108 - wishing for mimic corpse Fixes #108 "small"/"medium"/"large" prefix was being stripped off during wish parsing so that it could be used to control glob size. But those are also prefixes for monster and/or object names. Wishing for "small mimic corpse" or "large mimic" corpse failed with "nothing matching that description exists" when it tried to satisfy "mimic corpse". (Asking for "giant mimic corpse" worked as intended.) Not mentioned in the report: wishing for "large {dog, cat, kobold} corpse" produced the corpse of corresponding normal sized critter instead of that of a large one. Noticed while testing the fix: wishing for "glob" failed rather than pick a random glob type. Wishing for "glob of grey ooze" failed even though "grey ooze" is recognized as a variant spelling for the gray ooze monster. Wishing for " glob" also failed even when the monster type was viable for globs. This fixes all of those even though no one will ever notice.... Wishing for "small box" (and "medium box") no longer yields a large box, it fails with "nothing matching..." instead. I was ambivalent about the earlier change which had the unintended side-effect of making them synonyms for "large box" so haven't tried to revive it. --- doc/fixes36.2 | 5 +++++ src/objnam.c | 47 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index a24195e83..3bf9adf63 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -39,6 +39,11 @@ internals for 'sortloot' option have been changed to not reorder the actual give vault guards a cursed tin whistle since there is a shrill whistling sound if hero teleports out of vault while being confronted by guard polymorphing worn amulet triggers panic if it turns into amulet of change +wishing for small mimic corpse or large mimic corpse failed with 'nothing + matching that exists'; wishing for large {dog,cat,kobold} corpse + yielded normal size one (size prefix was being stripped off for globs) +wishing for "glob of grey ooze" failed even though grey ooze is recognized + as a variant spelling for gray ooze Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/objnam.c b/src/objnam.c index b836530b5..ec6861e64 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -2474,7 +2474,7 @@ boolean to_plural; /* true => makeplural, false => makesingular */ int i, al; char *endstr, *spot; /* these are all the prefixes for *man that don't have a *men plural */ - const char *no_men[] = { + static const char *no_men[] = { "albu", "antihu", "anti", "ata", "auto", "bildungsro", "cai", "cay", "ceru", "corner", "decu", "des", "dura", "fir", "hanu", "het", "infrahu", "inhu", "nonhu", "otto", "out", "prehu", "protohu", @@ -2482,11 +2482,11 @@ boolean to_plural; /* true => makeplural, false => makesingular */ "hu", "un", "le", "re", "so", "to", "at", "a", }; /* these are all the prefixes for *men that don't have a *man singular */ - const char *no_man[] = { + static const char *no_man[] = { "abdo", "acu", "agno", "ceru", "cogno", "cycla", "fleh", "grava", "hegu", "preno", "sonar", "speci", "dai", "exa", "fla", "sta", "teg", - "tegu", "vela", "da", "hy", "lu", "no", "nu", "ra", "ru", "se", "vi", "ya", - "o", "a", + "tegu", "vela", "da", "hy", "lu", "no", "nu", "ra", "ru", "se", "vi", + "ya", "o", "a", }; if (!basestr || strlen(basestr) < 4) @@ -2775,7 +2775,7 @@ struct obj *no_wish; int wetness, gsize = 0; struct fruit *f; int ftype = context.current_fruit; - char fruitbuf[BUFSZ]; + char fruitbuf[BUFSZ], globbuf[BUFSZ]; /* Fruits may not mess up the ability to wish for real objects (since * you can leave a fruit in a bones file and it will be added to * another person's game), so they must be checked for last, after @@ -2920,12 +2920,22 @@ struct obj *no_wish; } else if (!strncmpi(bp, "empty ", l = 6)) { contents = EMPTY; } else if (!strncmpi(bp, "small ", l = 6)) { /* glob sizes */ + /* "small" might be part of monster name (mimic, if wishing + for its corpse) rather than prefix for glob size */ + if (strncmpi(bp + l, "glob", 4)) + break; gsize = 1; } else if (!strncmpi(bp, "medium ", l = 7)) { /* xname() doesn't display "medium" but without this - there'd be no way to ask for the intermediate size */ + there'd be no way to ask for the intermediate size + ("glob" without size prefix yields smallest one) */ gsize = 2; } else if (!strncmpi(bp, "large ", l = 6)) { + /* "large" might be part of monster name (dog, cat, koboold, + mimic) or object name (box, round shield) rather than + prefix for glob size */ + if (strncmpi(bp + l, "glob", 4)) + break; /* "very large " had "very " peeled off on previous iteration */ gsize = (very != 1) ? 3 : 4; } else @@ -2933,7 +2943,7 @@ struct obj *no_wish; bp += l; } if (!cnt) - cnt = 1; /* %% what with "gems" etc. ? */ + cnt = 1; /* will be changed to 2 if makesingular() changes string */ if (strlen(bp) > 1 && (p = rindex(bp, '(')) != 0) { boolean keeptrailingchars = TRUE; @@ -3056,12 +3066,28 @@ struct obj *no_wish; * * also don't let player wish for multiple globs. */ - if ((p = strstri(bp, "glob of ")) != 0 + i = (int) strlen(bp); + if (!strcmpi(bp, "glob") || !BSTRCMPI(bp, bp + i - 5, " glob") + || !strcmpi(bp, "globs") || !BSTRCMPI(bp, bp + i - 6, " globs")) { + /* string ends in "glob"; accept "black pudding glob" variation */ + if ((mntmp = name_to_mon(bp)) == NON_PM) + /* "[size] glob" without "of "; pick random */ + mntmp = rn1(PM_BLACK_PUDDING - PM_GRAY_OOZE, PM_GRAY_OOZE); + replaceglob: + Sprintf(globbuf, "glob of %s", mons[mntmp].mname); + bp = globbuf; + mntmp = NON_PM; /* not useful for "glob of " object lookup */ + cnt = 0; /* globs don't stack */ + } else if ((p = strstri(bp, "glob of ")) != 0 || (p = strstri(bp, "globs of ")) != 0) { int globoffset = (*(p + 4) == 's') ? 9 : 8; - if ((mntmp = name_to_mon(p + globoffset)) >= PM_GRAY_OOZE - && mntmp <= PM_BLACK_PUDDING) { + if ((mntmp = name_to_mon(p + globoffset)) == PM_GRAY_OOZE) { + /* name_to_mon() recognizes "grey ooze" as variant spelling + but doesn't change input string to fix it; force canonical + spelling so that object name lookup always finds it */ + goto replaceglob; + } else if (mntmp > PM_GRAY_OOZE && mntmp <= PM_BLACK_PUDDING) { mntmp = NON_PM; /* lie to ourselves */ cnt = 0; /* force only one */ } @@ -3120,6 +3146,7 @@ struct obj *no_wish; /* first change to singular if necessary */ if (*bp) { char *sng = makesingular(bp); + if (strcmp(bp, sng)) { if (cnt == 1) cnt = 2; From 3c979cb0a5748538e02e63fe42eaa670b68e5195 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 19 Jun 2018 18:39:08 -0700 Subject: [PATCH 2/4] more glob wishing Wishing for " glob of black pudding" worked, and wishing for "black pudding glob" worked, but wishing for " black pudding glob" didn't work. Fix that. Also, remove a bit of spaghetti introduced by the previous patch. And once we know we're wishing for a glob, we can skip a big chunk of wish parsing and special case handling. --- src/objnam.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/objnam.c b/src/objnam.c index ec6861e64..5fa0ddd42 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -2721,9 +2721,9 @@ char oclass; if (!name) return STRANGE_OBJECT; - memset((genericptr_t) validobjs, 0, sizeof(validobjs)); + memset((genericptr_t) validobjs, 0, sizeof validobjs); - for (i = oclass ? bases[(int)oclass] : STRANGE_OBJECT + 1; + for (i = oclass ? bases[(int) oclass] : STRANGE_OBJECT + 1; i < NUM_OBJECTS && (!oclass || objects[i].oc_class == oclass); ++i) { /* don't match extra descriptions (w/o real name) */ @@ -2921,8 +2921,11 @@ struct obj *no_wish; contents = EMPTY; } else if (!strncmpi(bp, "small ", l = 6)) { /* glob sizes */ /* "small" might be part of monster name (mimic, if wishing - for its corpse) rather than prefix for glob size */ - if (strncmpi(bp + l, "glob", 4)) + for its corpse) rather than prefix for glob size; when + used for globs, it might be either "small glob of " or + "small glob" and user might add 's' even though plural + doesn't accomplish anything because globs don't stack */ + if (strncmpi(bp + l, "glob", 4) && !strstri(bp + l, " glob")) break; gsize = 1; } else if (!strncmpi(bp, "medium ", l = 7)) { @@ -2934,7 +2937,7 @@ struct obj *no_wish; /* "large" might be part of monster name (dog, cat, koboold, mimic) or object name (box, round shield) rather than prefix for glob size */ - if (strncmpi(bp + l, "glob", 4)) + if (strncmpi(bp + l, "glob", 4) && !strstri(bp + l, " glob")) break; /* "very large " had "very " peeled off on previous iteration */ gsize = (very != 1) ? 3 : 4; @@ -3067,30 +3070,27 @@ struct obj *no_wish; * also don't let player wish for multiple globs. */ i = (int) strlen(bp); + p = (char *) 0; + /* check for "glob", " glob", and "glob of " */ if (!strcmpi(bp, "glob") || !BSTRCMPI(bp, bp + i - 5, " glob") - || !strcmpi(bp, "globs") || !BSTRCMPI(bp, bp + i - 6, " globs")) { - /* string ends in "glob"; accept "black pudding glob" variation */ - if ((mntmp = name_to_mon(bp)) == NON_PM) - /* "[size] glob" without "of "; pick random */ + || !strcmpi(bp, "globs") || !BSTRCMPI(bp, bp + i - 6, " globs") + || (p = strstri(bp, "glob of ")) != 0 + || (p = strstri(bp, "globs of ")) != 0) { + mntmp = name_to_mon(!p ? bp : (strstri(p, " of ") + 4)); + /* if we didn't recognize monster type, pick a valid one at random */ + if (mntmp == NON_PM) mntmp = rn1(PM_BLACK_PUDDING - PM_GRAY_OOZE, PM_GRAY_OOZE); - replaceglob: + /* construct canonical spelling in case name_to_mon() recognized a + variant (grey ooze) or player used inverted syntax ( glob); + if player has given a valid monster type but not valid glob type, + object name lookup won't find it and wish attempt will fail */ Sprintf(globbuf, "glob of %s", mons[mntmp].mname); bp = globbuf; mntmp = NON_PM; /* not useful for "glob of " object lookup */ cnt = 0; /* globs don't stack */ - } else if ((p = strstri(bp, "glob of ")) != 0 - || (p = strstri(bp, "globs of ")) != 0) { - int globoffset = (*(p + 4) == 's') ? 9 : 8; - - if ((mntmp = name_to_mon(p + globoffset)) == PM_GRAY_OOZE) { - /* name_to_mon() recognizes "grey ooze" as variant spelling - but doesn't change input string to fix it; force canonical - spelling so that object name lookup always finds it */ - goto replaceglob; - } else if (mntmp > PM_GRAY_OOZE && mntmp <= PM_BLACK_PUDDING) { - mntmp = NON_PM; /* lie to ourselves */ - cnt = 0; /* force only one */ - } + oclass = FOOD_CLASS; + actualn = bp, dn = 0; + goto srch; } else { /* * Find corpse type using "of" (figurine of an orc, tin of orc meat) From 800a898b51ee5884d761247eddc199ee88037c04 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 20 Jun 2018 14:53:20 -0700 Subject: [PATCH 3/4] fix github issue #109 - healing spells Fixes #109 Spells of healing and extra healing cast at monsters were handling monster blindness differently from other forms of healing. (Potions also work differently when drunk by monsters but I haven't changed that since it seems to be intentional.) Hero: potion of healing cures blindness if blessed; spell of healing cast at skilled or better now behaves likewise; potion of extra healing cures blindness if not cursed; spell of extra healing is inherently not cursed and already behaved likewise; potion of full healing always cures blindness even if cursed. Monsters quaffing potions: plain healing cures blindness if not cursed; extra healing and full healing always cure blindess. Hero casting healing spell at monster: plain healing behaves like the hero plain healing case: cures blindness as if blessed when cast at skilled or expert level; this is a change in hehavior--it used to cure timed blindness even if unskilled and not cure 'permanent' blindness at all; extra healing cast by hero is inherently not cursed so always cures blindness. --- doc/fixes36.2 | 2 ++ src/spell.c | 28 ++++++++++++++++++---------- src/zap.c | 18 +++++++++++------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 3bf9adf63..1f222f849 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -44,6 +44,8 @@ wishing for small mimic corpse or large mimic corpse failed with 'nothing yielded normal size one (size prefix was being stripped off for globs) wishing for "glob of grey ooze" failed even though grey ooze is recognized as a variant spelling for gray ooze +spells of healing and extra healing cast at monsters handled monster blindness + inconsistently compared to other healing Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/spell.c b/src/spell.c index 2b0e83dd6..27702bfdd 100644 --- a/src/spell.c +++ b/src/spell.c @@ -886,7 +886,7 @@ int spell; boolean atme; { int energy, damage, chance, n, intell; - int skill, role_skill, res = 0; + int otyp, skill, role_skill, res = 0; boolean confused = (Confusion != 0); boolean physical_damage = FALSE; struct obj *pseudo; @@ -1029,10 +1029,11 @@ boolean atme; * Find the skill the hero has in a spell type category. * See spell_skilltype for categories. */ - skill = spell_skilltype(pseudo->otyp); + otyp = pseudo->otyp; + skill = spell_skilltype(otyp); role_skill = P_SKILL(skill); - switch (pseudo->otyp) { + switch (otyp) { /* * At first spells act as expected. As the hero increases in skill * with the appropriate spell type, some spells increase in their @@ -1056,9 +1057,9 @@ boolean atme; } } else { explode(u.dx, u.dy, - pseudo->otyp - SPE_MAGIC_MISSILE + 10, + otyp - SPE_MAGIC_MISSILE + 10, spell_damage_bonus(u.ulevel / 2 + 1), 0, - (pseudo->otyp == SPE_CONE_OF_COLD) + (otyp == SPE_CONE_OF_COLD) ? EXPL_FROSTY : EXPL_FIERY); } @@ -1073,12 +1074,13 @@ boolean atme; } } break; - } /* else fall through... */ + } /* else */ + /*FALLTHRU*/ /* these spells are all duplicates of wand effects */ case SPE_FORCE_BOLT: physical_damage = TRUE; - /* fall through */ + /*FALLTHRU*/ case SPE_SLEEP: case SPE_MAGIC_MISSILE: case SPE_KNOCK: @@ -1096,7 +1098,13 @@ boolean atme; case SPE_EXTRA_HEALING: case SPE_DRAIN_LIFE: case SPE_STONE_TO_FLESH: - if (!(objects[pseudo->otyp].oc_dir == NODIR)) { + if (objects[otyp].oc_dir != NODIR) { + if (otyp == SPE_HEALING || otyp == SPE_EXTRA_HEALING) { + /* healing and extra healing are actually potion effects, + but they've been extended to take a direction like wands */ + if (role_skill >= P_SKILLED) + pseudo->blessed = 1; + } if (atme) { u.dx = u.dy = u.dz = 0; } else if (!getdir((char *) 0)) { @@ -1136,7 +1144,7 @@ boolean atme; /* high skill yields effect equivalent to blessed scroll */ if (role_skill >= P_SKILLED) pseudo->blessed = 1; - /* fall through */ + /*FALLTHRU*/ case SPE_CHARM_MONSTER: case SPE_MAGIC_MAPPING: case SPE_CREATE_MONSTER: @@ -1152,7 +1160,7 @@ boolean atme; /* high skill yields effect equivalent to blessed potion */ if (role_skill >= P_SKILLED) pseudo->blessed = 1; - /* fall through */ + /*FALLTHRU*/ case SPE_INVISIBILITY: (void) peffects(pseudo); break; diff --git a/src/zap.c b/src/zap.c index b2ff4c77a..1b83a3ad5 100644 --- a/src/zap.c +++ b/src/zap.c @@ -138,7 +138,7 @@ struct obj *otmp; boolean wake = TRUE; /* Most 'zaps' should wake monster */ boolean reveal_invis = FALSE, learn_it = FALSE; boolean dbldam = Role_if(PM_KNIGHT) && u.uhave.questart; - boolean helpful_gesture = FALSE; + boolean skilled_spell, helpful_gesture = FALSE; int dmg, otyp = otmp->otyp; const char *zap_type_text = "spell"; struct obj *obj; @@ -149,10 +149,12 @@ struct obj *otmp; reveal_invis = FALSE; notonhead = (mtmp->mx != bhitpos.x || mtmp->my != bhitpos.y); + skilled_spell = (otmp && otmp->oclass == SPBOOK_CLASS && otmp->blessed); + switch (otyp) { case WAN_STRIKING: zap_type_text = "wand"; - /* fall through */ + /*FALLTHRU*/ case SPE_FORCE_BOLT: reveal_invis = TRUE; if (disguised_mimic) @@ -343,10 +345,12 @@ struct obj *otmp; mtmp->mhp += d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4); if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; - if (mtmp->mblinded) { - mtmp->mblinded = 0; - mtmp->mcansee = 1; - } + /* plain healing must be blessed to cure blindness; extra + healing only needs to not be cursed, so spell always cures + [potions quaffed by monsters behave slightly differently; + we use the rules for the hero here...] */ + if (skilled_spell || otyp == SPE_EXTRA_HEALING) + mcureblindness(mtmp, canseemon(mtmp)); if (canseemon(mtmp)) { if (disguised_mimic) { if (is_obj_mappear(mtmp,STRANGE_OBJECT)) { @@ -2407,7 +2411,7 @@ boolean ordinary; case SPE_EXTRA_HEALING: learn_it = TRUE; /* (no effect for spells...) */ healup(d(6, obj->otyp == SPE_EXTRA_HEALING ? 8 : 4), 0, FALSE, - (obj->otyp == SPE_EXTRA_HEALING)); + (obj->blessed || obj->otyp == SPE_EXTRA_HEALING)); You_feel("%sbetter.", obj->otyp == SPE_EXTRA_HEALING ? "much " : ""); break; case WAN_LIGHT: /* (broken wand) */ From 8c2bd75ce4c3be7428f761d271a3c191af965af8 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 21 Jun 2018 12:09:12 -0700 Subject: [PATCH 4/4] fix github issue #110 - sortloot segfault Fixes #110 NetHack dumped core while qsort was executing for sortloot. Fix a logic error introduced by adding filtering capability to sortloot() which could result in a sparsely populated array instead of having the number of elements be less than the list size. I don't know why this didn't show up sooner. --- doc/fixes36.2 | 1 + src/invent.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 1f222f849..fc7a7218e 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -53,6 +53,7 @@ Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository 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) Platform- and/or Interface-Specific Fixes diff --git a/src/invent.c b/src/invent.c index c2d4861a8..9ea974698 100644 --- a/src/invent.c +++ b/src/invent.c @@ -462,7 +462,7 @@ boolean FDECL((*filterfunc), (OBJ_P)); augment_filter = (mode & SORTLOOT_PETRIFY) ? TRUE : FALSE; mode &= ~SORTLOOT_PETRIFY; /* remove flag, leaving mode */ /* populate aliarray[0..n-1] */ - for (i = 0, o = *olist; o; ++i, o = by_nexthere ? o->nexthere : o->nobj) { + for (i = 0, o = *olist; o; o = by_nexthere ? o->nexthere : o->nobj) { if (filterfunc && !(*filterfunc)(o) /* caller may be asking us to override filterfunc (in order to do a cockatrice corpse touch check during pickup even @@ -473,6 +473,7 @@ boolean FDECL((*filterfunc), (OBJ_P)); sliarray[i].obj = o, sliarray[i].indx = (int) i; sliarray[i].str = (char *) 0; sliarray[i].orderclass = sliarray[i].subclass = sliarray[i].disco = 0; + ++i; } n = i; /* add a terminator so that we don't have to pass 'n' back to caller */