From: PatR Date: Wed, 12 Dec 2018 09:54:33 +0000 (-0800) Subject: fix #H7677 - guard placed twice at <0,0> X-Git-Tag: NetHack-3.6.2_Released~144^2~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9952049a752b6e310791740814cf3aea78d2951e;p=nethack fix #H7677 - guard placed twice at <0,0> "Placing monster over another?" warning was triggered for vault guard by an earlier change which made m_detach() stop removing monsters at <0,*> from level.monsters[][]. So one guard would replace another at <0,0> for however many guards were created, and memory for all but the last one would be lost. This involved a lot of flailing about and the patch includes various things would could have been discarded. One or two extended monster sanity checks are included, plus a couple of debugpline()'s for tracking guard movement. --- diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 9e6b004bb..51e6bfa69 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -293,6 +293,7 @@ changing Sting's description to be "(weapon in hand) (light blue aura)" was fix bit-use collision between WC2_TERM_SIZE and WC2_RESET_STATUS in include/winprocs.h following a recent merge fix foxen pluralization again after underflow remedy reintroduced the problem +fix "placing monster over another?" warning for vault guards 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/mon.c b/src/mon.c index a22db1a1a..74edcccd7 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1543455827 2018/11/29 01:43:47 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.272 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1544608467 2018/12/12 09:54:27 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.273 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -48,8 +48,6 @@ struct monst *mtmp; boolean chk_geno; const char *msg; { - if (DEADMONSTER(mtmp)) - return; if (mtmp->data < &mons[LOW_PM] || mtmp->data >= &mons[NUMMONS]) { impossible("illegal mon data %s; mnum=%d (%s)", fmt_ptr((genericptr_t) mtmp->data), mtmp->mnum, msg); @@ -61,6 +59,13 @@ const char *msg; mtmp->mnum, mndx, msg); mtmp->mnum = mndx; } + if (DEADMONSTER(mtmp)) { + /* bad if not fmons list or if not vault guard */ + if (strcmp(msg, "fmon") || !mtmp->isgd) + impossible("dead monster on %s; %s at <%d,%d>", + msg, mons[mndx].mname, mtmp->mx, mtmp->my); + return; + } if (chk_geno && (mvitals[mndx].mvflags & G_GENOD) != 0) impossible("genocided %s in play (%s)", mons[mndx].mname, msg); } @@ -85,8 +90,9 @@ mon_sanity_check() for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { sanity_check_single_mon(mtmp, TRUE, "fmon"); - if (DEADMONSTER(mtmp)) + if (DEADMONSTER(mtmp) && !mtmp->isgd) continue; + x = mtmp->mx, y = mtmp->my; if (!isok(x, y) && !(mtmp->isgd && x == 0 && y == 0)) { impossible("mon (%s) claims to be at <%d,%d>?", @@ -713,10 +719,17 @@ movemon() break; } nmtmp = mtmp->nmon; - /* one dead monster needs to perform a move after death: - vault guard whose temporary corridor is still on the map */ - if (mtmp->isgd && !mtmp->mx && DEADMONSTER(mtmp)) + /* one dead monster needs to perform a move after death: vault + guard whose temporary corridor is still on the map; live + guards who have led the hero back to civilization get moved + off the map too; gd_move() decides whether the temporary + corridor can be removed and guard discarded (via clearing + mon->isgd flag so that dmonsfree() will get rid of mon) */ + if (mtmp->isgd && !mtmp->mx) { + /* parked at <0,0>; eventually isgd should get set to false */ (void) gd_move(mtmp); + continue; + } if (DEADMONSTER(mtmp)) continue; @@ -1766,7 +1779,7 @@ struct permonst *mptr; /* reflects mtmp->data _prior_ to mtmp's death */ mtmp->mtrapped = 0; mtmp->mhp = 0; /* simplify some tests: force mhp to 0 */ relobj(mtmp, 0, FALSE); - if (onmap) { + if (onmap || mtmp == level.monsters[0][0]) { if (mtmp->wormno) remove_worm(mtmp); else diff --git a/src/steed.c b/src/steed.c index 65e5ebbae..9eaf8f4c0 100644 --- a/src/steed.c +++ b/src/steed.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 steed.c $NHDT-Date: 1543543362 2018/11/30 02:02:42 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.59 $ */ +/* NetHack 3.6 steed.c $NHDT-Date: 1544608468 2018/12/12 09:54:28 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.60 $ */ /* Copyright (c) Kevin Hugo, 1998-1999. */ /* NetHack may be freely redistributed. See license for details. */ @@ -742,6 +742,12 @@ place_monster(mon, x, y) struct monst *mon; int x, y; { + /* normal map bounds are <1..COLNO-1,0..ROWNO-1> but sometimes + vault guards (either living or dead) are parked at <0,0> */ + if (!isok(x, y) && (x != 0 || y != 0 || !mon->isgd)) { + impossible("trying to place monster at <%d,%d>", x, y); + x = y = 0; + } if (mon == u.usteed /* special case is for convoluted vault guard handling */ || (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) { diff --git a/src/vault.c b/src/vault.c index 7e47577e2..0e811d117 100644 --- a/src/vault.c +++ b/src/vault.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 vault.c $NHDT-Date: 1542765368 2018/11/21 01:56:08 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.55 $ */ +/* NetHack 3.6 vault.c $NHDT-Date: 1544608469 2018/12/12 09:54:29 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.57 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -10,6 +10,7 @@ STATIC_DCL struct monst *NDECL(findgd); STATIC_DCL boolean FDECL(clear_fcorr, (struct monst *, BOOLEAN_P)); STATIC_DCL void FDECL(blackout, (int, int)); STATIC_DCL void FDECL(restfakecorr, (struct monst *)); +STATIC_DCL void FDECL(parkguard, (struct monst *)); STATIC_DCL boolean FDECL(in_fcorridor, (struct monst *, int, int)); STATIC_DCL void FDECL(move_gold, (struct obj *, int)); STATIC_DCL void FDECL(wallify_vault, (struct monst *)); @@ -22,8 +23,8 @@ struct monst *mtmp; if (!mtmp->mextra) mtmp->mextra = newmextra(); if (!EGD(mtmp)) { - EGD(mtmp) = (struct egd *) alloc(sizeof(struct egd)); - (void) memset((genericptr_t) EGD(mtmp), 0, sizeof(struct egd)); + EGD(mtmp) = (struct egd *) alloc(sizeof (struct egd)); + (void) memset((genericptr_t) EGD(mtmp), 0, sizeof (struct egd)); } } @@ -38,6 +39,9 @@ struct monst *mtmp; mtmp->isgd = 0; } +/* try to remove the temporary corridor (from vault to rest of map) being + maintained by guard 'grd'; if guard is still in it, removal will fail, + to be tried again later */ STATIC_OVL boolean clear_fcorr(grd, forceshow) struct monst *grd; @@ -53,10 +57,14 @@ boolean forceshow; if (!on_level(&egrd->gdlevel, &u.uz)) return TRUE; + /* note: guard remains on 'fmons' list (alive or dead, at off-map + coordinate <0,0>), until temporary corridor from vault back to + civilization has been removed */ while ((fcbeg = egrd->fcbeg) < egrd->fcend) { fcx = egrd->fakecorr[fcbeg].fx; fcy = egrd->fakecorr[fcbeg].fy; - if ((DEADMONSTER(grd) || !in_fcorridor(grd, u.ux, u.uy)) && egrd->gddone) + if ((DEADMONSTER(grd) || !in_fcorridor(grd, u.ux, u.uy)) + && egrd->gddone) forceshow = TRUE; if ((u.ux == fcx && u.uy == fcy && !DEADMONSTER(grd)) || (!forceshow && couldsee(fcx, fcy)) @@ -137,6 +145,26 @@ struct monst *grd; } } +/* move guard--dead to alive--to <0,0> until temporary corridor is removed */ +STATIC_OVL void +parkguard(grd) +struct monst *grd; +{ + /* either guard is dead or will now be treated as if so; + monster traversal loops should skip it */ + if (grd == context.polearm.hitmon) + context.polearm.hitmon = 0; + if (grd->mx) { + remove_monster(grd->mx, grd->my); + newsym(grd->mx, grd->my); + place_monster(grd, 0, 0); + /* [grd->mx,my just got set to 0,0 by place_monster(), so this + just sets EGD(grd)->ogx,ogy to 0,0 too; is that what we want?] */ + EGD(grd)->ogx = grd->mx; + EGD(grd)->ogy = grd->my; + } +} + /* called in mon.c */ boolean grddead(grd) @@ -147,16 +175,8 @@ struct monst *grd; if (!dispose) { /* destroy guard's gold; drop any other inventory */ relobj(grd, 0, FALSE); - /* guard is dead; monster traversal loops should skip it */ grd->mhp = 0; - if (grd == context.polearm.hitmon) - context.polearm.hitmon = 0; - /* see comment by newpos in gd_move() */ - remove_monster(grd->mx, grd->my); - newsym(grd->mx, grd->my); - place_monster(grd, 0, 0); - EGD(grd)->ogx = grd->mx; - EGD(grd)->ogy = grd->my; + parkguard(grd); dispose = clear_fcorr(grd, TRUE); } if (dispose) @@ -226,13 +246,12 @@ invault() u.uinvault = 0; return; } - vaultroom -= ROOMOFFSET; guard = findgd(); if (++u.uinvault % VAULT_GUARD_TIME == 0 && !guard) { /* if time ok and no guard now. */ - char buf[BUFSZ] = DUMMY; + char buf[BUFSZ]; register int x, y, dd, gx, gy; int lx = 0, ly = 0; long umoney; @@ -387,6 +406,7 @@ invault() nomul(0); unmul((char *) 0); } + buf[0] = '\0'; trycount = 5; do { getlin(Deaf ? "You are required to supply your name. -" @@ -401,7 +421,7 @@ invault() } if (!strcmpi(buf, "Croesus") || !strcmpi(buf, "Kroisos") - || !strcmpi(buf, "Creosote")) { + || !strcmpi(buf, "Creosote")) { /* Discworld */ if (!mvitals[PM_CROESUS].died) { if (Deaf) { if (!Blind) @@ -468,9 +488,9 @@ invault() EGD(guard)->fcbeg = 0; EGD(guard)->fakecorr[0].fx = x; EGD(guard)->fakecorr[0].fy = y; - if (IS_WALL(levl[x][y].typ)) + if (IS_WALL(levl[x][y].typ)) { EGD(guard)->fakecorr[0].ftyp = levl[x][y].typ; - else { /* the initial guard location is a dug door */ + } else { /* the initial guard location is a dug door */ int vlt = EGD(guard)->vroom; xchar lowx = rooms[vlt].lx, hix = rooms[vlt].hx; xchar lowy = rooms[vlt].ly, hiy = rooms[vlt].hy; @@ -603,27 +623,29 @@ register struct monst *grd; int x, y, nx, ny, m, n; int dx, dy, gx, gy, fci; uchar typ; + struct rm *crm; struct fakecorridor *fcp; register struct egd *egrd = EGD(grd); - struct rm *crm; - boolean goldincorridor = FALSE, - u_in_vault = vault_occupied(u.urooms) ? TRUE : FALSE, - grd_in_vault = *in_rooms(grd->mx, grd->my, VAULT) ? TRUE : FALSE; - boolean disappear_msg_seen = FALSE, semi_dead = (DEADMONSTER(grd)); - long umoney = money_cnt(invent); - register boolean u_carry_gold = ((umoney + hidden_gold()) > 0L); - boolean see_guard, newspot = FALSE; + long umoney = 0L; + boolean goldincorridor = FALSE, u_in_vault = FALSE, grd_in_vault = FALSE, + disappear_msg_seen = FALSE, semi_dead = DEADMONSTER(grd), + u_carry_gold = FALSE, newspot = FALSE, see_guard; if (!on_level(&(egrd->gdlevel), &u.uz)) return -1; nx = ny = m = n = 0; + if (semi_dead || !grd->mx || egrd->gddone) { + egrd->gddone = 1; + goto cleanup; + } + debugpline1("gd_move: %s guard", grd->mpeaceful ? "peaceful" : "hostile"); + + u_in_vault = vault_occupied(u.urooms) ? TRUE : FALSE; + grd_in_vault = *in_rooms(grd->mx, grd->my, VAULT) ? TRUE : FALSE; if (!u_in_vault && !grd_in_vault) wallify_vault(grd); + if (!grd->mpeaceful) { - if (semi_dead) { - egrd->gddone = 1; - goto newpos; - } if (!u_in_vault && (grd_in_vault || (in_fcorridor(grd, grd->mx, grd->my) && !in_fcorridor(grd, u.ux, u.uy)))) { @@ -647,6 +669,9 @@ register struct monst *grd; grd->mpeaceful = 0; return -1; } + + umoney = money_cnt(invent); + u_carry_gold = umoney > 0L || hidden_gold() > 0L; if (egrd->fcend == 1) { if (u_in_vault && (u_carry_gold || um_dist(grd->mx, grd->my, 1))) { if (egrd->warncnt == 3 && !Deaf) @@ -696,7 +721,6 @@ register struct monst *grd; } else { if (!Deaf) verbalize("Well, begone."); - wallify_vault(grd); egrd->gddone = 1; goto cleanup; } @@ -881,7 +905,7 @@ proceed: fcp->fy = ny; fcp->ftyp = typ; newpos: - gd_mv_monaway(grd, nx,ny); + gd_mv_monaway(grd, nx, ny); if (egrd->gddone) { /* The following is a kludge. We need to keep */ /* the guard around in order to be able to make */ @@ -893,17 +917,14 @@ newpos: /* At the end of the process, the guard is killed */ /* in restfakecorr(). */ cleanup: - x = grd->mx; - y = grd->my; - + x = grd->mx, y = grd->my; see_guard = canspotmon(grd); + parkguard(grd); /* move to <0,0> */ wallify_vault(grd); - remove_monster(grd->mx, grd->my); - newsym(grd->mx, grd->my); - place_monster(grd, 0, 0); - egrd->ogx = grd->mx; - egrd->ogy = grd->my; restfakecorr(grd); + debugpline2("gd_move: %scleanup%s", + grd->isgd ? "" : "final ", + grd->isgd ? " attempt" : ""); if (!semi_dead && (in_fcorridor(grd, u.ux, u.uy) || cansee(x, y))) { if (!disappear_msg_seen && see_guard) pline("Suddenly, %s disappears.", noit_mon_nam(grd));