diff --git a/doc/fixes36.3 b/doc/fixes36.3 index f4e36ac3a..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, @@ -79,6 +79,11 @@ 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 +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 6ba9718e8..cafbf8f0a 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); @@ -1413,15 +1413,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 @@ -1499,7 +1496,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.", @@ -1723,8 +1720,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; static char msgbuf[BUFSZ]; @@ -1769,7 +1767,8 @@ struct obj *otmp; if (++g.context.victual.usedtime >= g.context.victual.reqtime) { /* print "finish eating" message if they just resumed -dlc */ - done_eating(g.context.victual.reqtime > 1 ? TRUE : FALSE); + done_eating((g.context.victual.reqtime > 1 + || already_partly_eaten) ? TRUE : FALSE); return; } @@ -1778,28 +1777,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(g.youmonst.data) && !humanoid(g.youmonst.data)) - pline("That tripe ration was surprisingly good!"); - else if (maybe_polyd(is_orc(g.youmonst.data), Race_if(PM_ORC))) + if (carnivorous(g.youmonst.data) && !humanoid(g.youmonst.data)) { + pline("This tripe ration is surprisingly good!"); + } else if (maybe_polyd(is_orc(g.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(); @@ -1832,7 +1838,7 @@ struct obj *otmp; default: if (otmp->otyp == SLIME_MOLD && !otmp->cursed && otmp->spe == g.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) { @@ -1851,9 +1857,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.", @@ -1871,7 +1875,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!") @@ -2438,7 +2442,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?"); @@ -2600,8 +2605,10 @@ doeat() g.context.victual.piece = touchfood(otmp); if (g.context.victual.piece) g.context.victual.o_id = g.context.victual.piece->o_id; - You("resume your meal."); - start_eating(g.context.victual.piece); + You("resume %syour meal.", + (g.context.victual.usedtime + 1 >= g.context.victual.reqtime) + ? "the last bite of " : ""); + start_eating(g.context.victual.piece, FALSE); return 1; } @@ -2616,7 +2623,7 @@ doeat() /* KMH, conduct */ u.uconduct.food++; - g.context.victual.o_id = 0; + already_partly_eaten = otmp->oeaten ? TRUE : FALSE; g.context.victual.piece = otmp = touchfood(otmp); if (g.context.victual.piece) g.context.victual.o_id = g.context.victual.piece->o_id; @@ -2669,8 +2676,13 @@ doeat() dont_start = TRUE; } consume_oeaten(otmp, 1); /* oeaten >>= 1 */ - } else + } else if (!already_partly_eaten) { fprefx(otmp); + } else { + You("%s %s.", + (g.context.victual.reqtime == 1) ? "eat" : "begin eating", + doname(otmp)); + } } /* re-calc the nutrition */ @@ -2702,7 +2714,7 @@ doeat() g.context.victual.canchoke = (u.uhs == SATIATED); if (!dont_start) - start_eating(otmp); + start_eating(otmp, already_partly_eaten); return 1; } @@ -3159,7 +3171,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. */ diff --git a/src/files.c b/src/files.c index d56e10496..7b2e8af7c 100644 --- a/src/files.c +++ b/src/files.c @@ -2994,9 +2994,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; diff --git a/src/makemon.c b/src/makemon.c index 9d36a3835..4c867fc0d 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 (!g.context.mon_moving) { + if (!g.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 = cg.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 = cg.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 && !g.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); }