]> granicus.if.org Git - nethack/commitdiff
fix #H7677 - guard placed twice at <0,0>
authorPatR <rankin@nethack.org>
Wed, 12 Dec 2018 09:54:33 +0000 (01:54 -0800)
committerPatR <rankin@nethack.org>
Wed, 12 Dec 2018 09:54:33 +0000 (01:54 -0800)
"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.

doc/fixes36.2
src/mon.c
src/steed.c
src/vault.c

index 9e6b004bb956fd903cdc0f379b804b8812fcb4aa..51e6bfa69cefb7c29a79412d6102f88a374b2d44 100644 (file)
@@ -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
index a22db1a1a878902e7009e18283cfeaf8eaa84185..74edcccd743d8bea3360bb0942c7404d0af94eef 100644 (file)
--- 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
index 65e5ebbae8f4e8d53bcbe11bcd195c2f1f2a9990..9eaf8f4c0c3b91401c3bd29bffecaa4ddb7a65a8 100644 (file)
@@ -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))) {
index 7e47577e27ff791cd3c749ff2a1c64c23aeffc6a..0e811d1171359411ba557e3bda1decc1a2eb4423 100644 (file)
@@ -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));