diff --git a/include/config.h b/include/config.h index 82b45cad9..e57b524fb 100644 --- a/include/config.h +++ b/include/config.h @@ -556,6 +556,15 @@ typedef unsigned char uchar; #endif +/* PLAYAGAIN support for allowing the game shell to stay open after the player + * saves or dies. This requires that the game engine can be re-entered to + * start another game. + * + * This support does not include supporting playing another game when + * a panic has occured due to undetermined state the engine is left in after a + * panic */ +/* #define PLAYAGAIN */ + /* End of Section 4 */ #ifdef TTY_TILES_ESCCODES diff --git a/include/decl.h b/include/decl.h index 4716f158c..2cc7f965c 100644 --- a/include/decl.h +++ b/include/decl.h @@ -192,6 +192,7 @@ E NEARDATA char dogname[]; E NEARDATA char catname[]; E NEARDATA char horsename[]; E char preferred_pet; +E int petname_used; E const char *occtxt; /* defined when occupation != NULL */ E const char *nomovemsg; @@ -443,15 +444,10 @@ struct early_opt { * things initialized during the initialization of the game engine. * It is initialized with icontext_initial_state found in decl.c */ +#define PLAYAGAIN + struct instance_context { int oldcap; /* encumberance - pickup.c */ - int petname_used; /* user preferred pet name has been used - dog.c */ - int jumping_is_magic; /* current jump result of magic - apply.c */ - int polearm_range_min; /* apply.c */ - int polearm_range_max; /* apply.c */ - int spec_dbon_applies; /* coordinate effects from spec_dbon() with - * messages in artifact_hit() - artifact.c */ - int mrank_sz; /* loaded by max_rank_sz - botl.c */ }; E struct instance_context icontext; diff --git a/include/extern.h b/include/extern.h index 89910b0bb..d3a251b83 100644 --- a/include/extern.h +++ b/include/extern.h @@ -251,6 +251,9 @@ E void FDECL(destroy_drawbridge, (int, int)); /* ### decl.c ### */ E void NDECL(decl_init); +#ifdef PLAYAGAIN +E void NDECL(decl_early_init); +#endif /* ### detect.c ### */ diff --git a/src/apply.c b/src/apply.c index b4c0270df..d202acc77 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1600,13 +1600,15 @@ boolean showmsg; return TRUE; } +static int jumping_is_magic; + STATIC_OVL boolean get_valid_jump_position(x,y) int x,y; { return (isok(x, y) && (ACCESSIBLE(levl[x][y].typ) || Passes_walls) - && is_valid_jump_pos(x, y, icontext.jumping_is_magic, FALSE)); + && is_valid_jump_pos(x, y, jumping_is_magic, FALSE)); } void @@ -1720,7 +1722,7 @@ int magic; /* 0=Physical, otherwise skill level */ pline("Where do you want to jump?"); cc.x = u.ux; cc.y = u.uy; - icontext.jumping_is_magic = magic; + jumping_is_magic = magic; getpos_sethilite(display_jump_positions, get_valid_jump_position); if (getpos(&cc, TRUE, "the desired position") < 0) return 0; /* user pressed ESC */ @@ -2912,13 +2914,16 @@ int min_range, max_range; return TRUE; } +static int polearm_range_min = -1; +static int polearm_range_max = -1; + STATIC_OVL boolean get_valid_polearm_position(x, y) int x, y; { return (isok(x, y) && ACCESSIBLE(levl[x][y].typ) - && distu(x, y) >= icontext.polearm_range_min - && distu(x, y) <= icontext.polearm_range_max); + && distu(x, y) >= polearm_range_min + && distu(x, y) <= polearm_range_max); } void @@ -2990,8 +2995,8 @@ struct obj *obj; else max_range = 8; /* (P_SKILL(typ) >= P_EXPERT) */ - icontext.polearm_range_min = min_range; - icontext.polearm_range_max = max_range; + polearm_range_min = min_range; + polearm_range_max = max_range; /* Prompt for a location */ pline(where_to_hit); diff --git a/src/artifact.c b/src/artifact.c index a5d090200..87cbf3b61 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -40,6 +40,9 @@ STATIC_DCL int FDECL(count_surround_traps, (int, int)); of hit points that will fit in a 15 bit integer. */ #define FATAL_DAMAGE_MODIFIER 200 +/* coordinate effects from spec_dbon() with messages in artifact_hit() */ +STATIC_OVL int spec_dbon_applies = 0; + /* flags including which artifacts have already been created */ static boolean artiexist[1 + NROFARTIFACTS + 1]; /* and a discovery list for them (no dummy first entry here) */ @@ -844,15 +847,15 @@ int tmp; if (!weap || (weap->attk.adtyp == AD_PHYS /* check for `NO_ATTK' */ && weap->attk.damn == 0 && weap->attk.damd == 0)) - icontext.spec_dbon_applies = FALSE; + spec_dbon_applies = FALSE; else if (otmp->oartifact == ART_GRIMTOOTH) /* Grimtooth has SPFX settings to warn against elves but we want its damage bonus to apply to all targets, so bypass spec_applies() */ - icontext.spec_dbon_applies = TRUE; + spec_dbon_applies = TRUE; else - icontext.spec_dbon_applies = spec_applies(weap, mon); + spec_dbon_applies = spec_applies(weap, mon); - if (icontext.spec_dbon_applies) + if (spec_dbon_applies) return weap->attk.damd ? rnd((int) weap->attk.damd) : max(tmp, 1); return 0; } @@ -976,14 +979,14 @@ char *hittee; /* target's name: "you" or mon_nam(mdef) */ scare_dieroll /= (1 << (mb->spe / 3)); /* if target successfully resisted the artifact damage bonus, reduce overall likelihood of the assorted special effects */ - if (!icontext.spec_dbon_applies) + if (!spec_dbon_applies) dieroll += 1; /* might stun even when attempting a more severe effect, but in that case it will only happen if the other effect fails; extra damage will apply regardless; 3.4.1: sometimes might just probe even when it hasn't been enchanted */ - do_stun = (max(mb->spe, 0) < rn2(icontext.spec_dbon_applies ? 11 : 7)); + do_stun = (max(mb->spe, 0) < rn2(spec_dbon_applies ? 11 : 7)); /* the special effects also boost physical damage; increments are generally cumulative, but since the stun effect is based on a @@ -1182,12 +1185,12 @@ int dieroll; /* needed for Magicbane and vorpal blades */ if (attacks(AD_FIRE, otmp)) { if (realizes_damage) pline_The("fiery blade %s %s%c", - !icontext.spec_dbon_applies + !spec_dbon_applies ? "hits" : (mdef->data == &mons[PM_WATER_ELEMENTAL]) ? "vaporizes part of" : "burns", - hittee, !icontext.spec_dbon_applies ? '.' : '!'); + hittee, !spec_dbon_applies ? '.' : '!'); if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_FIRE); if (!rn2(4)) @@ -1201,8 +1204,8 @@ int dieroll; /* needed for Magicbane and vorpal blades */ if (attacks(AD_COLD, otmp)) { if (realizes_damage) pline_The("ice-cold blade %s %s%c", - !icontext.spec_dbon_applies ? "hits" : "freezes", hittee, - !icontext.spec_dbon_applies ? '.' : '!'); + !spec_dbon_applies ? "hits" : "freezes", hittee, + !spec_dbon_applies ? '.' : '!'); if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_COLD); return realizes_damage; @@ -1210,9 +1213,9 @@ int dieroll; /* needed for Magicbane and vorpal blades */ if (attacks(AD_ELEC, otmp)) { if (realizes_damage) pline_The("massive hammer hits%s %s%c", - !icontext.spec_dbon_applies ? "" : "! Lightning strikes", - hittee, !icontext.spec_dbon_applies ? '.' : '!'); - if (icontext.spec_dbon_applies) + !spec_dbon_applies ? "" : "! Lightning strikes", + hittee, !spec_dbon_applies ? '.' : '!'); + if (spec_dbon_applies) wake_nearto(mdef->mx, mdef->my, 4 * 4); if (!rn2(5)) (void) destroy_mitem(mdef, RING_CLASS, AD_ELEC); @@ -1223,10 +1226,10 @@ int dieroll; /* needed for Magicbane and vorpal blades */ if (attacks(AD_MAGM, otmp)) { if (realizes_damage) pline_The("imaginary widget hits%s %s%c", - !icontext.spec_dbon_applies + !spec_dbon_applies ? "" : "! A hail of magic missiles strikes", - hittee, !icontext.spec_dbon_applies ? '.' : '!'); + hittee, !spec_dbon_applies ? '.' : '!'); return realizes_damage; } @@ -1235,7 +1238,7 @@ int dieroll; /* needed for Magicbane and vorpal blades */ return Mb_hit(magr, mdef, otmp, dmgptr, dieroll, vis, hittee); } - if (!icontext.spec_dbon_applies) { + if (!spec_dbon_applies) { /* since damage bonus didn't apply, nothing more to do; no further attacks have side-effects on inventory */ return FALSE; diff --git a/src/botl.c b/src/botl.c index 9256051b3..4af749105 100644 --- a/src/botl.c +++ b/src/botl.c @@ -11,6 +11,7 @@ extern const char *hu_stat[]; /* defined in eat.c */ const char *const enc_stat[] = { "", "Burdened", "Stressed", "Strained", "Overtaxed", "Overloaded" }; +STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */ STATIC_DCL const char *NDECL(rank); #ifdef STATUS_HILITES STATIC_DCL void NDECL(bot_via_windowport); @@ -64,7 +65,7 @@ do_statusline1() Strcpy(nb = eos(nb), rank()); Sprintf(nb = eos(nb), " "); - i = icontext.mrank_sz + 15; + i = mrank_sz + 15; j = (int) ((nb + 2) - newbot1); /* strlen(newbot1) but less computation */ if ((i - j) > 0) Sprintf(nb = eos(nb), "%*s", i - j, " "); /* pad with spaces */ @@ -345,7 +346,7 @@ max_rank_sz() if (urole.rank[i].f && (r = strlen(urole.rank[i].f)) > maxr) maxr = r; } - icontext.mrank_sz = maxr; + mrank_sz = maxr; return; } diff --git a/src/decl.c b/src/decl.c index 2a259efaa..edfeb3a60 100644 --- a/src/decl.c +++ b/src/decl.c @@ -207,6 +207,7 @@ NEARDATA char dogname[PL_PSIZ] = DUMMY; NEARDATA char catname[PL_PSIZ] = DUMMY; NEARDATA char horsename[PL_PSIZ] = DUMMY; char preferred_pet; /* '\0', 'c', 'd', 'n' (none) */ +int petname_used = 0; /* monsters that went down/up together with @ */ NEARDATA struct monst *mydogs = (struct monst *) 0; /* monsters that are moving to another dungeon level */ @@ -284,6 +285,7 @@ char *fqn_prefix_names[PREFIX_COUNT] = { }; #endif +#ifdef PLAYAGAIN const struct savefile_info default_sfinfo = { #ifdef NHSTDC 0x00000000UL @@ -306,8 +308,53 @@ const struct savefile_info default_sfinfo = { 0x00000000L, 0x00000000L #endif }; +#endif -NEARDATA struct savefile_info sfcap, sfrestinfo, sfsaveinfo; +NEARDATA struct savefile_info sfcap = { +#ifdef NHSTDC + 0x00000000UL +#else + 0x00000000L +#endif +#if defined(COMPRESS) || defined(ZLIB_COMP) + | SFI1_EXTERNALCOMP +#endif +#if defined(ZEROCOMP) + | SFI1_ZEROCOMP +#endif +#if defined(RLECOMP) + | SFI1_RLECOMP +#endif + , +#ifdef NHSTDC + 0x00000000UL, 0x00000000UL +#else + 0x00000000L, 0x00000000L +#endif +}; + +NEARDATA struct savefile_info sfrestinfo, sfsaveinfo = { +#ifdef NHSTDC + 0x00000000UL +#else + 0x00000000L +#endif +#if defined(COMPRESS) || defined(ZLIB_COMP) + | SFI1_EXTERNALCOMP +#endif +#if defined(ZEROCOMP) + | SFI1_ZEROCOMP +#endif +#if defined(RLECOMP) + | SFI1_RLECOMP +#endif + , +#ifdef NHSTDC + 0x00000000UL, 0x00000000UL +#else + 0x00000000L, 0x00000000L +#endif +}; struct plinemsg_type *plinemsg_types = (struct plinemsg_type *) 0; @@ -325,14 +372,243 @@ decl_init() return; } +#ifdef PLAYAGAIN + +static boolean s_firstStart = TRUE; + +static boolean +decl_is_block_zero(p, n) +unsigned char * p; +int n; +{ + static unsigned char zeroblock[512] = { 0 }; + unsigned char * sentinel = p + n; + while (p < sentinel) { + int c = (n < sizeof(zeroblock) ? n : sizeof(zeroblock)); + if (memcmp(p, zeroblock, c) != 0) return FALSE; + p += c; + } + return TRUE; +} + +static void +decl_zero_block(p, n) +unsigned char * p; +int n; +{ + nhassert(!s_firstStart || decl_is_block_zero(p, n)); + memset(p, 0, n); +} + +#define ZEROARRAY(x) decl_zero_block((void *) &x[0], 0, sizeof(x)) +#define ZEROARRAYN(x,n) decl_zero_block((void *) &x[0], 0, sizeof(x[0])*(n)) +#define ZERO(x) decl_zero_block((void *) &x, 0, sizeof(x)) +#define ZEROPTR(x) { nhassert(x == NULL); x = NULL; } +#define ZEROPTRNOCHECK(x) { x = NULL; } + +/* decl_early_init() is called when we are starting a game. On first + * start, it validates that global state is in the expected state. + * When called on subsequent starts, it initializes global state to + * expected state. + * + * In the case that of global pointers, on subsequent starts it will + * panic if it finds a non-NULL pointer with the assumption that a + * pointer has leaked. */ + +void +decl_early_init() +{ + hackpid = 0; +#if defined(UNIX) || defined(VMS) + locknum = 0; +#endif +#ifdef DEF_PAGER + catmore = 0; +#endif + + ZEROARRAY(bases); + + multi = 0; + multi_reason = NULL; + nroom = 0; + nsubroom = 0; + occtime = 0; + + x_maze_max = (COLNO - 1) & ~1; + y_maze_max = (ROWNO - 1) & ~1; + + otg_temp = 0; + + ZERO(dungeon_topology); + ZERO(quest_status); + + warn_obj_cnt = 0; + ZEROARRAYN(smeq, MAXNROFROOMS + 1); + doorindex = 0; + save_cm = NULL; + + ZERO(killer); + done_money = 0; + nomovemsg = NULL; + ZEROARRAY(plname); + ZEROARRAY(pl_character); + pl_race = '\0'; + + ZEROARRAY(pl_fruit); + ffruit = NULL; + + ZEROARRAY(tune); + + occtxt = NULL; + + yn_number = 0; + +#if defined(MICRO) || defined(WIN32) + ZEROARRAYN(hackdir, PATHLEN); +#ifdef MICRO + ZEROARRAYN(levels, PATHLEN); +#endif /* MICRO */ +#endif /* MICRO || WIN32 */ + +#ifdef MFLOPPY + ZEROARRAYN(permbones, PATHLEN); + ramdisk = FALSE; + saveprompt = TRUE; +#endif + + ZEROARRAY(level_info); + + ZERO(program_state); + + tbx = 0; + tby = 0; + + ZERO(m_shot); + + ZEROARRAYN(dungeons, MAXDUNGEON); + ZEROPTR(sp_levchn); + ZERO(upstair); + ZERO(dnstair); + ZERO(upladder); + ZERO(dnladder); + ZERO(sstairs); + ZERO(updest); + ZERO(dndest); + ZERO(inv_pos); + + defer_see_monsters = FALSE; + in_mklev = FALSE; + stoned = FALSE; + unweapon = FALSE; + mrg_to_wielded = FALSE; + + in_steed_dismounting = FALSE; + + ZERO(bhitpos); + ZEROARRAY(doors); + + ZEROARRAY(rooms); + subrooms = &rooms[MAXNROFROOMS + 1]; + upstairs_room = NULL; + dnstairs_room = NULL; + sstairs_room = NULL; + + ZERO(level); + ZEROPTR(ftrap); + ZERO(youmonst); + ZERO(context); + ZERO(flags); +#ifdef SYSFLAGS + ZERO(sysflags); +#endif + ZERO(iflags); + ZERO(u); + ZERO(ubirthday); + ZERO(urealtime); + + ZEROARRAY(lastseentyp); + + ZEROPTR(invent); + ZEROPTRNOCHECK(uwep); + ZEROPTRNOCHECK(uarm); + ZEROPTRNOCHECK(uswapwep); + ZEROPTRNOCHECK(uquiver); + ZEROPTRNOCHECK(uarmu); + ZEROPTRNOCHECK(uskin); + ZEROPTRNOCHECK(uarmc); + ZEROPTRNOCHECK(uarmh); + ZEROPTRNOCHECK(uarms); + ZEROPTRNOCHECK(uarmg); + ZEROPTRNOCHECK(uarmf); + ZEROPTRNOCHECK(uamul); + ZEROPTRNOCHECK(uright); + ZEROPTRNOCHECK(uleft); + ZEROPTRNOCHECK(ublindf); + ZEROPTRNOCHECK(uchain); + ZEROPTRNOCHECK(uball); + + ZEROPTR(current_wand); + ZEROPTR(thrownobj); + ZEROPTR(kickedobj); + + ZEROARRAYN(spl_book, MAXSPELL + 1); + + moves = 1; + monstermoves = 1; + + wailmsg = 0L; + + ZEROPTR(migrating_objs); + ZEROPTR(billobjs); + + ZERO(zeroobj); + ZERO(zeromonst); + ZERO(zeroany); + + ZEROARRAYN(dogname, PL_PSIZ); + ZEROARRAYN(catname, PL_PSIZ); + ZEROARRAYN(horsename, PL_PSIZ); + ZERO(preferred_pet); + ZERO(petname_used); + ZEROPTR(mydogs); + ZEROPTR(migrating_mons); + + ZEROARRAY(mvitals); + + ZEROPTR(menu_colorings); + + vision_full_recalc = 0; + viz_array = NULL; + + WIN_MESSAGE = WIN_ERR; +#ifndef STATUS_VIA_WINDOWPORT + WIN_STATUS = WIN_ERR; +#endif + WIN_MAP = WIN_ERR; + WIN_INVEN = WIN_ERR; + + ZEROARRAYN(toplines, TBUFSZ); + ZERO(tc_gbl_data); + ZEROARRAYN(fqn_prefix, PREFIX_COUNT); + + sfcap = default_sfinfo; + sfrestinfo = default_sfinfo; + sfsaveinfo = default_sfinfo; + + ZEROPTR(plinemsg_types); + +#ifdef PANICTRACE + ARGV0 = NULL; +#endif + + nhUse_dummy = 0; + + s_firstStart = FALSE; +} +#endif + const struct instance_context icontext_initial_state = { - 0, /* oldcap - last encumberance in pickup.c */ - 0, /* petname_used - dog.c */ - 0, /* jumping_is_magic - apply.c */ - -1, /* polearm_range_min - apply.c */ - -1, /* polearm_range_max - apply.c */ - 0, /* spec_dbon_applies - artifact.c */ - 0, /* mrank_sz - botl.c */ + 0, /* oldcap - last encumberance in pickup.c */ }; struct instance_context icontext; @@ -342,9 +618,7 @@ icontext_init() { icontext = icontext_initial_state; - sfcap = default_sfinfo; - sfrestinfo = default_sfinfo; - sfsaveinfo = default_sfinfo; + decl_early_init(); }; /*decl.c*/ diff --git a/src/dog.c b/src/dog.c index 79e6b781f..7c1b62e8b 100644 --- a/src/dog.c +++ b/src/dog.c @@ -197,7 +197,7 @@ makedog() put_saddle_on_mon(otmp, mtmp); } - if (!icontext.petname_used++ && *petname) + if (!petname_used++ && *petname) mtmp = christen_monst(mtmp, petname); initedog(mtmp); diff --git a/src/restore.c b/src/restore.c index 557cb3219..f26379967 100644 --- a/src/restore.c +++ b/src/restore.c @@ -899,7 +899,7 @@ register int fd; #ifdef MFLOPPY gameDiskPrompt(); #endif - max_rank_sz(); /* to recompute icontext.mrank_sz (botl.c) */ + max_rank_sz(); /* to recompute mrank_sz (botl.c) */ /* take care of iron ball & chain */ for (otmp = fobj; otmp; otmp = otmp->nobj) if (otmp->owornmask)