From e12d2d326b9f4e552c16c16e02f2a78fdb505e71 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 26 Mar 2019 16:58:52 -0700 Subject: [PATCH 1/3] fix #H7454 - Cleaver vs long worm tails Cleaver's ability to hit up to three adjacent targets could kill a long worm and then try to cut it in two. When this was first reported I was unable to reproduce it, but this time I've managed to do so. But not reliably, so it's hard to claim that it's now fixed. However, the new report's explanation of why it happens and suggested fix was a big help. I had been trying to hit three tail segments, but you need to attack a segment next to the head, then have Cleaver hit and kill the head first (50:50 chance depending upon whether current swing is clockwise or counter). Worm cutting would be looking at the location of the targetted segment but there won't be any monster there when the head dies. (Cleaver's attack itself already copes the situation where its 2nd and/or 3rd potential targets aren't there any more by the time it's ready to try to hit them.) --- doc/fixes36.2 | 4 +++- src/uhitm.c | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 6ddbc7866..4e3edb5a3 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.282 $ $NHDT-Date: 1553480403 2019/03/25 02:20:03 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.283 $ $NHDT-Date: 1553644725 2019/03/26 23:58:45 $ 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, @@ -396,6 +396,8 @@ status lines' dungeon location field in the elemental planes now shows Earth or Air or Fire or Water instead of generic End Game avoid spurious status refresh when hero gains experience while 'showexp' and 'showscore' options are off +using Cleaver to attack a worm tail segment but kill adjacent head first would + result in an impossible warning from cutworm Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/uhitm.c b/src/uhitm.c index c71d9946a..8ed66e919 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 uhitm.c $NHDT-Date: 1548209742 2019/01/23 02:15:42 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.203 $ */ +/* NetHack 3.6 uhitm.c $NHDT-Date: 1553644725 2019/03/26 23:58:45 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.206 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -481,7 +481,7 @@ int dieroll; if (!*mhit) { missum(mon, uattk, (rollneeded + armorpenalty > dieroll)); } else { - int oldhp = mon->mhp, x = u.ux + u.dx, y = u.uy + u.dy; + int oldhp = mon->mhp; long oldweaphit = u.uconduct.weaphit; /* KMH, conduct */ @@ -490,7 +490,7 @@ int dieroll; /* we hit the monster; be careful: it might die or be knocked into a different location */ - notonhead = (mon->mx != x || mon->my != y); + notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y); malive = hmon(mon, weapon, HMON_MELEE, dieroll); if (malive) { /* monster still alive */ @@ -510,7 +510,7 @@ int dieroll; u.uconduct.weaphit = oldweaphit; } if (mon->wormno && *mhit) - cutworm(mon, x, y, slice_or_chop); + cutworm(mon, bhitpos.x, bhitpos.y, slice_or_chop); } } return malive; @@ -530,6 +530,7 @@ struct attack *uattk; /* ... but we don't enforce that here; Null works ok */ simulation attempt a bit */ static boolean clockwise = FALSE; unsigned i; + coord save_bhitpos; int count, umort, x = u.ux, y = u.uy; /* find the direction toward primary target */ @@ -546,6 +547,7 @@ struct attack *uattk; /* ... but we don't enforce that here; Null works ok */ to primary target */ i = (i + (clockwise ? 6 : 2)) % 8; umort = u.umortality; /* used to detect life-saving */ + save_bhitpos = bhitpos; /* * Three attacks: adjacent to primary, primary, adjacent on other @@ -576,6 +578,7 @@ struct attack *uattk; /* ... but we don't enforce that here; Null works ok */ &attknum, &armorpenalty); dieroll = rnd(20); mhit = (tmp > dieroll); + bhitpos.x = tx, bhitpos.y = ty; /* normally set up by attack() */ (void) known_hitum(mtmp, uwep, &mhit, tmp, armorpenalty, uattk, dieroll); (void) passive(mtmp, uwep, mhit, !DEADMONSTER(mtmp), AT_WEAP, !uwep); @@ -587,6 +590,8 @@ struct attack *uattk; /* ... but we don't enforce that here; Null works ok */ } /* set up for next time */ clockwise = !clockwise; /* alternate */ + bhitpos = save_bhitpos; /* in case somebody relies on bhitpos + * designating the primary target */ /* return False if primary target died, True otherwise; note: if 'target' was nonNull upon entry then it's still nonNull even if *target died */ @@ -616,6 +621,7 @@ struct attack *uattk; if (tmp > dieroll) exercise(A_DEX, TRUE); + /* bhitpos is set up by caller */ malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk, dieroll); if (wepbefore && !uwep) wep_was_destroyed = TRUE; @@ -2285,13 +2291,14 @@ register struct monst *mon; struct attack *mattk, alt_attk; struct obj *weapon, **originalweapon; boolean altwep = FALSE, weapon_used = FALSE, odd_claw = TRUE; - int i, tmp, armorpenalty, sum[NATTK] = { 0 }, nsum = 0, dhit = 0, attknum = 0; + int i, tmp, armorpenalty, sum[NATTK], nsum = 0, dhit = 0, attknum = 0; int dieroll, multi_claw = 0; /* with just one touch/claw/weapon attack, both rings matter; with more than one, alternate right and left when checking whether silver ring causes successful hit */ for (i = 0; i < NATTK; i++) { + sum[i] = 0; mattk = getmattk(&youmonst, mon, i, sum, &alt_attk); if (mattk->aatyp == AT_WEAP || mattk->aatyp == AT_CLAW || mattk->aatyp == AT_TUCH) @@ -2300,7 +2307,7 @@ register struct monst *mon; multi_claw = (multi_claw > 1); /* switch from count to yes/no */ for (i = 0; i < NATTK; i++) { - sum[i] = 0; + /* sum[i] = 0; -- now done above */ mattk = getmattk(&youmonst, mon, i, sum, &alt_attk); weapon = 0; switch (mattk->aatyp) { @@ -2357,6 +2364,7 @@ register struct monst *mon; &armorpenalty); dieroll = rnd(20); dhit = (tmp > dieroll || u.uswallow); + /* caller must set bhitpos */ if (!known_hitum(mon, weapon, &dhit, tmp, armorpenalty, mattk, dieroll)) { /* enemy dead, before any special abilities used */ From 42cb4ac3e8169d761c27db66e6b092da8bd3b268 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 26 Mar 2019 19:16:01 -0700 Subject: [PATCH 2/3] disclosure fix Back in December, a change was made to suppress status when u.uhp == -1. But if the hero died with exactly that amount, the status display would be blanked out during end of game disclosure. Force u.uhp to be 0 when dying. That was already happening if death occurred while hero still had positive HP, but not when damage took him/her to negative. --- doc/fixes36.2 | 4 +++- src/end.c | 11 ++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 4e3edb5a3..78fba893f 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.283 $ $NHDT-Date: 1553644725 2019/03/26 23:58:45 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.284 $ $NHDT-Date: 1553652951 2019/03/27 02:15:51 $ 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, @@ -467,6 +467,8 @@ having an artifact wish be refused ("for a moment you feel in using 'O' to set status hilites for any status condition (Blind, &c) and specifying more than one attribute (Bold, Inverse, &c) it would only retain one of the chosen attributes +when u.uhp==-1 became a flag to suppress status updating, if game ended when + hero died with exactly -1 HP, status would be blank during disclosure 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/end.c b/src/end.c index e2090bcdf..e52145755 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1549921169 2019/02/11 21:39:29 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.163 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1553652951 2019/03/27 02:15:51 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.166 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1086,10 +1086,11 @@ int how; if (how < PANICKED) { u.umortality++; /* in case caller hasn't already done this */ - if (u.uhp > 0 || (Upolyd && u.mh > 0)) { - /* for deaths not triggered by loss of hit points, force - current HP to zero (0 HP when turning into green slime - is iffy but we don't have much choice--that is fatal) */ + if (u.uhp != 0 || (Upolyd && u.mh != 0)) { + /* force HP to zero in case it is still positive (some + deaths aren't triggered by loss of hit points), or + negative (-1 is used as a flag in some circumstances + which don't apply when actually dying due to HP loss) */ u.uhp = u.mh = 0; context.botl = 1; } From 91ab87db2ee3cde029fa8fc70f1659e93bb9d210 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 26 Mar 2019 19:27:11 -0700 Subject: [PATCH 3/3] tty hitpointbar Catch up with curses and have hitpointbar work even if statushilites is 0 to suppress other highlighting. Indirectly fixes #H8389 by making the circumstance which triggered that bug no longer do so. --- doc/fixes36.2 | 3 +- win/tty/wintty.c | 90 ++++++++++++++++++++++-------------------------- 2 files changed, 44 insertions(+), 49 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 78fba893f..9439ab4d3 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.284 $ $NHDT-Date: 1553652951 2019/03/27 02:15:51 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.285 $ $NHDT-Date: 1553653612 2019/03/27 02:26:52 $ 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, @@ -477,6 +477,7 @@ tty: fix an out of bounds error in tty_status_update() for BL_HUNGER case tty: fix leftover display artifact when the last field on the row got placed to the left of where it was previously due to it, or one of the fields to its left, getting shorter +tty: support hitpointbar even when statushilites is set to 0 X11: its use of genl_status_update exposed a negative index use that could lead to a segfault X11: rollback disabling of keystroke input for PICK_NONE menus (for scrolling) diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 1fc1af000..212cd2997 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1550629490 2019/02/20 02:24:50 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.196 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1553653619 2019/03/27 02:26:59 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.197 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -3838,9 +3838,6 @@ unsigned long *colormasks; /* Special additional processing for hitpointbar */ hpbar_percent = percent; hpbar_color = (color & 0x00FF); - } - if (iflags.wc2_hitpointbar - && (tty_procs.wincap2 & WC2_FLUSH_STATUS) != 0L) { tty_status[NOW][BL_TITLE].color = hpbar_color; tty_status[NOW][BL_TITLE].dirty = TRUE; } @@ -3850,17 +3847,18 @@ unsigned long *colormasks; /* The core sends trailing blanks for some fields. Let's suppress the trailing blanks */ if (tty_status[NOW][fldidx].lth > 0) { - lastchar = eos(status_vals[fldidx]); - lastchar--; - while (lastchar >= status_vals[fldidx] && *lastchar == ' ') { - *lastchar-- = '\0'; + p = eos(status_vals[fldidx]); + for (lastchar = eos(p); lastchar > p && *--lastchar == ' '; ) { + *lastchar = '\0'; tty_status[NOW][fldidx].lth--; } } break; case BL_TITLE: + /* when hitpointbar is enabled, rendering will enforce a length + of 30 on title, padding with spaces or truncating if necessary */ if (iflags.wc2_hitpointbar) - tty_status[NOW][fldidx].lth += 2; /* '[' and ']' */ + tty_status[NOW][fldidx].lth = 30 + 2; /* '[' and ']' */ break; case BL_GOLD: /* \GXXXXNNNN counts as 1 */ @@ -4371,54 +4369,50 @@ render_status(VOID_ARGS) * +-------------------------+ */ /* hitpointbar using hp percent calculation */ - int bar_pos, bar_len; - char *bar2 = (char *)0; - char bar[MAXCO], savedch = 0; - boolean twoparts = FALSE; + int bar_len, bar_pos = 0; + char bar[MAXCO], *bar2 = (char *) 0, savedch = '\0'; + boolean twoparts = (hpbar_percent < 100); - bar_len = strlen(text); - if (bar_len < MAXCO-1) { + /* force exactly 30 characters, padded with spaces + if shorter or truncated if longer */ + if (strlen(text) != 30) { + Sprintf(bar, "%-30.30s", text); + Strcpy(status_vals[BL_TITLE], bar); + } else Strcpy(bar, text); + bar_len = (int) strlen(bar); /* always 30 */ + /* when at full HP, the whole title will be highlighted; + when injured or dead, there will be a second portion + which is not highlighted */ + if (twoparts) { + /* figure out where to separate the two parts */ bar_pos = (bar_len * hpbar_percent) / 100; if (bar_pos < 1 && hpbar_percent > 0) bar_pos = 1; if (bar_pos >= bar_len && hpbar_percent < 100) bar_pos = bar_len - 1; - if (bar_pos > 0 && bar_pos < bar_len) { - twoparts = TRUE; - bar2 = &bar[bar_pos]; - savedch = *bar2; - *bar2 = '\0'; - } + bar2 = &bar[bar_pos]; + savedch = *bar2; + *bar2 = '\0'; } - if (iflags.hilite_delta) { - char *s = bar; - tty_putstatusfield(nullfield, "[", x++, y); - if (hpbar_percent > 0) { - if (hpbar_color != NO_COLOR && coloridx != CLR_MAX) - term_start_color(hpbar_color); - term_start_attr(ATR_INVERSE); - } - if (hpbar_percent == 0) - s = text; - tty_putstatusfield(nullfield, s, x, y); - x += (int) strlen(s); - if (hpbar_percent > 0) { - term_end_attr(ATR_INVERSE); - if (hpbar_color != NO_COLOR && coloridx != CLR_MAX) - term_end_color(); - } - if (twoparts && hpbar_percent > 0) { - *bar2 = savedch; - tty_putstatusfield(nullfield, bar2, x, y); - x += (int) strlen(bar2); - tty_curs(WIN_STATUS, x, y); - } - tty_putstatusfield(nullfield, "]", x++, y); - } else { - tty_putstatusfield(&tty_status[NOW][idx], - (char *) 0, x, y); + tty_putstatusfield(nullfield, "[", x++, y); + if (*bar) { /* always True, unless twoparts+dead (0 HP) */ + term_start_attr(ATR_INVERSE); + if (iflags.hilite_delta && hpbar_color != NO_COLOR) + term_start_color(hpbar_color); + tty_putstatusfield(nullfield, bar, x, y); + x += (int) strlen(bar); + if (iflags.hilite_delta && hpbar_color != NO_COLOR) + term_end_color(); + term_end_attr(ATR_INVERSE); } + if (twoparts) { /* no highlighting for second part */ + *bar2 = savedch; + tty_putstatusfield(nullfield, bar2, x, y); + x += (int) strlen(bar2); + /*tty_curs(WIN_STATUS, x, y);*/ + } + tty_putstatusfield(nullfield, "]", x++, y); } else { /* * +-----------------------------+