From eaa8c278c88003562aaade45e223b5e656bbdba8 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 22 Jun 2019 00:05:16 -0400 Subject: [PATCH 1/3] code in parse_conf_file() to trim trailing blanks/cr was missing them Observed on a text file with crlf endings on Linux, where the so-called blank lines weren't being ignored as they should have been, despite there being a line to remove \r from the end. A pointer was being pre-decremented before the check for a matching whitespace character. --- doc/fixes36.3 | 1 + src/files.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.3 b/doc/fixes36.3 index f4e36ac3a..daedd86ec 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -79,6 +79,7 @@ for wizard mode 'monpolycontrol', allow usually disallowed type 'chameleon', add Space, Return, and Escape to '? k' (help for menu control keys) hero can no longer negotiate a bribe with a demon lord when deaf wishing for "foo amulet" now yields an "amulet of foo" rather than random one +code in parse_conf_file() to trim trailing blanks/cr was skipping over them Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository diff --git a/src/files.c b/src/files.c index 92940c9b0..29ad2f1a8 100644 --- a/src/files.c +++ b/src/files.c @@ -3051,9 +3051,9 @@ boolean FDECL((*proc), (char *)); *ep = '\0'; /* trim off spaces at end of line */ - while (--ep >= inbuf + while (ep >= inbuf && (*ep == ' ' || *ep == '\t' || *ep == '\r')) - *ep = '\0'; + *ep-- = '\0'; if (!config_error_nextline(inbuf)) { rv = FALSE; From 456996509b680e8a28f3f5d0401fd24d61248060 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 22 Jun 2019 13:03:50 -0700 Subject: [PATCH 2/3] fix #H8922 - no "you finish eating" message after being interrupted and then resuming. Resuming a multi-turn food when it only had one bite left was the culprit. --- doc/fixes36.3 | 6 +++- src/eat.c | 81 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/doc/fixes36.3 b/doc/fixes36.3 index daedd86ec..bbc324668 100644 --- a/doc/fixes36.3 +++ b/doc/fixes36.3 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.62 $ $NHDT-Date: 1561081353 2019/06/21 01:42:33 $ +$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.64 $ $NHDT-Date: 1561233801 2019/06/22 20:03:21 $ This fixes36.3 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.2 in May 2019. Please note, however, @@ -80,6 +80,10 @@ add Space, Return, and Escape to '? k' (help for menu control keys) hero can no longer negotiate a bribe with a demon lord when deaf wishing for "foo amulet" now yields an "amulet of foo" rather than random one code in parse_conf_file() to trim trailing blanks/cr was skipping over them +partly eaten food with one bite left had message anomalies when eaten; the + usual "you resume your meal" case lacked the "you're finished" message + when done; eating something else in between to clobber meal context + resulted in no messages at all when restarting and finishing last bite Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository diff --git a/src/eat.c b/src/eat.c index de0563310..efc04cdfb 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 eat.c $NHDT-Date: 1559670604 2019/06/04 17:50:04 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.202 $ */ +/* NetHack 3.6 eat.c $NHDT-Date: 1561233801 2019/06/22 20:03:21 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.203 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -25,7 +25,7 @@ STATIC_DCL void FDECL(cpostfx, (int)); STATIC_DCL void FDECL(consume_tin, (const char *)); STATIC_DCL void FDECL(start_tin, (struct obj *)); STATIC_DCL int FDECL(eatcorpse, (struct obj *)); -STATIC_DCL void FDECL(start_eating, (struct obj *)); +STATIC_DCL void FDECL(start_eating, (struct obj *, BOOLEAN_P)); STATIC_DCL void FDECL(fprefx, (struct obj *)); STATIC_DCL void FDECL(fpostfx, (struct obj *)); STATIC_DCL int NDECL(bite); @@ -1419,15 +1419,12 @@ const char *mesg; gainstr(tin, 0, FALSE); tin = costly_tin(COST_OPEN); - - lesshungry(tin->blessed - ? 600 /* blessed */ - : !tin->cursed - ? (400 + rnd(200)) /* uncursed */ - : (200 + rnd(400))); /* cursed */ + lesshungry(tin->blessed ? 600 /* blessed */ + : !tin->cursed ? (400 + rnd(200)) /* uncursed */ + : (200 + rnd(400))); /* cursed */ } -use_up_tin: + use_up_tin: if (carried(tin)) useup(tin); else @@ -1505,7 +1502,7 @@ struct obj *otmp; } pline("Using %s you try to open the tin.", yobjnam(uwep, (char *) 0)); } else { - no_opener: + no_opener: pline("It is not so easy to open this tin."); if (Glib) { pline_The("tin slips from your %s.", @@ -1729,8 +1726,9 @@ struct obj *otmp; /* called as you start to eat */ STATIC_OVL void -start_eating(otmp) +start_eating(otmp, already_partly_eaten) struct obj *otmp; +boolean already_partly_eaten; { const char *old_nomovemsg, *save_nomovemsg; @@ -1774,7 +1772,8 @@ struct obj *otmp; if (++context.victual.usedtime >= context.victual.reqtime) { /* print "finish eating" message if they just resumed -dlc */ - done_eating(context.victual.reqtime > 1 ? TRUE : FALSE); + done_eating((context.victual.reqtime > 1 + || already_partly_eaten) ? TRUE : FALSE); return; } @@ -1783,28 +1782,35 @@ struct obj *otmp; } /* - * called on "first bite" of (non-corpse) food. - * used for non-rotten non-tin non-corpse food + * Called on "first bite" of (non-corpse) food, after touchfood() has + * marked it 'partly eaten'. Used for non-rotten non-tin non-corpse food. + * Messages should use present tense since multi-turn food won't be + * finishing at the time they're issued. */ STATIC_OVL void fprefx(otmp) struct obj *otmp; { switch (otmp->otyp) { - case FOOD_RATION: + case FOOD_RATION: /* nutrition 800 */ + /* 200+800 remains below 1000+1, the satiation threshold */ if (u.uhunger <= 200) - pline(Hallucination ? "Oh wow, like, superior, man!" - : "That food really hit the spot!"); - else if (u.uhunger <= 700) - pline("That satiated your %s!", body_part(STOMACH)); + pline("%s!", Hallucination ? "Oh wow, like, superior, man" + : "This food really hits the spot"); + + /* 700-1+800 remains below 1500, the choking threshold which + triggers "you're having a hard time getting it down" feedback */ + else if (u.uhunger < 700) + pline("This satiates your %s!", body_part(STOMACH)); + /* [satiation message may be inaccurate if eating gets interrupted] */ break; case TRIPE_RATION: - if (carnivorous(youmonst.data) && !humanoid(youmonst.data)) - pline("That tripe ration was surprisingly good!"); - else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC))) + if (carnivorous(youmonst.data) && !humanoid(youmonst.data)) { + pline("This tripe ration is surprisingly good!"); + } else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC))) { pline(Hallucination ? "Tastes great! Less filling!" : "Mmm, tripe... not bad!"); - else { + } else { pline("Yak - dog food!"); more_experienced(1, 0); newexplevel(); @@ -1837,7 +1843,7 @@ struct obj *otmp; default: if (otmp->otyp == SLIME_MOLD && !otmp->cursed && otmp->spe == context.current_fruit) { - pline("My, that was a %s %s!", + pline("My, this is a %s %s!", Hallucination ? "primo" : "yummy", singular(otmp, xname)); } else if (otmp->otyp == APPLE && otmp->cursed && !Sleep_resistance) { @@ -1856,9 +1862,7 @@ struct obj *otmp; if (!Hallucination) { pline("Core dumped."); } else { - /* This is based on an old Usenet joke, a fake a.out manual - * page - */ + /* based on an old Usenet joke, a fake a.out manual page */ int x = rnd(100); pline("%s -- core dumped.", @@ -1876,7 +1880,7 @@ struct obj *otmp; will be abused more times before illness completes */ make_vomiting((Vomiting & TIMEOUT) + (long) d(10, 4), TRUE); } else { - give_feedback: + give_feedback: pline("This %s is %s", singular(otmp, xname), otmp->cursed ? (Hallucination ? "grody!" : "terrible!") @@ -2443,7 +2447,8 @@ doeat() { struct obj *otmp; int basenutrit; /* nutrition of full item */ - boolean dont_start = FALSE, nodelicious = FALSE; + boolean dont_start = FALSE, nodelicious = FALSE, + already_partly_eaten; if (Strangled) { pline("If you can't breathe air, how can you consume solids?"); @@ -2605,8 +2610,10 @@ doeat() context.victual.piece = touchfood(otmp); if (context.victual.piece) context.victual.o_id = context.victual.piece->o_id; - You("resume your meal."); - start_eating(context.victual.piece); + You("resume %syour meal.", + (context.victual.usedtime + 1 >= context.victual.reqtime) + ? "the last bite of " : ""); + start_eating(context.victual.piece, FALSE); return 1; } @@ -2621,6 +2628,7 @@ doeat() /* KMH, conduct */ u.uconduct.food++; + already_partly_eaten = otmp->oeaten ? TRUE : FALSE; context.victual.o_id = 0; context.victual.piece = otmp = touchfood(otmp); if (context.victual.piece) @@ -2674,8 +2682,13 @@ doeat() dont_start = TRUE; } consume_oeaten(otmp, 1); /* oeaten >>= 1 */ - } else + } else if (!already_partly_eaten) { fprefx(otmp); + } else { + You("%s %s.", + (context.victual.reqtime == 1) ? "eat" : "begin eating", + doname(otmp)); + } } /* re-calc the nutrition */ @@ -2707,7 +2720,7 @@ doeat() context.victual.canchoke = (u.uhs == SATIATED); if (!dont_start) - start_eating(otmp); + start_eating(otmp, already_partly_eaten); return 1; } @@ -3164,7 +3177,7 @@ int corpsecheck; /* 0, no check, 1, corpses, 2, tinnable corpses */ } } -skipfloor: + skipfloor: /* We cannot use ALL_CLASSES since that causes getobj() to skip its * "ugly checks" and we need to check for inedible items. */ From c968f03af3b4477a9749adf54887c9c21a120e99 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 22 Jun 2019 13:47:35 -0700 Subject: [PATCH 3/3] makemon() and clone_mon() Express the logic of various early returns more consistently. clone_mon() wasn't handling mon->isminion correctly. I'm not sure whether it is actually possible to clone a minion (maybe after polymorphing it into a gremlin or blue jelly?). When it wasn't tame, which is the case for every minion other than the guardian angel on Astral, the emin structure wasn't being allocated for the clone but its isminion flag was left set. Also, clones inherited mon->mtrack[] so would unnecessarily avoid moving onto spots the original had recently moved across. Cloned pets are inheriting various pet-specific fields that they probably should be starting with a clean slate on but I haven't made any attempt to address that. --- src/makemon.c | 123 ++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/src/makemon.c b/src/makemon.c index b2cb2c8a1..0f731c353 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 makemon.c $NHDT-Date: 1559733390 2019/06/05 11:16:30 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.137 $ */ +/* NetHack 3.6 makemon.c $NHDT-Date: 1561236435 2019/06/22 20:47:15 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.138 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -819,18 +819,20 @@ xchar x, y; /* clone's preferred location or 0 (near mon) */ if (x == 0) { mm.x = mon->mx; mm.y = mon->my; - if (!enexto(&mm, mm.x, mm.y, mon->data) || MON_AT(mm.x, mm.y)) - return (struct monst *) 0; - } else if (!isok(x, y)) { - return (struct monst *) 0; /* paranoia */ } else { mm.x = x; mm.y = y; - if (MON_AT(mm.x, mm.y)) { - if (!enexto(&mm, mm.x, mm.y, mon->data) || MON_AT(mm.x, mm.y)) - return (struct monst *) 0; - } } + if (!isok(mm.x, mm.y)) { /* paranoia */ + impossible("clone_mon trying to create a monster at <%d,%d>?", + mm.x, mm.y); + return (struct monst *) 0; + } + if (MON_AT(mm.x, mm.y)) { /* (always True for the x==0 case) */ + if (!enexto(&mm, mm.x, mm.y, mon->data) || MON_AT(mm.x, mm.y)) + return (struct monst *) 0; + } + m2 = newmonst(); *m2 = *mon; /* copy condition of old monster */ m2->mextra = (struct mextra *) 0; @@ -846,7 +848,7 @@ xchar x, y; /* clone's preferred location or 0 (near mon) */ m2->mtrapped = 0; m2->mcloned = 1; m2->minvent = (struct obj *) 0; /* objects don't clone */ - m2->mleashed = FALSE; + m2->mleashed = 0; /* Max HP the same, but current HP halved for both. The caller * might want to override this by halving the max HP also. * When current HP is odd, the original keeps the extra point. @@ -856,21 +858,20 @@ xchar x, y; /* clone's preferred location or 0 (near mon) */ m2->mhp = mon->mhp / 2; mon->mhp -= m2->mhp; - /* since shopkeepers and guards will only be cloned if they've been - * polymorphed away from their original forms, the clone doesn't have - * room for the extra information. we also don't want two shopkeepers - * around for the same shop. - */ - if (mon->isshk) - m2->isshk = FALSE; - if (mon->isgd) - m2->isgd = FALSE; - if (mon->ispriest) - m2->ispriest = FALSE; + /* clone doesn't have mextra so mustn't retain special monster flags */ + m2->isshk = 0; + m2->isgd = 0; + m2->ispriest = 0; + /* ms->isminion handled below */ + + /* clone shouldn't be reluctant to move on spots 'parent' just moved on */ + (void) memset((genericptr_t) m2->mtrack, 0, sizeof m2->mtrack); + place_monster(m2, m2->mx, m2->my); if (emits_light(m2->data)) new_light_source(m2->mx, m2->my, emits_light(m2->data), LS_MONSTER, monst_to_any(m2)); + /* if 'parent' is named, give the clone the same name */ if (has_mname(mon)) { m2 = christen_monst(m2, MNAME(mon)); } else if (mon->isshk) { @@ -878,31 +879,35 @@ xchar x, y; /* clone's preferred location or 0 (near mon) */ } /* not all clones caused by player are tame or peaceful */ - if (!context.mon_moving) { + if (!context.mon_moving && mon->mpeaceful) { if (mon->mtame) m2->mtame = rn2(max(2 + u.uluck, 2)) ? mon->mtame : 0; else if (mon->mpeaceful) m2->mpeaceful = rn2(max(2 + u.uluck, 2)) ? 1 : 0; } + /* if guardian angel could be cloned (maybe after polymorph?), + m2 could be both isminion and mtame; isminion takes precedence */ + if (m2->isminion) { + int atyp; - newsym(m2->mx, m2->my); /* display the new monster */ - if (m2->mtame) { - if (mon->isminion) { - newemin(m2); - if (EMIN(mon)) - *(EMIN(m2)) = *(EMIN(mon)); - } else { - /* because m2 is a copy of mon it is tame but not init'ed. - * however, tamedog will not re-tame a tame dog, so m2 - * must be made non-tame to get initialized properly. - */ - m2->mtame = 0; - if (tamedog(m2, (struct obj *) 0)) { - *(EDOG(m2)) = *(EDOG(mon)); - } - } + newemin(m2); + *EMIN(m2) = *EMIN(mon); + /* renegade when same alignment as hero but not peaceful or + when peaceful while being different alignment from hero */ + atyp = EMIN(m2)->min_align; + EMIN(m2)->renegade = (atyp != u.ualign.type) ^ !m2->mpeaceful; + } else if (m2->mtame) { + /* Because m2 is a copy of mon it is tame but not init'ed. + However, tamedog() will not re-tame a tame dog, so m2 + must be made non-tame to get initialized properly. */ + m2->mtame = 0; + if (tamedog(m2, (struct obj *) 0)) + *EDOG(m2) = *EDOG(mon); + /* [TODO? some (most? all?) edog fields probably should be + reinitialized rather that retain the 'parent's values] */ } set_malign(m2); + newsym(m2->mx, m2->my); /* display the new monster */ return m2; } @@ -1107,6 +1112,8 @@ register int x, y; int mmflags; { register struct monst *mtmp; + struct monst fakemon; + coord cc; int mndx, mcham, ct, mitem; boolean anymon = (!ptr); boolean byyou = (x == u.ux && y == u.uy); @@ -1114,40 +1121,37 @@ int mmflags; boolean countbirth = ((mmflags & MM_NOCOUNTBIRTH) == 0); unsigned gpflags = (mmflags & MM_IGNOREWATER) ? MM_IGNOREWATER : 0; + fakemon = zeromonst; + cc.x = cc.y = 0; + /* if caller wants random location, do it here */ if (x == 0 && y == 0) { - coord cc; - struct monst fakemon; - - cc.x = cc.y = 0; /* lint suppression */ - fakemon = zeromonst; fakemon.data = ptr; /* set up for goodpos */ - if (!makemon_rnd_goodpos(ptr ? &fakemon : (struct monst *)0, + if (!makemon_rnd_goodpos(ptr ? &fakemon : (struct monst *) 0, gpflags, &cc)) return (struct monst *) 0; x = cc.x; y = cc.y; } else if (byyou && !in_mklev) { - coord bypos; - - if (enexto_core(&bypos, u.ux, u.uy, ptr, gpflags)) { - x = bypos.x; - y = bypos.y; - } else + if (!enexto_core(&cc, u.ux, u.uy, ptr, gpflags)) return (struct monst *) 0; + x = cc.x; + y = cc.y; + } + + /* sanity check */ + if (!isok(x, y)) { + impossible("makemon trying to create a monster at <%d,%d>?", x, y); + return (struct monst *) 0; } /* Does monster already exist at the position? */ if (MON_AT(x, y)) { - if ((mmflags & MM_ADJACENTOK) != 0) { - coord bypos; - if (enexto_core(&bypos, x, y, ptr, gpflags)) { - x = bypos.x; - y = bypos.y; - } else - return (struct monst *) 0; - } else + if (!(mmflags & MM_ADJACENTOK) + || !enexto_core(&cc, x, y, ptr, gpflags)) return (struct monst *) 0; + x = cc.x; + y = cc.y; } if (ptr) { @@ -1167,7 +1171,6 @@ int mmflags; * for instance.) */ int tryct = 0; /* maybe there are no good choices */ - struct monst fakemon; do { if (!(ptr = rndmonst())) { @@ -1354,6 +1357,7 @@ int mmflags; ? !(mmflags & (MM_EPRI | MM_EMIN)) : (mndx == PM_ANGEL && !(mmflags & MM_EMIN) && !rn2(3))) { struct emin *eminp; + newemin(mtmp); eminp = EMIN(mtmp); @@ -1385,6 +1389,7 @@ int mmflags; if (!rn2(100) && is_domestic(ptr) && can_saddle(mtmp) && !which_armor(mtmp, W_SADDLE)) { struct obj *otmp = mksobj(SADDLE, TRUE, FALSE); + put_saddle_on_mon(otmp, mtmp); }