From d7f26afba85801f41547de49e5564fc21ad38ceb Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 5 Dec 2017 03:38:23 -0800 Subject: [PATCH 01/14] more #adjust (#H6571) Make the suggested change that only adjusting something into its own slot be the way to collect/merge compatible stacks with it, instead of any #adjust without a split count. This removes the previous special case for a count that matches the stack size. Having to know the exact count was not a burden on the player, but being able to move things around without merging with other stacks makes more sense than the original behavior or the hack to work-around that behavior. --- doc/Guidebook.mn | 20 +++++--- doc/Guidebook.tex | 18 +++++-- doc/fixes36.1 | 6 +-- src/invent.c | 128 ++++++++++++++++++++++++++-------------------- 4 files changed, 102 insertions(+), 70 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 16199ef81..3989cec93 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.240 $ $NHDT-Date: 1512098127 2017/12/01 03:15:27 $ +.\" $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.241 $ $NHDT-Date: 1512473628 2017/12/05 11:33:48 $ .ds h0 "NetHack Guidebook .ds h1 .ds h2 % @@ -930,11 +930,19 @@ being moved. ``#adjust'' can also be used to split a stack of objects; when choosing the item to adjust, enter a count prior to its letter. .lp "" -When no count is given, other compatible stacks are merged with the source -stack as it's moved to the destination. When a split count is given, the -only stack that might be merged is the one already in the destination. -To move a whole stack without having other compatible ones merge with it, -specify a ``split'' count which matches the full stack size. +Adjusting without a count used to collect all compatible stacks when +moving to the destination. That behavior has been changed; to gather +compatible stacks, ``#adjust'' a stack into its own inventory slot. +If it has a name assigned, other stacks with the same name or with +no name will merge provided that all their other attributes match. +If it does not have a name, only other stacks with no name are eligible. +In either case, otherwise compatible stacks with a different name +will not be merged. This contrasts with using ``#adjust'' to move +from one slot to a different slot. In that situation, moving (no +count given) a compatible stack will merge if either stack has a +name when the other doesn't and give that name to the result, while +splitting (count given) will ignore the source stack's name when +deciding whether to merge with the destination stack. .lp #annotate Allows you to specify one line of text to associate with the current dungeon level. All levels with annotations are displayed by the diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index e776b02d0..ea948813b 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1110,11 +1110,19 @@ being moved. ``{\tt \#adjust}'' can also be used to split a stack of objects; when choosing the item to adjust, enter a count prior to its letter.\\ %.lp "" -When no count is given, other compatible stacks are merged with the source -stack as it's moved to the destination. When a split count is given, the -only stack that might be merged is the one already in the destination. -To move a whole stack without having other compatible ones merge with it, -specify a ``split'' count which matches the full stack size. +Adjusting without a count used to collect all compatible stacks when +moving to the destination. That behavior has been changed; to gather +compatible stacks, ``{\tt \#adjust}'' a stack into its own inventory slot. +If it has a name assigned, other stacks with the same name or with +no name will merge provided that all their other attributes match. +If it does not have a name, only other stacks with no name are eligible. +In either case, otherwise compatible stacks with a different name +will not be merged. This contrasts with using ``{\tt \#adjust}'' to move +from one slot to a different slot. In that situation, moving (no +count given) a compatible stack will merge if either stack has a +name when the other doesn't and give that name to the result, while +splitting (count given) will ignore the source stack's name when +deciding whether to merge with the destination stack. %.lp \item[\tb{\#annotate}] Allows you to specify one line of text to associate with the current diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 4e1d79c76..8b27ef999 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -720,9 +720,9 @@ Master Key of Thievery always finds door and chest traps if used to lock or "Elbereth" now erodes based on attacks by the player, not monsters scared option herecmd_menu to make a mouse click on your character pop up a context menu, and extended command #herecmdmenu to do the same -for #adjust, specifying a split count which matches an inventory slot's full - size will move or swap that stack without combining other compatible - stacks (except for the one in the destination slot, if applicable) +change #adjust's behavior when collecting compatible stacks; that used to + occur for any #adjust which lacked a split count, now it only happens + when 'adjusting' into a stack's own inventory slot Platform- and/or Interface-Specific New Features diff --git a/src/invent.c b/src/invent.c index 4a8a68815..89d823eec 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 invent.c $NHDT-Date: 1512096431 2017/12/01 02:47:11 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.222 $ */ +/* NetHack 3.6 invent.c $NHDT-Date: 1512473628 2017/12/05 11:33:48 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.223 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1472,9 +1472,6 @@ redo_menu: else if (otmp->otyp == LOADSTONE && otmp->cursed) /* kludge for canletgo()'s can't-drop-this message */ otmp->corpsenm = (int) cnt; - } else if (!strcmp(word, "adjust")) { - /* specifying stack's full count means something to #adjust */ - otmp->nomerge = 1; } } return otmp; @@ -3539,10 +3536,18 @@ reassign() * then a 'to' slot for its destination. Open slots and those * filled by compatible stacks are listed as likely candidates * but user can pick any inventory letter (including 'from'). - * All compatible items found are gathered into the 'from' - * stack as it is moved. If the 'to' slot isn't empty and - * doesn't merge, then its stack is swapped to the 'from' slot. * + * to == from, 'from' has a name + * All compatible items (same name or no name) are gathered + * into the 'from' stack. No count is allowed. + * to == from, 'from' does not have a name + * All compatible items without a name are gathered into the + * 'from' stack. No count is allowed. Compatible stacks with + * names are left as-is. + * to != from, no count + * Move 'from' to 'to'. If 'to' is not empty, merge 'from' + * into it if possible, otherwise swap it with the 'from' slot. + * to != from, count given * If the user specifies a count when choosing the 'from' slot, * and that count is less than the full size of the stack, * then the stack will be split. The 'count' portion is moved @@ -3552,10 +3557,19 @@ reassign() * will be moved to an open slot; if there isn't any open slot * available, the adjustment attempt fails. * - * Splitting has one special case: if 'to' slot is non-empty - * and is compatible with 'from' in all respects except for - * user-assigned names, the 'count' portion being moved is - * effectively renamed so that it will merge with 'to' stack. + * To minimize merging for 'from == to', unnamed stacks will + * merge with named 'from' but named ones won't merge with + * unnamed 'from'. Otherwise attempting to collect all unnamed + * stacks would lump the first compatible named stack with them + * and give them its name. + * + * To maximize merging for 'from != to', compatible stacks will + * merge when either lacks a name (or they already have the same + * name). When no count is given and one stack has a name and + * the other doesn't, the merged result will have that name. + * However, when splitting results in a merger, the name of the + * destination overrides that of the source, even if destination + * is unnamed and source is named. */ int doorganize() /* inventory organizer by Del Lamb */ @@ -3569,8 +3583,9 @@ doorganize() /* inventory organizer by Del Lamb */ char lets[1 + 52 + 1 + 1]; /* room for '$a-zA-Z#\0' */ char qbuf[QBUFSZ]; char allowall[4]; /* { ALLOW_COUNT, ALL_CLASSES, 0, 0 } */ + char *objname, *otmpname; const char *adj_type; - boolean ever_mind = FALSE, dont_collect = FALSE; + boolean ever_mind = FALSE, collect; if (!invent) { You("aren't carrying anything to adjust."); @@ -3602,19 +3617,12 @@ doorganize() /* inventory organizer by Del Lamb */ /* figure out whether user gave a split count to getobj() */ splitting = bumped = 0; - if (obj->nomerge) { - /* player specified full count; no split occurred and we'll - avoid collecting compatible stacks when moving this one */ - obj->nomerge = 0; - dont_collect = TRUE; - } else { - for (otmp = invent; otmp; otmp = otmp->nobj) - if (otmp->nobj == obj) { /* knowledge of splitobj() operation */ - if (otmp->invlet == obj->invlet) - splitting = otmp; - break; - } - } + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp->nobj == obj) { /* knowledge of splitobj() operation */ + if (otmp->invlet == obj->invlet) + splitting = otmp; + break; + } /* initialize the list with all lower and upper case letters */ lets[GOLD_INDX] = (obj->oclass == COIN_CLASS) ? GOLD_SYM : ' '; @@ -3668,7 +3676,7 @@ doorganize() /* inventory organizer by Del Lamb */ /* adjusting to same slot is meaningful since all compatible stacks get collected along the way, but splitting to same slot is not */ - || ((splitting || dont_collect) && let == obj->invlet)) { + || (splitting && let == obj->invlet)) { noadjust: if (splitting) (void) merged(&splitting, &obj); @@ -3691,8 +3699,9 @@ doorganize() /* inventory organizer by Del Lamb */ pline("Select an inventory slot letter."); /* else try again */ } + collect = (let == obj->invlet); /* change the inventory and print the resulting item */ - adj_type = !splitting ? "Moving:" : "Splitting:"; + adj_type = collect ? "Collecting" : !splitting ? "Moving:" : "Splitting:"; /* * don't use freeinv/addinv to avoid double-touching artifacts, @@ -3701,45 +3710,52 @@ doorganize() /* inventory organizer by Del Lamb */ extract_nobj(obj, &invent); for (otmp = invent; otmp;) { - if (!splitting && !dont_collect) { - if (merged(&otmp, &obj)) { + /* it's tempting to pull this outside the loop, but merged() could + free ONAME(obj) [via obfree()] and replace it with ONAME(otmp) */ + objname = has_oname(obj) ? ONAME(obj) : (char *) 0; + + if (collect) { + /* Collecting: #adjust an inventory stack into its same slot; + keep it there and merge other compatible stacks into it. + Traditional inventory behavior is to merge unnamed stacks + with compatible named ones; we only want that if it is + the 'from' stack (obj) with a name and candidate (otmp) + without one, not unnamed 'from' with named candidate. */ + otmpname = has_oname(otmp) ? ONAME(otmp) : (char *) 0; + if ((!otmpname || (objname && !strcmp(objname, otmpname))) + && merged(&otmp, &obj)) { adj_type = "Merging:"; obj = otmp; otmp = otmp->nobj; extract_nobj(obj, &invent); continue; /* otmp has already been updated */ - } else if (otmp->invlet == let) { + } + } else if (otmp->invlet == let) { + /* Moving or splitting: don't merge extra compatible stacks. + Found 'otmp' in destination slot; merge if compatible, + otherwise bump whatever is there to an open slot. */ + if (!splitting) { adj_type = "Swapping:"; otmp->invlet = obj->invlet; - } - } else { - /* splitting: don't merge extra compatible stacks; - if destination is compatible, do merge with it, - otherwise bump whatever is there to an open slot */ - if (otmp->invlet == let) { - int olth = 0; - - if (has_oname(obj)) - olth = strlen(ONAME(obj)); - /* ugly hack: if these objects aren't going to merge - solely because they have conflicting user-assigned - names, strip off the name of the one being moved */ - if (olth && !obj->oartifact && !mergable(otmp, obj)) { - char *holdname = ONAME(obj); - + } else { + /* strip 'from' name if it has one */ + if (objname && !obj->oartifact) ONAME(obj) = (char *) 0; - /* restore name iff merging is still not possible */ - if (!mergable(otmp, obj)) { - ONAME(obj) = holdname; - holdname = (char *) 0; - } else - free((genericptr_t) holdname); + if (!mergable(otmp, obj)) { + /* won't merge; put 'from' name back */ + if (objname) + ONAME(obj) = objname; + } else { + /* will merge; discard 'from' name */ + if (objname) + free((genericptr_t) objname), objname = 0; } if (merged(&otmp, &obj)) { + adj_type = "Splitting and merging:"; obj = otmp; extract_nobj(obj, &invent); - } else if (inv_cnt(FALSE) >= 52 && !dont_collect) { + } else if (inv_cnt(FALSE) >= 52) { (void) merged(&splitting, &obj); /* undo split */ /* "knapsack cannot accommodate any more items" */ Your("pack is too full."); @@ -3748,9 +3764,9 @@ doorganize() /* inventory organizer by Del Lamb */ bumped = otmp; extract_nobj(bumped, &invent); } - break; - } /* found 'to' slot */ - } /* splitting */ + } /* moving vs splitting */ + break; /* not collecting and found 'to' slot */ + } /* collect */ otmp = otmp->nobj; } From 5e7327cb39a25a12896744c3043077f9c8f58c0d Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 6 Dec 2017 18:49:08 +0000 Subject: [PATCH 02/14] TDTTOE: Discourage generating elf corpses on sleeping gas traps These are elven /adventurers/, so they get sleep resistance at experience level 4 (not immediately), and so there's an outside chance they'll be killed by a sleeping gas trap. This commit reduces the probability, though. --- src/mklev.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mklev.c b/src/mklev.c index c8fa866b5..e3d28891d 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -1461,6 +1461,12 @@ coord *tm; case 0: /* elf corpses are the rarest as they're the most useful */ victim_mnum = PM_ELF; + /* elven adventurers get sleep resistance early; so don't + generate elf corpses on sleeping gas traps unless a) + we're on dlvl 2 (1 is impossible) and b) we pass a coin + flip */ + if (kind == SLP_GAS_TRAP && !(lvl <= 2 && rn2(2))) + victim_mnum = PM_HUMAN; break; case 1: case 2: victim_mnum = PM_DWARF; From 3f9522041c3b57daf30361ecbf5e4a24c2c4b6e0 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 8 Dec 2017 14:12:35 -0800 Subject: [PATCH 03/14] fix #6598 - monster briefly rendered as hero When swapping places with a pet, the hero's coordinates are changed before some tests which might disallow the swap, and if the pet was a hidden mimic or was trapped and became untame, the attempt to draw the revised pet or former pet would actually draw the hero and have that mistake be visible during the message about not swapping. That last bit only occurred when the pet couldn't move diagonally (due to being a grid bug or to being unable to squeeze through a tight space). Also, spoteffects for arriving at a new location took place even though the hero hadn't changed position. --- doc/fixes36.1 | 6 ++++++ src/hack.c | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 8b27ef999..dd192f3f8 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -478,6 +478,12 @@ monster holding hero takes double damage from an explosion for reaching into or beyond the explosion radius poly'd hero holding monster now takes double damage (if monster is within explosion radious) instead of inflicting that upon the monster +when trying to swap places with a pet and failing due to pet being unable + to move diagonally to hero's spot (grid bug or too tight squeeze), if + pet became untame the map would briefly show hero at pet's location +when trying to swap places with a pet and failing due to pet being trapped + or disallowed diagonal move, the arrive-on-new-spot code (autopickup, + trap triggering) executed even though hero didn't ultimately move Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/hack.c b/src/hack.c index 64bc25e3b..f16ed1bf2 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 hack.c $NHDT-Date: 1508549436 2017/10/21 01:30:36 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.180 $ */ +/* NetHack 3.6 hack.c $NHDT-Date: 1512771130 2017/12/08 22:12:10 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.181 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1720,26 +1720,38 @@ domove() yelp(mtmp); } } + + /* seemimic/newsym should be done before moving hero, otherwise + the display code will draw the hero here before we possibly + cancel the swap below (we can ignore steed mx,my here) */ + u.ux = u.ux0, u.uy = u.uy0; mtmp->mundetected = 0; if (mtmp->m_ap_type) seemimic(mtmp); else if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my); + u.ux = mtmp->mx, u.uy = mtmp->my; /* resume swapping positions */ if (mtmp->mtrapped && (trap = t_at(mtmp->mx, mtmp->my)) != 0 && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT) && sobj_at(BOULDER, trap->tx, trap->ty)) { /* can't swap places with pet pinned in a pit by a boulder */ u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */ + if (u.usteed) + u.usteed->mx = u.ux, u.usteed->my = u.uy; } else if (u.ux0 != x && u.uy0 != y && NODIAG(mtmp->data - mons)) { /* can't swap places when pet can't move to your spot */ u.ux = u.ux0, u.uy = u.uy0; + if (u.usteed) + u.usteed->mx = u.ux, u.usteed->my = u.uy; You("stop. %s can't move diagonally.", upstart(y_monnam(mtmp))); } else if (u.ux0 != x && u.uy0 != y && bad_rock(mtmp->data, x, u.uy0) && bad_rock(mtmp->data, u.ux0, y) && (bigmonst(mtmp->data) || (curr_mon_load(mtmp) > 600))) { /* can't swap places when pet won't fit thru the opening */ u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */ + if (u.usteed) + u.usteed->mx = u.ux, u.usteed->my = u.uy; You("stop. %s won't fit through.", upstart(y_monnam(mtmp))); } else { char pnambuf[BUFSZ]; @@ -1836,7 +1848,8 @@ domove() if (Punished) /* put back ball and chain */ move_bc(0, bc_control, ballx, bally, chainx, chainy); - spoteffects(TRUE); + if (u.umoved) + spoteffects(TRUE); /* delay next move because of ball dragging */ /* must come after we finished picking up, in spoteffects() */ From 99f5bbc59edb4ce9e2e6a3cf9bf88d0ab6b1606f Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 8 Dec 2017 23:06:25 -0800 Subject: [PATCH 04/14] fix #H5590 - pets not shown on dumplog map When ascending or escaping from the dungeon, adjacent pets are moved onto the 'mydogs' list so that they can be included in the score and mentioned as being with hero in the final messages. But keepdogs() was caled to do that before the known portion of the map was drawn in the dumplog file, so adjacent pets were missing. Defer that until after the map has been dumped so that pets will still be present. --- doc/fixes36.1 | 2 ++ src/end.c | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index dd192f3f8..d844df709 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -514,6 +514,8 @@ DUMPLOG: genocided and extinct species was always a blank line; vanquished creatures was just a blank line if nothing had been killed DUMPLOG: RIP tombstone was printed for characters who survived (ascended, escaped dungeon, quit, trickery or panic) +DUMPLOG: pets who accompany hero during ascension or dungeon escape were + already gone by the time known portion of the level map was dumped artifact creation violated illiterate conduct when artifact name was assigned, behavior intended only for creating Sting or Orcrist via naming tty: revert to pline() for issuing prompts (override MSGTYPE=hide differently) diff --git a/src/end.c b/src/end.c index e1d8a6423..427f4501c 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1495232357 2017/05/19 22:19:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.131 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1512803167 2017/12/09 07:06:07 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.137 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1142,11 +1142,6 @@ int how; else if (how == TURNED_SLIME) u.ugrave_arise = PM_GREEN_SLIME; - /* if pets will contribute to score, populate mydogs list now - (bones creation isn't a factor, but pline() messaging is) */ - if (how == ESCAPED || how == ASCENDED) - keepdogs(TRUE); - if (how == QUIT) { killer.format = NO_KILLER_PREFIX; if (u.uhp < 1) { @@ -1194,6 +1189,13 @@ int how; dump_everything(how, endtime); } + /* if pets will contribute to score, populate mydogs list now + (bones creation isn't a factor, but pline() messaging is; used to + be done ever sooner, but we need it to come after dump_everything() + so that any accompanying pets are still on the map during dump) */ + if (how == ESCAPED || how == ASCENDED) + keepdogs(TRUE); + /* finish_paybill should be called after disclosure but before bones */ if (bones_ok && taken) finish_paybill(); From 20accd4bb756cf295ffc119a4e8cca9f511d5651 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 9 Dec 2017 00:36:19 -0800 Subject: [PATCH 05/14] fix #H6597 - genocide exploit Self-genocide (own role or race) while polymorphed sets u.uhp to -1 so that you'll be killed during rehumanization. I found a couple of places which were testing (u.uhp < 1) without checking polymorph state, and one of those was where monster movement decides whether or not to attack. This bug seems to have been present since start of the second cvs repository, so has been around for quite a long time without anybody letting on that they'd noticed. So it probably isn't a very effective exploit, although it would certainly make ascending without wearing armor become much more feasible. There are bound to be other places which examine u.uhp directly instead of '(Upolyd ? u.mh : u.uhp)' but I only checked m*.c. --- doc/fixes36.1 | 2 ++ src/mhitu.c | 9 ++++++--- src/monmove.c | 5 +++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index d844df709..b8dc23143 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -484,6 +484,8 @@ when trying to swap places with a pet and failing due to pet being unable when trying to swap places with a pet and failing due to pet being trapped or disallowed diagonal move, the arrive-on-new-spot code (autopickup, trap triggering) executed even though hero didn't ultimately move +being "dead inside" (self-genocide while polymorphed) conferred partial + invulnerability--normal monster behavior stopped attacking hero Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mhitu.c b/src/mhitu.c index 50399899a..7d07a8782 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mhitu.c $NHDT-Date: 1505001092 2017/09/09 23:51:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.147 $ */ +/* NetHack 3.6 mhitu.c $NHDT-Date: 1512808564 2017/12/09 08:36:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.148 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1629,8 +1629,11 @@ register struct attack *mattk; dmg = 0; break; } - if (u.uhp < 1) - done_in_by(mtmp, DIED); + if ((Upolyd ? u.mh : u.uhp) < 1) { + /* already dead? call rehumanize() or done_in_by() as appropriate */ + mdamageu(mtmp, 1); + dmg = 0; + } /* Negative armor class reduces damage done instead of fully protecting * against hits. diff --git a/src/monmove.c b/src/monmove.c index 26d7e2144..9febe3fc7 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 monmove.c $NHDT-Date: 1505265968 2017/09/13 01:26:08 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.93 $ */ +/* NetHack 3.6 monmove.c $NHDT-Date: 1512808567 2017/12/09 08:36:07 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -617,7 +617,8 @@ toofar: */ if (!mtmp->mpeaceful || (Conflict && !resist(mtmp, RING_CLASS, 0, 0))) { - if (inrange && !noattacks(mdat) && u.uhp > 0 && !scared && tmp != 3) + if (inrange && !noattacks(mdat) + && (Upolyd ? u.mh : u.uhp) > 0 && !scared && tmp != 3) if (mattacku(mtmp)) return 1; /* monster died (e.g. exploded) */ From 045ee2a8980a961e83eea649975c0cabbd25ffcf Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Sat, 9 Dec 2017 14:12:40 +0000 Subject: [PATCH 06/14] Add an instance flag for being inside parse() Some windowports that are currently being written by third parties need more information about the engine than they currently have. Two specific reported problems: a) needing to know whether a putstr() call relates to a count (so that it can be placed in a different part of the user interface from the message area); b) needing to know whether a request for a character relates to command input (some hangup handling routines need this so that they can determine what behaviour is potentially exploitable). Knowing whether or not you're inside parse() fixes both of them. This would be cleaner to do by changing the windowport API, but that'd break existing windowports, which isn't really ideal. Setting a globalish variable that the windowport can inspect, but can ignore if it prefers, means that existing windowports will continue to work fine, but new windowports will have more information and thus more flexibility in how they handle command entry. --- include/flag.h | 1 + src/cmd.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/include/flag.h b/include/flag.h index 81630bd27..a8f9172f7 100644 --- a/include/flag.h +++ b/include/flag.h @@ -216,6 +216,7 @@ struct instance_flags { boolean sanity_check; /* run sanity checks */ boolean mon_polycontrol; /* debug: control monster polymorphs */ boolean in_dumplog; /* doing the dumplog right now? */ + boolean in_parse; /* is a command being parsed? */ /* stuff that is related to options and/or user or platform preferences */ diff --git a/src/cmd.c b/src/cmd.c index 0272ec0b9..4da0509d2 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -5125,6 +5125,7 @@ parse() register int foo; boolean prezero = FALSE; + iflags.in_parse = TRUE; multi = 0; context.move = 1; flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */ @@ -5191,6 +5192,8 @@ parse() clear_nhwindow(WIN_MESSAGE); if (prezero) in_line[0] = Cmd.spkeys[NHKF_ESC]; + + iflags.in_parse = FALSE; return in_line; } From 7eb05aef655cd3a17e4c330fbdf5de4a78612ad2 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 9 Dec 2017 10:51:41 -0500 Subject: [PATCH 07/14] git on windows was misbehaving on doc/window.doc Changes to be committed: modified: .gitattributes --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitattributes b/.gitattributes index 9af92ddc6..e23bd577d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,6 @@ *.hqx -text *.sln -text *.vcxproj -text +*.doc !diff +*.DOC !diff + From b4540086f8b12b9daeb8c4732a937e9ce698ccb3 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 12 Dec 2017 17:53:54 -0800 Subject: [PATCH 08/14] self-genocide's "you feel dead inside" It seems to me that the reaction to "you feel dead inside" when you're polymorphed into an undead creature at the time would be "so what else is new?". Vary the "dead" when current form is something which gets reported as "destroyed" rather than "killed" when killed. That happens for things flagged as non-living. Now undead "feel condemned inside" and golems "feel empty inside". Neither of those are ideal but they're more interesting than "feel dead inside". After becoming dead inside, give a reminder about that during enlightenment and if you restore a saved game in that condition. It was the latter that set this in motion: I wanted to confirm that restoring with u.uhp == -1 didn't give "you aren't healthy enough to survive restoration" when polymorphed. (It doesn't; the game resumes and you'll die if/when you rehumanize.) --- include/extern.h | 4 +++- include/mondata.h | 9 +++++---- src/allmain.c | 9 ++++++++- src/cmd.c | 11 ++++++++--- src/polyself.c | 36 +++++++++++++++++++++++++++++------- src/read.c | 6 +++--- 6 files changed, 56 insertions(+), 19 deletions(-) diff --git a/include/extern.h b/include/extern.h index de7c35999..6fd1df12f 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1508549428 2017/10/21 01:30:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.619 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1513130012 2017/12/13 01:53:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.621 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1881,6 +1881,8 @@ E const char *FDECL(mbodypart, (struct monst *, int)); E const char *FDECL(body_part, (int)); E int NDECL(poly_gender); E void FDECL(ugolemeffects, (int, int)); +E boolean NDECL(ugenocided); +E const char *NDECL(udeadinside); /* ### potion.c ### */ diff --git a/include/mondata.h b/include/mondata.h index cdb299007..2b186dc62 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 mondata.h $NHDT-Date: 1432512776 2015/05/25 00:12:56 $ $NHDT-Branch: master $:$NHDT-Revision: 1.26 $ */ +/* NetHack 3.6 mondata.h $NHDT-Date: 1513130015 2017/12/13 01:53:35 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.32 $ */ /* Copyright (c) 1989 Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ @@ -175,9 +175,10 @@ #define is_vampire(ptr) ((ptr)->mlet == S_VAMPIRE) -#define nonliving(ptr) \ - (is_golem(ptr) || is_undead(ptr) || (ptr)->mlet == S_VORTEX \ - || (ptr) == &mons[PM_MANES]) +/* used to vary a few messages */ +#define weirdnonliving(ptr) (is_golem(ptr) || (ptr)->mlet == S_VORTEX) +#define nonliving(ptr) \ + (is_undead(ptr) || (ptr) == &mons[PM_MANES] || weirdnonliving(ptr)) /* Used for conduct with corpses, tins, and digestion attacks */ /* G_NOCORPSE monsters might still be swallowed as a purple worm */ diff --git a/src/allmain.c b/src/allmain.c index 8834c6a60..4a0cfaf8f 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 allmain.c $NHDT-Date: 1463217182 2016/05/14 09:13:02 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.72 $ */ +/* NetHack 3.6 allmain.c $NHDT-Date: 1513130016 2017/12/13 01:53:36 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.81 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -640,6 +640,13 @@ boolean new_game; /* false => restoring an old game */ char buf[BUFSZ]; boolean currentgend = Upolyd ? u.mfemale : flags.female; + /* skip "welcome back" if restoring a doomed character */ + if (!new_game && Upolyd && ugenocided()) { + /* death via self-genocide is pending */ + pline("You're back, but you still feel %s inside.", udeadinside()); + return; + } + /* * The "welcome back" message always describes your innate form * even when polymorphed or wearing a helm of opposite alignment. diff --git a/src/cmd.c b/src/cmd.c index 4da0509d2..dc62c0ed3 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 cmd.c $NHDT-Date: 1508880573 2017/10/24 21:29:33 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.275 $ */ +/* NetHack 3.6 cmd.c $NHDT-Date: 1513130017 2017/12/13 01:53:37 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.277 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1887,8 +1887,13 @@ int final; Strcpy(youtoo, You_); /* not a traditional status but inherently obvious to player; more detail given below (attributes section) for magic enlightenment */ - if (Upolyd) - you_are("transformed", ""); + if (Upolyd) { + Strcpy(buf, "transformed"); + if (ugenocided()) + Sprintf(eos(buf), " and %s %s inside", + final ? "felt" : "feel", udeadinside()); + you_are(buf, ""); + } /* not a trouble, but we want to display riding status before maybe reporting steed as trapped or hero stuck to cursed saddle */ if (Riding) { diff --git a/src/polyself.c b/src/polyself.c index a7febb3b1..e66c522b6 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 polyself.c $NHDT-Date: 1497485548 2017/06/15 00:12:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.112 $ */ +/* NetHack 3.6 polyself.c $NHDT-Date: 1513130017 2017/12/13 01:53:37 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.115 $ */ /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -189,12 +189,7 @@ const char *fmt, *arg; You(fmt, arg); /* check whether player foolishly genocided self while poly'd */ - if ((mvitals[urole.malenum].mvflags & G_GENOD) - || (urole.femalenum != NON_PM - && (mvitals[urole.femalenum].mvflags & G_GENOD)) - || (mvitals[urace.malenum].mvflags & G_GENOD) - || (urace.femalenum != NON_PM - && (mvitals[urace.femalenum].mvflags & G_GENOD))) { + if (ugenocided()) { /* intervening activity might have clobbered genocide info */ struct kinfo *kptr = find_delayed_killer(POLYMORPH); @@ -1813,4 +1808,31 @@ polysense() } } +/* True iff hero's role or race has been genocided */ +boolean +ugenocided() +{ + return (boolean) ((mvitals[urole.malenum].mvflags & G_GENOD) + || (urole.femalenum != NON_PM + && (mvitals[urole.femalenum].mvflags & G_GENOD)) + || (mvitals[urace.malenum].mvflags & G_GENOD) + || (urace.femalenum != NON_PM + && (mvitals[urace.femalenum].mvflags & G_GENOD))); +} + +/* how hero feels "inside" after self-genocide of role or race */ +const char * +udeadinside() +{ + /* self-genocide used to always say "you feel dead inside" but that + seems silly when you're polymorphed into something undead; + monkilled() distinguishes between living (killed) and non (destroyed) + for monster death message; we refine the nonliving aspect a bit */ + return !nonliving(youmonst.data) + ? "dead" /* living, including demons */ + : !weirdnonliving(youmonst.data) + ? "condemned" /* undead plus manes */ + : "empty"; /* golems plus vortices */ +} + /*polyself.c*/ diff --git a/src/read.c b/src/read.c index 69d588427..e452bf33a 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 read.c $NHDT-Date: 1508479721 2017/10/20 06:08:41 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.148 $ */ +/* NetHack 3.6 read.c $NHDT-Date: 1513130018 2017/12/13 01:53:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.149 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2088,7 +2088,7 @@ do_class_genocide() u.uhp = -1; if (Upolyd) { if (!feel_dead++) - You_feel("dead inside."); + You_feel("%s inside.", udeadinside()); } else { if (!feel_dead++) You("die."); @@ -2269,7 +2269,7 @@ int how; /* KMH -- Unchanging prevents rehumanization */ if (Upolyd && ptr != youmonst.data) { delayed_killer(POLYMORPH, killer.format, killer.name); - You_feel("dead inside."); + You_feel("%s inside.", udeadinside()); } else done(GENOCIDED); } else if (ptr == youmonst.data) { From 892f210c1ef4fe6b5c4f5001e3374718ae8bcb79 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 14 Dec 2017 16:22:36 -0800 Subject: [PATCH 09/14] fix #H6610 - completely burnt paper golem When a monster killed a paper golem with a fire attack, the player was told that the golem "burns completely" yet it might still leave some blank scrolls as 'corpse'. The fix for that was one-line, but several other death-by-fire situations which didn't report "burns completely" were also leaving scrolls: fireball spell or scroll of fire or other fire explosions (if any), also wand of fire. Fire trap and poly'd hero with fire attack were already suppressing 'corpse'. --- doc/fixes36.1 | 2 ++ include/mondata.h | 6 +++++- src/explode.c | 20 ++++++++++++++------ src/mhitm.c | 6 +++--- src/mhitu.c | 7 +++---- src/uhitm.c | 8 ++++++-- src/zap.c | 20 ++++++++++++++++---- 7 files changed, 49 insertions(+), 20 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index b8dc23143..a99a24fd0 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -486,6 +486,8 @@ when trying to swap places with a pet and failing due to pet being trapped trap triggering) executed even though hero didn't ultimately move being "dead inside" (self-genocide while polymorphed) conferred partial invulnerability--normal monster behavior stopped attacking hero +if a fiery monster, wand of fire, or fiery explosion burned up a paper golem, + it could still leave blank scrolls Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/include/mondata.h b/include/mondata.h index 2b186dc62..95153ce5d 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 mondata.h $NHDT-Date: 1513130015 2017/12/13 01:53:35 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.32 $ */ +/* NetHack 3.6 mondata.h $NHDT-Date: 1513297342 2017/12/15 00:22:22 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.33 $ */ /* Copyright (c) 1989 Mike Threepoint */ /* NetHack may be freely redistributed. See license for details. */ @@ -180,6 +180,10 @@ #define nonliving(ptr) \ (is_undead(ptr) || (ptr) == &mons[PM_MANES] || weirdnonliving(ptr)) +/* no corpse (ie, blank scrolls) if killed by fire */ +#define completelyburns(ptr) \ + ((ptr) == &mons[PM_PAPER_GOLEM] || (ptr) == &mons[PM_STRAW_GOLEM]) + /* Used for conduct with corpses, tins, and digestion attacks */ /* G_NOCORPSE monsters might still be swallowed as a purple worm */ /* Maybe someday this could be in mflags... */ diff --git a/src/explode.c b/src/explode.c index cb672c1b6..86462a189 100644 --- a/src/explode.c +++ b/src/explode.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 explode.c $NHDT-Date: 1511658058 2017/11/26 01:00:58 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.54 $ */ +/* NetHack 3.6 explode.c $NHDT-Date: 1513297345 2017/12/15 00:22:25 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.55 $ */ /* Copyright (C) 1990 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -443,8 +443,12 @@ int expltype; mtmp->mhp -= (idamres + idamnonres); } if (mtmp->mhp <= 0) { + int xkflg = ((adtyp == AD_FIRE + && completelyburns(mtmp->data)) + ? XKILL_NOCORPSE : 0); + if (!context.mon_moving) { - killed(mtmp); + xkilled(mtmp, XKILL_GIVEMSG | xkflg); } else if (mdef && mtmp == mdef) { /* 'mdef' killed self trying to cure being turned * into slime due to some action by the player. @@ -456,11 +460,15 @@ int expltype; */ if (cansee(mtmp->mx, mtmp->my) || canspotmon(mtmp)) pline("%s is %s!", Monnam(mtmp), - nonliving(mtmp->data) ? "destroyed" - : "killed"); - xkilled(mtmp, XKILL_NOMSG | XKILL_NOCONDUCT); - } else + xkflg ? "burned completely" + : nonliving(mtmp->data) ? "destroyed" + : "killed"); + xkilled(mtmp, XKILL_NOMSG | XKILL_NOCONDUCT | xkflg); + } else { + if (xkflg) + adtyp = AD_RBRE; /* no corpse */ monkilled(mtmp, "", (int) adtyp); + } } else if (!context.mon_moving) { /* all affected monsters, even if mdef is set */ setmangry(mtmp, TRUE); diff --git a/src/mhitm.c b/src/mhitm.c index b267a96b0..3427645fe 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mhitm.c $NHDT-Date: 1504999944 2017/09/09 23:32:24 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.98 $ */ +/* NetHack 3.6 mhitm.c $NHDT-Date: 1513297346 2017/12/15 00:22:26 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.99 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -937,10 +937,10 @@ register struct attack *mattk; } if (vis && canseemon(mdef)) pline("%s is %s!", Monnam(mdef), on_fire(pd, mattk)); - if (pd == &mons[PM_STRAW_GOLEM] || pd == &mons[PM_PAPER_GOLEM]) { + if (completelyburns(pd)) { /* paper golem or straw golem */ if (vis && canseemon(mdef)) pline("%s burns completely!", Monnam(mdef)); - mondied(mdef); + mondead(mdef); /* was mondied() but that dropped paper scrolls */ if (mdef->mhp > 0) return 0; else if (mdef->mtame && !vis) diff --git a/src/mhitu.c b/src/mhitu.c index 7d07a8782..fb483b811 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mhitu.c $NHDT-Date: 1512808564 2017/12/09 08:36:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.148 $ */ +/* NetHack 3.6 mhitu.c $NHDT-Date: 1513297347 2017/12/15 00:22:27 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.149 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1030,9 +1030,8 @@ register struct attack *mattk; hitmsg(mtmp, mattk); if (uncancelled) { pline("You're %s!", on_fire(youmonst.data, mattk)); - if (youmonst.data == &mons[PM_STRAW_GOLEM] - || youmonst.data == &mons[PM_PAPER_GOLEM]) { - You("roast!"); + if (completelyburns(youmonst.data)) { /* paper or straw golem */ + You("go up in flames!"); /* KMH -- this is okay with unchanging */ rehumanize(); break; diff --git a/src/uhitm.c b/src/uhitm.c index 97e49dbca..df0645b96 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 uhitm.c $NHDT-Date: 1504999056 2017/09/09 23:17:36 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.167 $ */ +/* NetHack 3.6 uhitm.c $NHDT-Date: 1513297347 2017/12/15 00:22:27 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.172 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1543,9 +1543,13 @@ register struct attack *mattk; } if (!Blind) pline("%s is %s!", Monnam(mdef), on_fire(pd, mattk)); - if (pd == &mons[PM_STRAW_GOLEM] || pd == &mons[PM_PAPER_GOLEM]) { + if (completelyburns(pd)) { /* paper golem or straw golem */ if (!Blind) pline("%s burns completely!", Monnam(mdef)); + else + You("smell burning%s.", + (pd == &mons[PM_PAPER_GOLEM]) ? " paper" + : (pd == &mons[PM_STRAW_GOLEM]) ? " straw" : ""); xkilled(mdef, XKILL_NOMSG | XKILL_NOCORPSE); tmp = 0; break; diff --git a/src/zap.c b/src/zap.c index 1a8570991..4ab138938 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 zap.c $NHDT-Date: 1505475171 2017/09/15 11:32:51 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.267 $ */ +/* NetHack 3.6 zap.c $NHDT-Date: 1513297348 2017/12/15 00:22:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.270 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -4022,10 +4022,22 @@ boolean say; /* Announce out of sight hit/miss events if true */ if (tmp == MAGIC_COOKIE) { /* disintegration */ disintegrate_mon(mon, type, fltxt); } else if (mon->mhp < 1) { - if (type < 0) + if (type < 0) { + /* mon has just been killed by another monster */ monkilled(mon, fltxt, AD_RBRE); - else - killed(mon); + } else { + int xkflags = XKILL_GIVEMSG; /* killed(mon); */ + + /* killed by hero; we know 'type' isn't negative; + if it's fire, highly flammable monsters leave + no corpse; don't bother reporting that they + "burn completely" -- unnecessary verbosity */ + if ((type % 10 == ZT_FIRE) + /* paper golem or straw golem */ + && completelyburns(mon->data)) + xkflags |= XKILL_NOCORPSE; + xkilled(mon, xkflags); + } } else { if (!otmp) { /* normal non-fatal hit */ From c2fb384a5d03fac741a2cc479c883e83dffcdff6 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 14 Dec 2017 16:39:21 -0800 Subject: [PATCH 10/14] rehumanizing while Unchanging When hero poly'd into paper golem "burns completely" he is rehumanized even if he has the Unchanging attribute. A comment states that that is intentional, but there was no explanation given to the player. Report that "your amulet of unchanging failed" when rehumanization despite Unchanging happens. (Don't ask me how or why it fails; I don't know.) --- doc/fixes36.1 | 2 ++ src/polyself.c | 16 +++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index a99a24fd0..f2c33c3c3 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -488,6 +488,8 @@ being "dead inside" (self-genocide while polymorphed) conferred partial invulnerability--normal monster behavior stopped attacking hero if a fiery monster, wand of fire, or fiery explosion burned up a paper golem, it could still leave blank scrolls +hero poly'd into paper or straw golem reverts to human if burned up even when + Unchanging without any explanation given Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/polyself.c b/src/polyself.c index e66c522b6..d79f01f44 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 polyself.c $NHDT-Date: 1513130017 2017/12/13 01:53:37 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.115 $ */ +/* NetHack 3.6 polyself.c $NHDT-Date: 1513298347 2017/12/15 00:39:07 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.116 $ */ /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -1010,10 +1010,16 @@ void rehumanize() { /* You can't revert back while unchanging */ - if (Unchanging && (u.mh < 1)) { - killer.format = NO_KILLER_PREFIX; - Strcpy(killer.name, "killed while stuck in creature form"); - done(DIED); + if (Unchanging) { + if (u.mh < 1) { + killer.format = NO_KILLER_PREFIX; + Strcpy(killer.name, "killed while stuck in creature form"); + done(DIED); + } else if (uamul && uamul->otyp == AMULET_OF_UNCHANGING) { + Your("%s %s!", simpleonames(uamul), otense(uamul, "fail")); + uamul->dknown = 1; + makeknown(AMULET_OF_UNCHANGING); + } } if (emits_light(youmonst.data)) From 876d50992175e15e1ada648397ab6996102af727 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 14 Dec 2017 16:46:16 -0800 Subject: [PATCH 11/14] sanity check bit - current_wand 'current_wand' should always be Null at the time the sanity checking routine is called, so check for that. Also a couple of formatting bits. --- src/mkobj.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/mkobj.c b/src/mkobj.c index ed59caa9a..567b84528 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mkobj.c $NHDT-Date: 1501725405 2017/08/03 01:56:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.124 $ */ +/* NetHack 3.6 mkobj.c $NHDT-Date: 1513298759 2017/12/15 00:45:59 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.129 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2168,7 +2168,8 @@ static const char NEARDATA /* pline formats for insane_object() */ ofmt0[] = "%s obj %s %s: %s", ofmt3[] = "%s [not null] %s %s: %s", /* " held by mon %p (%s)" will be appended, filled by M,mon_nam(M) */ - mfmt1[] = "%s obj %s %s (%s)", mfmt2[] = "%s obj %s %s (%s) *not*"; + mfmt1[] = "%s obj %s %s (%s)", + mfmt2[] = "%s obj %s %s (%s) *not*"; /* Check all object lists for consistency. */ void @@ -2177,6 +2178,13 @@ obj_sanity_check() int x, y; struct obj *obj; + /* + * TODO: + * Should check whether the obj->bypass and/or obj->nomerge bits + * are set. Those are both used for temporary purposes and should + * be clear between moves. + */ + objlist_sanity(fobj, OBJ_FLOOR, "floor sanity"); /* check that the map's record of floor objects is consistent; @@ -2186,12 +2194,12 @@ obj_sanity_check() for (y = 0; y < ROWNO; y++) for (obj = level.objects[x][y]; obj; obj = obj->nexthere) { /* should match ; <0,*> should always be empty */ - if (obj->where != OBJ_FLOOR || x == 0 || obj->ox != x - || obj->oy != y) { + if (obj->where != OBJ_FLOOR || x == 0 + || obj->ox != x || obj->oy != y) { char at_fmt[BUFSZ]; - Sprintf(at_fmt, "%%s obj@<%d,%d> %%s %%s: %%s@<%d,%d>", x, - y, obj->ox, obj->oy); + Sprintf(at_fmt, "%%s obj@<%d,%d> %%s %%s: %%s@<%d,%d>", + x, y, obj->ox, obj->oy); insane_object(obj, at_fmt, "location sanity", (struct monst *) 0); } @@ -2219,7 +2227,11 @@ obj_sanity_check() if (kickedobj) insane_object(kickedobj, ofmt3, "kickedobj sanity", (struct monst *) 0); - /* [how about current_wand too?] */ + /* current_wand isn't removed from invent while in use, but should + be Null between moves when we're called */ + if (current_wand) + insane_object(current_wand, ofmt3, "current_wand sanity", + (struct monst *) 0); } /* sanity check for objects on specified list (fobj, &c) */ From f296c6605ded5cd59379df668b710e7d20d05af5 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 21 Dec 2017 10:04:18 -0800 Subject: [PATCH 12/14] fix #H6628 - secret doors display as wrong wall A relatively recent change to make secret doors within horizontal walls become horizontal doors after discovery was making some secret doors that should have remained vertical become horizontal too. While still hidden, they got displayed as horizontal wall segments in the midst of vertical walls. Example was the "Catacombs" (minend-3) variant of mines' end. The hidden door on the east wall of the entry room was shown as horizontal, while another one on the west wall of that same room was correctly vertical. This fix uses different criteria to decide horizontal vs vertical, partly because I couldn't understand how the previous code was supposed to work. Hidden doors now seem to display as correctly oriented walls and once discovered seem to become correctly oriented doors. I tested by checking quite a few special levels (and some regular ones)--but not all--with '#terrain d'. Plus some searching to unhide secret doors while using a custom symbol set that displayed closed horizontal doors (S_hcdoor) as '=' and vertical ones (S_vcdoor) as '"'. --- doc/fixes36.1 | 3 +++ src/sp_lev.c | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index f2c33c3c3..80e792766 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -552,6 +552,9 @@ fix mention_walls reporting secret doors as solid stone jumping over water unintentionally moved hero through that water, causing drowning if not able to water walk or fly try again to fix achievement recording bug with mines and sokoban prizes +the fix for secret doors on special levels always having vertical orientation + resulted in some--but not all--secret doors within vertical walls + being displayed as horizontal walls while still hidden Platform- and/or Interface-Specific Fixes diff --git a/src/sp_lev.c b/src/sp_lev.c index f5a532230..5981a1b5e 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1508879840 2017/10/24 21:17:20 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.90 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1513879435 2017/12/21 18:03:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.93 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -4181,8 +4181,8 @@ int dx, dy; genericptr_t arg; { xchar typ = *(xchar *) arg; - xchar x = dx; - xchar y = dy; + xchar x = dx, y = dy; + boolean left_or_right, up_and_down; if (!IS_DOOR(levl[x][y].typ) && levl[x][y].typ != SDOOR) levl[x][y].typ = (typ & D_SECRET) ? SDOOR : DOOR; @@ -4192,12 +4192,35 @@ genericptr_t arg; typ = D_CLOSED; } - if (((isok(x-1,y) && IS_DOORJOIN(levl[x-1][y].typ)) || !isok(x-1,y)) - || (isok(x+1,y) && IS_DOORJOIN(levl[x+1][y].typ)) || !isok(x+1,y)) - levl[x][y].horizontal = 1; - else - levl[x][y].horizontal = 0; - + /* If there's a wall or door on either the left side or right + * side (or both) of this secret door, make it be horizontal. + * + * It is feasible to put SDOOR in a corner, tee, or crosswall + * position, although once the door is found and opened it won't + * make a lot sense (diagonal access required). Still, we try to + * handle that as best as possible. For top or bottom tee, using + * horizontal is the best we can do. For corner or crosswall, + * either horizontal or vertical are just as good as each other; + * we produce horizontal for corners and vertical for crosswalls. + * For left or right tee, using vertical is best. + * + * A secret door with no adjacent walls is also feasible and makes + * even less sense. It will be displayed as a vertical wall while + * hidden and become a vertical door when found. + */ + left_or_right = ((isok(x - 1, y) && (IS_WALL(levl[x - 1][y].typ) + || IS_DOOR(levl[x - 1][y].typ) + || levl[x - 1][y].typ == SDOOR)) + || (isok(x + 1, y) && (IS_WALL(levl[x + 1][y].typ) + || IS_DOOR(levl[x + 1][y].typ) + || levl[x + 1][y].typ == SDOOR))); + up_and_down = ((isok(x, y - 1) && (IS_WALL(levl[x][y - 1].typ) + || IS_DOOR(levl[x][y - 1].typ) + || levl[x][y - 1].typ == SDOOR)) + && (isok(x, y + 1) && (IS_WALL(levl[x][y + 1].typ) + || IS_DOOR(levl[x][y + 1].typ) + || levl[x][y + 1].typ == SDOOR))); + levl[x][y].horizontal = (left_or_right && !up_and_down) ? 1 : 0; levl[x][y].doormask = typ; SpLev_Map[x][y] = 1; } From 93f61975f58a426cc6f58a9a4816976a08f04501 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 23 Dec 2017 15:19:27 -0800 Subject: [PATCH 13/14] flags.sortloot This should maximize save file compatibility between 3.6.1 and 3.6.0, at the risk of breaking save files for folks using post-3.6.0 git sources. (It's unlikely that many in that situation are using a configuration which will be affected, so probably nobody will notice.) --- include/flag.h | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/include/flag.h b/include/flag.h index a8f9172f7..d3938adca 100644 --- a/include/flag.h +++ b/include/flag.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 flag.h $NHDT-Date: 1508827590 2017/10/24 06:46:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.129 $ */ +/* NetHack 3.6 flag.h $NHDT-Date: 1514071158 2017/12/23 23:19:18 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.132 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -48,7 +48,42 @@ struct flag { boolean showexp; /* show experience points */ boolean showscore; /* show score */ boolean silent; /* whether the bell rings or not */ + /* The story so far: + * 'sortloot' originally took a True/False value but was changed + * to use a letter instead. 3.6.0 was released without changing its + * type from 'boolean' to 'char'. A compiler was smart enough to + * complain that assigning any of the relevant letters was not 0 or 1 + * so not appropriate for boolean (by a configuration which used + * SKIP_BOOLEAN to bypass nethack's 'boolean' and use a C++-compatible + * one). So the type was changed to 'xchar', which is guaranteed to + * match the size of 'boolean' (this guarantee only applies for the + * !SKIP_BOOLEAN config, unfortunately). Since xchar does not match + * actual use, the type was later changed to 'char'. But that would + * break 3.6.0 savefile compatibility for configurations which typedef + * 'schar' to 'short int' instead of to 'char'. (Needed by pre-ANSI + * systems that use unsigned characters without a way to force them + * to be signed.) So, the type has been changed back to 'xchar' for + * 3.6.1. + * + * TODO: change to 'char' (and move out of this block of booleans, + * and get rid of these comments...) once 3.6.0 savefile compatibility + * eventually ends. + */ +#ifndef SKIP_BOOLEAN + /* this is the normal configuration; assigning a character constant + for a normal letter to an 'xchar' variable should always work even + if 'char' is unsigned since character constants are actually 'int' + and letters are within the range where signedness shouldn't matter */ + xchar sortloot; /* 'n'=none, 'l'=loot (pickup), 'f'=full ('l'+invent) */ +#else + /* with SKIP_BOOLEAN, we have no idea what underlying type is being + used, other than it isn't 'xchar' (although its size might match + that) or a bitfield (because it must be directly addressable); + it's probably either 'char' for compactness or 'int' for access, + but we don't know which and it might be something else anyway; + flip a coin here and guess 'char' for compactness */ char sortloot; /* 'n'=none, 'l'=loot (pickup), 'f'=full ('l'+invent) */ +#endif boolean sortpack; /* sorted inventory */ boolean sparkle; /* show "resisting" special FX (Scott Bigham) */ boolean standout; /* use standout for --More-- */ From 062748c69554e276ad798e92c69ce13a27f2985f Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 23 Dec 2017 15:42:20 -0800 Subject: [PATCH 14/14] fix #H6648 - can't wear via 'W' but can via 'P' More fallout from allowing W/T on accessories and P/R on armor without combining them outright. If poly'd into verysmall or nohands critter, 'W' yields "don't even bother" before even prompting what to wear, but 'P' would prompt for an accessory and then wear armor if that was what got picked. Now 'P' will still prompt, in case it's for an accessory, but picking a piece of armor no longer wears that armor. 'W' still doesn't even prompt, so won't allow accessories as well as no armor. I'm not sure whether that should be changed. --- doc/fixes36.1 | 2 ++ src/do_wear.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 80e792766..69724485b 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -490,6 +490,8 @@ if a fiery monster, wand of fire, or fiery explosion burned up a paper golem, it could still leave blank scrolls hero poly'd into paper or straw golem reverts to human if burned up even when Unchanging without any explanation given +hero polymorphed into form which can't wear armor via 'W' (eliciting "don't + even bother") could wear it via 'P' Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/do_wear.c b/src/do_wear.c index 2adcb6816..f8f94e342 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do_wear.c $NHDT-Date: 1496959478 2017/06/08 22:04:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.98 $ */ +/* NetHack 3.6 do_wear.c $NHDT-Date: 1514072526 2017/12/23 23:42:06 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.100 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1617,6 +1617,14 @@ boolean noisy; int err = 0; const char *which; + /* this is the same check as for 'W' (dowear), but different message, + in case we get here via 'P' (doputon) */ + if (verysmall(youmonst.data) || nohands(youmonst.data)) { + if (noisy) + You("can't wear any armor in your current form."); + return 0; + } + which = is_cloak(otmp) ? c_cloak : is_shirt(otmp) @@ -1964,7 +1972,7 @@ dowear() /* cantweararm() checks for suits of armor, not what we want here; verysmall() or nohands() checks for shields, gloves, etc... */ - if ((verysmall(youmonst.data) || nohands(youmonst.data))) { + if (verysmall(youmonst.data) || nohands(youmonst.data)) { pline("Don't even bother."); return 0; }