diff --git a/doc/fixes5-0-1.txt b/doc/fixes5-0-1.txt index cbcc5c14f..5b7180122 100644 --- a/doc/fixes5-0-1.txt +++ b/doc/fixes5-0-1.txt @@ -46,6 +46,10 @@ replace the hashtable used for parsing glyphnames with sorted indices and of pr #1548 by ingpaschke) for SELECTSAVE, allow utd flags for uptodate() to be passed down through the small set of callers that eventually call uptodate() +avoid calling costly_spot(), costly_adjacent(), find_byowner(), which + all rely on shop_keeper() under the hood, in the midst of the level + loading process before all the required data structures have + been finalized Platform- and/or Interface-Specific Fixes diff --git a/include/decl.h b/include/decl.h index 2eb47fcbd..c6dbe7072 100644 --- a/include/decl.h +++ b/include/decl.h @@ -1288,6 +1288,11 @@ extern struct instance_globals_saved_w svw; extern struct instance_globals_saved_x svx; extern struct instance_globals_saved_y svy; extern struct sinfo program_state; /* flags describing game's current state */ +/* flags describing current level's loading/making/readiness status; + * restlevelstate() already associated term 'levelstate' for a different + * purpose, so attempt to avoid confusion + */ +extern struct levelstatus level_status; struct const_globals { const struct obj zeroobj; /* used to zero out a struct obj */ diff --git a/include/extern.h b/include/extern.h index 6dd14db52..5e8b9fa26 100644 --- a/include/extern.h +++ b/include/extern.h @@ -520,6 +520,7 @@ extern void destroy_drawbridge(coordxy, coordxy); /* ### decl.c ### */ extern void program_state_init(void); +extern void level_status_init(void); extern void decl_globals_init(void); extern void sa_victual(volatile struct victual_info *); diff --git a/include/hack.h b/include/hack.h index acf93509b..c98c8ab8a 100644 --- a/include/hack.h +++ b/include/hack.h @@ -821,6 +821,14 @@ struct sinfo { #endif }; +/* structure for current 'level_status'; not saved and restored */ +struct levelstatus { + int making; /* makelevel has begun */ + int loading; /* level loading has begun */ + int shkready; /* shops ready */ + int ready; /* level is ready */ +}; + /* value of program_state.input_state, significant during readchar(); get_count() expects digits then a command so sets it to commandInp */ enum InputState { diff --git a/src/decl.c b/src/decl.c index 587c83e90..8b04a05ac 100644 --- a/src/decl.c +++ b/src/decl.c @@ -1002,6 +1002,7 @@ static const struct instance_globals_saved_y init_svy = { }; static const struct sinfo init_program_state = { 0 }; +static const struct levelstatus init_level_status = { 0 }; #if 0 struct instance_globals g; @@ -1054,6 +1055,7 @@ struct instance_globals_saved_w svw; struct instance_globals_saved_x svx; struct instance_globals_saved_y svy; struct sinfo program_state; +struct levelstatus level_status; const struct const_globals cg = { DUMMY, /* zeroobj */ @@ -1079,6 +1081,12 @@ program_state_init(void) program_state = init_program_state; } +void +level_status_init(void) +{ + level_status = init_level_status; +} + void decl_globals_init(void) { diff --git a/src/mklev.c b/src/mklev.c index f11c2b1e8..2c48af09a 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -1261,6 +1261,9 @@ makelevel(void) impossible("makelevel() called when dungeon not yet initialized."); init_dungeons(); } + level_status_init(); + level_status.making = 1; + oinit(); /* assign level dependent obj probabilities */ clear_level_structures(); @@ -1416,7 +1419,7 @@ makelevel(void) for (i = 0; i < svn.nroom; ++i) { fill_special_room(&svr.rooms[i]); } - + level_status.shkready = 1; themerooms_post_level_generate(); if (gl.luacore && nhcb_counts[NHCB_LVL_ENTER]) { @@ -1425,6 +1428,7 @@ makelevel(void) nhl_pcall_handle(gl.luacore, 1, 0, "makelevel", NHLpa_panic); lua_settop(gl.luacore, 0); } + level_status.making = 0, level_status.ready = 1; } /* return TRUE if water location at (x,y) should have kelp. */ diff --git a/src/mkobj.c b/src/mkobj.c index 822badf51..735c88d34 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2354,9 +2354,7 @@ place_object(struct obj *otmp, coordxy x, coordxy y) otmp->where = OBJ_FLOOR; /* if placed outside of shop, no_charge is no longer applicable */ - if (program_state.beyond_savefile_load - && otmp->no_charge - && !costly_spot(x, y) + if (level_status.shkready && otmp->no_charge && !costly_spot(x, y) && !costly_adjacent(find_objowner(otmp, x, y), x, y)) otmp->no_charge = 0; diff --git a/src/restore.c b/src/restore.c index 318f90e53..50c3a0137 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1068,6 +1068,8 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) #endif program_state.in_getlev = TRUE; + level_status_init(); + level_status.loading = 1; #ifndef SFCTOOL if (ghostly) @@ -1170,9 +1172,11 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) dealloc_trap(trap); fobj = restobjchn(nhfp, FALSE); -#ifndef SFCTOOL - find_lev_obj(); -#endif /* !SFCTOOL */ + /* more work needs to be done on fobj in find_lev_obj() further down, + * but that needs to happen after set_residency() so that shop_keeper() + * will return correct results during the processing. + */ + /* restobjchn()'s `frozen' argument probably ought to be a callback routine so that we can check for objects being buried under ice */ svl.level.buriedobjlist = restobjchn(nhfp, FALSE); @@ -1202,7 +1206,6 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) if (hides_under(mtmp->data) && mtmp->mundetected) (void) hideunder(mtmp); } - /* regenerate monsters while on another level */ if (!u.uz.dlevel || program_state.restoring == REST_LEVELS) continue; @@ -1225,6 +1228,11 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) if (ghostly || (elapsed > 0L && elapsed > (long) rnd(10))) hide_monst(mtmp); } + level_status.shkready = 1; + /* post-5.0.0: this is now postponed until here so that it takes place + after set_residency() has been called */ + find_lev_obj(); + #endif /* !SFCTOOL */ restdamage(nhfp); @@ -1308,12 +1316,13 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) if (ghostly) clear_id_mapping(); +#endif + level_status.loading = 0, level_status.ready = 1; program_state.in_getlev = FALSE; -#else +#ifdef SFCTOOL nhUse(pid); nhUse(lev); #endif /* !SFCTOOL */ - program_state.in_getlev = FALSE; } void diff --git a/src/shk.c b/src/shk.c index cb86dfaa5..099215697 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1075,6 +1075,17 @@ shop_keeper(char rmno) correct the underlying svr.rooms[].resident issue but... */ return (struct monst *) 0; } + } else { + if (!level_status.shkready) { + int hmm UNUSED = 1; +#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED \ + && NH_DEVEL_STATUS != NH_STATUS_POSTRELEASE) + impossible("untrustworthy null shkp; level_status.shkready" + " is FALSE (%d, %d, %d, &d)", + level_status.making, level_status.loading, + level_status.shkready, level_status.ready); +#endif + } } return shkp; }