From 2f6d82dcc69f2e164d6e655c8913e66a802d57c6 Mon Sep 17 00:00:00 2001 From: "nethack.rankin" Date: Wed, 16 Oct 2002 22:20:30 +0000 Subject: [PATCH] fix B14002 - Stinking clouds in bonefiles 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 | 25 ++++++++++++++-- src/region.c | 78 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 81 insertions(+), 22 deletions(-) diff --git a/include/region.h b/include/region.h index 96ebd99e9..e328956b4 100644 --- a/include/region.h +++ b/include/region.h @@ -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 diff --git a/src/region.c b/src/region.c index f1a95e1a1..e4d12aff8 100644 --- a/src/region.c +++ b/src/region.c @@ -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) ®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], @@ -716,8 +721,11 @@ boolean ghostly; /* If a bones file restore */ 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 = @@ -732,10 +740,33 @@ boolean ghostly; /* If a bones file restore */ 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 @@ -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; -- 2.40.0