From: PatR Date: Sat, 12 Feb 2022 00:17:17 +0000 (-0800) Subject: fix gamelog 1st kill vs 1st weapon hit sequencing X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2b4cf042812ef8ded9b496fd35bab7523156b009;p=nethack fix gamelog 1st kill vs 1st weapon hit sequencing If the first monster the hero kills is killed by the hero's first hit with a wielded weapon, report the hit first and kill second instead of the other way around. Not as hard to manage as I feared, but bound to be more fragile than the simpler handling that produced the odd order. Also while testing it I knocked something into a polymorph trap and it changed form without any feedback. Give foo-changes-into-bar message if the hero is moving and can see it happening. It isn't needed with a monster moves deliberately into a polymorph trap but probably would be useful when that's is unintentional. The " enters the dungeon" log message had a trailing period but other log messages don't have sentence punctuation, so take that off. --- diff --git a/src/allmain.c b/src/allmain.c index d518b7499..74260e859 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -762,7 +762,7 @@ welcome(boolean new_game) /* false => restoring an old game */ l_nhcore_call(new_game ? NHCORE_START_NEW_GAME : NHCORE_RESTORE_OLD_GAME); if (new_game) - livelog_printf(LL_MINORAC, "%s the%s entered the dungeon.", + livelog_printf(LL_MINORAC, "%s the%s entered the dungeon", g.plname, buf); } diff --git a/src/trap.c b/src/trap.c index 720fef778..9e60060d3 100644 --- a/src/trap.c +++ b/src/trap.c @@ -2129,7 +2129,12 @@ trapeffect_poly_trap( if (resists_magm(mtmp)) { shieldeff(mtmp->mx, mtmp->my); } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { - (void) newcham(mtmp, (struct permonst *) 0, FALSE, FALSE); + (void) newcham(mtmp, (struct permonst *) 0, FALSE, + /* if hero is moving, he probably just swapped + places with a pet or perhaps used a joust + attack to push mtmp into the trap; describe + mtmp's transformation into another shape */ + (!g.context.mon_moving && in_sight)); if (in_sight) seetrap(trap); } diff --git a/src/uhitm.c b/src/uhitm.c index 4638351c0..71b34b028 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -23,6 +23,9 @@ static void end_engulf(void); static int gulpum(struct monst *, struct attack *); static boolean hmonas(struct monst *); static void nohandglow(struct monst *); +static boolean mhurtle_to_doom(struct monst *, int, struct permonst **, + boolean); +static void first_weapon_hit(void); static boolean shade_aware(struct obj *); #define PROJECTILE(obj) ((obj) && is_ammo(obj)) @@ -568,10 +571,6 @@ known_hitum( if (mon->wormno && *mhit) cutworm(mon, g.bhitpos.x, g.bhitpos.y, slice_or_chop); } - if (u.uconduct.weaphit && !oldweaphit) - livelog_printf(LL_CONDUCT, - "hit with a wielded weapon for the first time"); - } return malive; } @@ -743,6 +742,11 @@ hmon_hitmon( * damage it did _before_ outputting a hit message, but any messages * associated with the damage don't come out until _after_ outputting * a hit message. + * + * More complications: first_weapon_hit() should be called before + * xkilled() in order to have the gamelog messages in the right order. + * So it can't be deferred until end of known_hitum() as was originally + * done. We might call it directly or indirectly via mhurtle_to_doom(). */ boolean hittxt = FALSE, destroyed = FALSE, already_killed = FALSE; boolean get_dmg_bonus = TRUE; @@ -1300,13 +1304,8 @@ hmon_hitmon( useup(obj); obj = (struct obj *) 0; } - /* avoid migrating a dead monster */ - if (mon->mhp > tmp) { - mhurtle(mon, u.dx, u.dy, 1); - mdat = mon->data; /* in case of a polymorph trap */ - if (DEADMONSTER(mon)) - already_killed = TRUE; - } + if (mhurtle_to_doom(mon, tmp, &mdat, TRUE)) + already_killed = TRUE; hittxt = TRUE; } else if (unarmed && tmp > 1 && !thrown && !obj && !Upolyd) { /* VERY small chance of stunning opponent if unarmed. */ @@ -1315,19 +1314,21 @@ hmon_hitmon( if (canspotmon(mon)) pline("%s %s from your powerful strike!", Monnam(mon), makeplural(stagger(mon->data, "stagger"))); - /* avoid migrating a dead monster */ - if (mon->mhp > tmp) { - mhurtle(mon, u.dx, u.dy, 1); - mdat = mon->data; /* in case of a polymorph trap */ - if (DEADMONSTER(mon)) - already_killed = TRUE; - } + if (mhurtle_to_doom(mon, tmp, &mdat, FALSE)) + already_killed = TRUE; hittxt = TRUE; } } - if (!already_killed) + if (!already_killed) { + if (obj && (obj == uwep || (obj == uswapwep && u.twoweap)) + && (thrown == HMON_MELEE || thrown == HMON_APPLIED) + /* note: caller has already incremented u.uconduct.weaphit + so we test for 1; 0 shouldn't be able to happen here... */ + && tmp > 0 && u.uconduct.weaphit <= 1) + first_weapon_hit(); mon->mhp -= tmp; + } /* adjustments might have made tmp become less than what a level draining artifact has already done to max HP */ if (mon->mhp > mon->mhpmax) @@ -1483,6 +1484,47 @@ hmon_hitmon( return destroyed ? FALSE : TRUE; } +/* joust or martial arts punch is knocking the target back; that might + kill 'mon' (via trap) before known_hitum() has a chance to do so; + return True if we kill mon, False otherwise */ +static boolean +mhurtle_to_doom( + struct monst *mon, /* target monster */ + int tmp, /* amount of pending damage */ + struct permonst **mptr, /* caller's cached copy of mon->data */ + boolean by_wielded_weapon) /* True: violating a potential conduct */ +{ + /* if this hit is breaking the never-hit-with-wielded-weapon conduct + (handled by caller's caller...) we need to log the message about + that before mon is killed; without this, the log entry sequence + N : killed for the first time + N : hit with a wielded weapon for the first time + reported on the same turn (N) looks "suboptimal"; + u.uconduct.weaphit has already been incremented => 1 is first hit */ + if (by_wielded_weapon && u.uconduct.weaphit <= 1) + first_weapon_hit(); + + /* only hurtle if pending physical damage (tmp) isn't going to kill mon */ + if (tmp < mon->mhp) { + mhurtle(mon, u.dx, u.dy, 1); + /* update caller's cached mon->data in case mon was pushed into + a polymorph trap or is a vampshifter whose current form has + been killed by a trap so that it reverted to original form */ + *mptr = mon->data; + if (DEADMONSTER(mon)) + return TRUE; + } + return FALSE; /* mon isn't dead yet */ +} + +/* gamelog version of "you've broken never-hit-with-wielded-weapon conduct; + the conduct is tracked in known_hitum(); we're called by hmon_hitmon() */ +static void +first_weapon_hit(void) +{ + livelog_printf(LL_CONDUCT, "hit with a wielded weapon for the first time"); +} + static boolean shade_aware(struct obj *obj) {