]> granicus.if.org Git - nethack/commitdiff
fix B14002 - Stinking clouds in bonefiles
authornethack.rankin <nethack.rankin>
Wed, 16 Oct 2002 22:20:30 +0000 (22:20 +0000)
committernethack.rankin <nethack.rankin>
Wed, 16 Oct 2002 22:20:30 +0000 (22:20 +0000)
     More region fixes.  The region restore code wasn't updating monster
ID numbers for bones data, so monsters already inside stinking clouds
might or might not be affected by them depending on arbitrary mon->mid
assignments across the old and new games.

     This also flags stinking clouds in a way that lets the game keep
track of whether they were created by the player, and clears that state
for bones data so that the current player doesn't get credit or blame
for clouds left by the former one.  File compatibility with 3.4.0 is
retained; if/when that eventually changes, this code can be simplified.

     Testing with current development code has been limited but seems
to be working correctly; testing with actual 3.4.0 files has not been
attempted.

include/region.h
src/region.c

index 96ebd99e93c130cce290d0cc1070f7f160e0fef1..e328956b441a8a6a4364b8fc95f425fdb1126648 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)region.h   3.4     1996/06/17      */
+/*     SCCS Id: @(#)region.h   3.4     2002/10/15      */
 /* Copyright (c) 1996 by Jean-Christophe Collet                          */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -9,6 +9,27 @@
 
 typedef boolean FDECL((*callback_proc), (genericptr_t, genericptr_t));
 
+/*
+ * Overload the old player_inside field with two values, coded in such
+ * a way as to retain compatibility with 3.4.0 save and bones files;
+ * this relies on the fact that nethack's `boolean' is really stored
+ * in a `char' (or bigger type) rather than in a single bit.
+ *
+ * 3.4.1 save and bones files will be correct.
+ * 3.4.0 save files restored under 3.4.1 will be correct.
+ * 3.4.0 bones files used with 3.4.1 will continue to have the minor
+ *      3.4.0 bug of falsely claiming that the current game's hero is
+ *      responsible for the dead former hero's stinking clouds.
+ */
+#define REG_HERO_INSIDE        1
+#define REG_NOT_HEROS  2
+#define hero_inside(r) ((unsigned)(r)->player_flags & REG_HERO_INSIDE)
+#define heros_fault(r) (!((unsigned)(r)->player_flags & REG_NOT_HEROS))
+#define set_hero_inside(r)     ((r)->player_flags |= REG_HERO_INSIDE)
+#define clear_hero_inside(r)   ((r)->player_flags &= ~REG_HERO_INSIDE)
+#define set_heros_fault(r)     ((r)->player_flags &= ~REG_NOT_HEROS)
+#define clear_heros_fault(r)   ((r)->player_flags |= REG_NOT_HEROS)
+
 typedef struct {
   NhRect bounding_box;         /* Bounding box of the region */
   NhRect *rects;               /* Rectangles composing the region */
@@ -28,7 +49,7 @@ typedef struct {
   short leave_f;               /* Function to call when the player leaves */
   short inside_f;              /* Function to call every turn if player's
                                   inside */
-  boolean player_inside;       /* Is the player inside this region */
+  boolean player_flags;        /* (see above) */
   unsigned int* monsters;      /* Monsters currently inside this region */
   short n_monst;               /* Number of monsters inside this region */
   short max_monst;             /* Maximum number of monsters that can be
index f1a95e1a173a3a22ed30fa2437209c3053d702f2..e4d12aff8e6f5946b51a15b00b19cec717ffc8ae 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)region.c   3.4     1999/12/29      */
+/*     SCCS Id: @(#)region.c   3.4     2002/10/15      */
 /* Copyright (c) 1996 by Jean-Christophe Collet         */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -43,6 +43,7 @@ boolean FDECL(enter_force_field, (genericptr,genericptr));
 NhRegion *FDECL(create_force_field, (XCHAR_P,XCHAR_P,int,int));
 #endif
 
+static void FDECL(reset_region_mids, (NhRegion *));
 
 static callback_proc callbacks[] = {
 #define INSIDE_GAS_CLOUD 0
@@ -124,7 +125,8 @@ int nrect;
     reg->leave_f = NO_CALLBACK;
     reg->can_leave_f = NO_CALLBACK;
     reg->inside_f = NO_CALLBACK;
-    reg->player_inside = FALSE;
+    clear_hero_inside(reg);
+    clear_heros_fault(reg);
     reg->n_monst = 0;
     reg->max_monst = 0;
     reg->monsters = NULL;
@@ -245,7 +247,7 @@ NhRegion *reg;
     ret_reg->can_enter_f = reg->can_enter_f;
     ret_reg->leave_f = reg->leave_f;
     ret_reg->can_leave_f = reg->can_leave_f;
-    ret_reg->player_inside = reg->player_inside;
+    ret_reg->player_flags = reg->player_flags; /* set/clear_hero_inside,&c*/
     ret_reg->n_monst = reg->n_monst;
     if (reg->n_monst > 0) {
        ret_reg->monsters = (unsigned *)
@@ -310,7 +312,10 @@ NhRegion *reg;
                newsym(i, j);
        }
     /* Check for player now... */
-    reg->player_inside = inside_region(reg, u.ux, u.uy);
+    if (inside_region(reg, u.ux, u.uy)) 
+       set_hero_inside(reg);
+    else
+       clear_hero_inside(reg);
 }
 
 /*
@@ -387,7 +392,7 @@ run_regions()
            regions[i]->ttl--;
        /* Check if player is inside region */
        f_indx = regions[i]->inside_f;
-       if (f_indx != NO_CALLBACK && regions[i]->player_inside)
+       if (f_indx != NO_CALLBACK && hero_inside(regions[i]))
            (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0);
        /* Check if any monster is inside region */
        if (f_indx != NO_CALLBACK) {
@@ -420,12 +425,12 @@ xchar
     /* First check if we can do the move */
     for (i = 0; i < n_regions; i++) {
        if (inside_region(regions[i], x, y)
-           && !regions[i]->player_inside && !regions[i]->attach_2_u) {
+           && !hero_inside(regions[i]) && !regions[i]->attach_2_u) {
            if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK)
                if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0))
                    return FALSE;
        } else
-           if (regions[i]->player_inside
+           if (hero_inside(regions[i])
                && !inside_region(regions[i], x, y)
                && !regions[i]->attach_2_u) {
            if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK)
@@ -436,9 +441,9 @@ xchar
 
     /* Callbacks for the regions we do leave */
     for (i = 0; i < n_regions; i++)
-       if (regions[i]->player_inside &&
+       if (hero_inside(regions[i]) &&
                !regions[i]->attach_2_u && !inside_region(regions[i], x, y)) {
-           regions[i]->player_inside = FALSE;
+           clear_hero_inside(regions[i]);
            if (regions[i]->leave_msg != NULL)
                pline(regions[i]->leave_msg);
            if ((f_indx = regions[i]->leave_f) != NO_CALLBACK)
@@ -447,9 +452,9 @@ xchar
 
     /* Callbacks for the regions we do enter */
     for (i = 0; i < n_regions; i++)
-       if (!regions[i]->player_inside &&
+       if (!hero_inside(regions[i]) &&
                !regions[i]->attach_2_u && inside_region(regions[i], x, y)) {
-           regions[i]->player_inside = TRUE;
+           set_hero_inside(regions[i]);
            if (regions[i]->enter_msg != NULL)
                pline(regions[i]->enter_msg);
            if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
@@ -497,7 +502,7 @@ xchar x, y;
 
     /* Callbacks for the regions we do enter */
     for (i = 0; i < n_regions; i++)
-       if (!regions[i]->player_inside &&
+       if (!hero_inside(regions[i]) &&
                !regions[i]->attach_2_u && inside_region(regions[i], x, y)) {
            add_mon_to_reg(regions[i], mon);
            if ((f_indx = regions[i]->enter_f) != NO_CALLBACK)
@@ -515,10 +520,10 @@ update_player_regions()
     register int i;
 
     for (i = 0; i < n_regions; i++)
-       if (!regions[i]->attach_2_u)
-           regions[i]->player_inside = inside_region(regions[i], u.ux, u.uy);
+       if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy))
+           set_hero_inside(regions[i]);
        else
-           regions[i]->player_inside = FALSE;
+           clear_hero_inside(regions[i]);
 }
 
 /*
@@ -641,7 +646,7 @@ int mode;
        bwrite(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof (short));
        bwrite(fd, (genericptr_t) &regions[i]->leave_f, sizeof (short));
        bwrite(fd, (genericptr_t) &regions[i]->inside_f, sizeof (short));
-       bwrite(fd, (genericptr_t) &regions[i]->player_inside, sizeof (boolean));
+       bwrite(fd, (genericptr_t) &regions[i]->player_flags, sizeof (boolean));
        bwrite(fd, (genericptr_t) &regions[i]->n_monst, sizeof (short));
        for (j = 0; j < regions[i]->n_monst; j++)
            bwrite(fd, (genericptr_t) &regions[i]->monsters[j],
@@ -716,8 +721,11 @@ boolean ghostly; /* If a bones file restore */
        mread(fd, (genericptr_t) &regions[i]->can_leave_f, sizeof (short));
        mread(fd, (genericptr_t) &regions[i]->leave_f, sizeof (short));
        mread(fd, (genericptr_t) &regions[i]->inside_f, sizeof (short));
-       mread(fd, (genericptr_t) &regions[i]->player_inside, sizeof (boolean));
-       if (ghostly) regions[i]->player_inside = FALSE; /* old player */
+       mread(fd, (genericptr_t) &regions[i]->player_flags, sizeof (boolean));
+       if (ghostly) {  /* settings pertained to old player */
+           clear_hero_inside(regions[i]);
+           clear_heros_fault(regions[i]);
+       }
        mread(fd, (genericptr_t) &regions[i]->n_monst, sizeof (short));
        if (regions[i]->n_monst > 0)
            regions[i]->monsters =
@@ -732,10 +740,33 @@ boolean ghostly; /* If a bones file restore */
        mread(fd, (genericptr_t) &regions[i]->glyph, sizeof (int));
        mread(fd, (genericptr_t) &regions[i]->arg, sizeof (genericptr_t));
     }
-    /* remove expired regions, do not trigger the expire_f callback (yet!) */
+    /* remove expired regions, do not trigger the expire_f callback (yet!);
+       also update monster lists if this data is coming from a bones file */
     for (i = n_regions - 1; i >= 0; i--)
        if (regions[i]->ttl == 0)
            remove_region(regions[i]);
+       else if (ghostly && regions[i]->n_monst > 0)
+           reset_region_mids(regions[i]);
+}
+
+/* update monster IDs for region being loaded from bones; `ghostly' implied */
+static void
+reset_region_mids(reg)
+NhRegion *reg;
+{
+    int i = 0, n = reg->n_monst;
+    unsigned *mid_list = reg->monsters;
+
+    while (i < n)
+       if (!lookup_id_mapping(mid_list[i], &mid_list[i])) {
+           /* shrink list to remove missing monster; order doesn't matter */
+           mid_list[i] = mid_list[--n];
+       } else {
+           /* move on to next monster */
+           ++i;
+       }
+    reg->n_monst = n;
+    return;
 }
 
 #if 0
@@ -821,6 +852,8 @@ int radius, ttl;
        tmprect.hy--;
     }
     ff->ttl = ttl;
+    if (!in_mklev && !flags.mon_moving)
+       set_heros_fault(ff);            /* assume player has created it */
  /* ff->can_enter_f = enter_force_field; */
  /* ff->can_leave_f = enter_force_field; */
     add_region(ff);
@@ -901,7 +934,10 @@ genericptr_t p2;
                return FALSE;
            mtmp->mhp -= rnd(dam) + 5;
            if (mtmp->mhp <= 0) {
-               killed(mtmp);
+               if (heros_fault(reg))
+                   killed(mtmp);
+               else
+                   monkilled(mtmp, "gas cloud", AD_DRST);
                if (mtmp->mhp <= 0) {   /* not lifesaved */
                    return TRUE;
                }
@@ -935,6 +971,8 @@ int damage;
        tmprect.hy--;
     }
     cloud->ttl = rn1(3,4);
+    if (!in_mklev && !flags.mon_moving)
+       set_heros_fault(cloud);         /* assume player has created it */
     cloud->inside_f = INSIDE_GAS_CLOUD;
     cloud->expire_f = EXPIRE_GAS_CLOUD;
     cloud->arg = (genericptr_t) damage;