-/* 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. */
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 */
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
-/* 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. */
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
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;
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 *)
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);
}
/*
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) {
/* 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)
/* 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)
/* 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)
/* 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)
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]);
}
/*
bwrite(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short));
bwrite(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short));
bwrite(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short));
- bwrite(fd, (genericptr_t) ®ions[i]->player_inside, sizeof (boolean));
+ bwrite(fd, (genericptr_t) ®ions[i]->player_flags, sizeof (boolean));
bwrite(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short));
for (j = 0; j < regions[i]->n_monst; j++)
bwrite(fd, (genericptr_t) ®ions[i]->monsters[j],
mread(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short));
mread(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short));
mread(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short));
- mread(fd, (genericptr_t) ®ions[i]->player_inside, sizeof (boolean));
- if (ghostly) regions[i]->player_inside = FALSE; /* old player */
+ mread(fd, (genericptr_t) ®ions[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) ®ions[i]->n_monst, sizeof (short));
if (regions[i]->n_monst > 0)
regions[i]->monsters =
mread(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int));
mread(fd, (genericptr_t) ®ions[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
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);
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;
}
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;