due to details of how they use overlaid fields in the rm structure
for force-fight against edge of level, report "you harmlessly attack unknown
obstacle" rather than "you have moved as far <direction> as possible"
+using wizard mode ^V in endgame to return to previously visited Plane of Water
+ now gets the same air bubbles back instead of a replacement set;
+ likewise for clouds on Plane of Air
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
inadvertently prevented them from being used up when Riders revived
force-fight against furniture covered by an object described the attack as
missing an unknown obstacle
+using #wizmakemap on Plane of Water added a new set of air bubbles each time
+ it was run and eventually replaced just about all the water; likewise
+ with clouds on Plane of Air
curses: 'msg_window' option wasn't functional for curses unless the binary
also included tty support
allow setting msgtype in SOUND line
display detected door traps and chest traps as trapped doors and trapped
chests rather than as fake bear traps
+if built with DEBUG enabled and running in wizard mode, starting play with
+ DEBUGFILES=seethru in environment makes clouds on the Plane of Air,
+ water on Plane of Water, and fumaroles on Plane of Fire be transparent
Platform- and/or Interface-Specific New Features
file called monsters.h, then taking advantage of the C preprocessor to
generate appropriate enum values during compile
onames.h dependency and inclusion is removed and comparable functionality is
- produced by moving the object definitions from objects.c into new header
+ produced by moving object definitions from objects.c into new header
file called objects.h, then taking advantage of the C preprocessor to
generate appropriate enum values during compile
artilist.h is used to generate appropriate artifact enum values by the C
add git submodule support to the Makefiles by specifying git=1 or GIT=1 on the
make command
add TT_NONE==0, renumber other u.utraptype values so that TT_BEARTRAP isn't 0
+for Planes of Water and Air, save the air bubbles and clouds with the level
+ rather than as game state; affects wizard mode ^V and #wizmakemap
unsigned usteed_id; /* need to preserve during save */
struct obj *looseball; /* track uball during save and... */
struct obj *loosechain; /* track uchain since saving might free it */
+ d_level uz_save;
/* shk.c */
/* auto-response flag for/from "sell foo?" 'a' => 'y', 'q' => 'n' */
xchar *viz_rmin; /* min could see indices */
xchar *viz_rmax; /* max could see indices */
boolean vision_full_recalc;
+ int seethru; /* 'bubble' debugging: clouds and water don't block light */
/* weapon.c */
struct obj *propellor;
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
-#define EDITLEVEL 55
+#define EDITLEVEL 56
/*
* Development status possibilities.
0, /* usteed_id */
(struct obj *) 0, /* looseball */
(struct obj *) 0, /* loosechain */
+ { 0, 0 }, /* uz_save */
/* shk.c */
'a', /* sell_response */
NULL, /* viz_rmin */
NULL, /* viz_rmax */
FALSE, /* vision_full_recalc */
+ 0, /* seethru */
/* weapon.c */
UNDEFINED_PTR, /* propellor */
nhfp->mode = cant_go_back ? FREEING : (WRITING | FREEING);
savelev(nhfp, ledger_no(&u.uz));
nhfp->mode = save_mode;
- /* air bubbles and clouds are saved in game-state rather than with the
- level they're used on; in normal play, you can't leave and return
- to any endgame level--bubbles aren't needed once you move to the
- next level so used to be discarded when the next special level was
- loaded; but in wizard mode you can leave and return, and since they
- aren't saved with the level and restored upon return (new ones are
- created instead), we need to discard them to avoid a memory leak;
- so bubbles are now discarded as we leave the level they're used on */
- if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
- NHFILE tmpnhfp;
-
- zero_nhfile(&tmpnhfp);
- tmpnhfp.fd = -1;
- tmpnhfp.mode = FREEING;
- save_waterlevel(&tmpnhfp);
- }
close_nhfile(nhfp);
if (cant_go_back) {
/* discard unreachable levels; keep #0 */
reseed_random(rn2_on_display_rng);
minit(); /* ZEROCOMP */
getlev(nhfp, g.hackpid, new_ledger);
- /* when in wizard mode, it is possible to leave from and return to
- any level in the endgame; above, we discarded bubble/cloud info
- when leaving Plane of Water or Air so recreate some now */
- if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
- NHFILE tmpnhfp;
-
- zero_nhfile(&tmpnhfp);
- tmpnhfp.fd = -1;
- restore_waterlevel(&tmpnhfp);
- }
close_nhfile(nhfp);
oinit(); /* reassign level dependent obj probabilities */
}
{
struct bubble *b;
- if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
+ if (!g.bbubbles)
return;
if (perform_bwrite(nhfp)) {
struct bubble *b = (struct bubble *) 0, *btmp;
int i, n = 0;
- if (!Is_waterlevel(&u.uz) && !Is_airlevel(&u.uz))
- return;
-
- if (nhfp->fd == -1) { /* special handling for restore in goto_level() */
- if (!wizard)
- impossible("restore_waterlevel: returning to %s?",
- Is_waterlevel(&u.uz) ? "Water" : "Air");
- setup_waterlevel();
- return;
- }
-
set_wportal();
if (nhfp->structlevel) {
mread(nhfp->fd,(genericptr_t)&n,sizeof(int));
mv_bubble(b, 0, 0, TRUE);
}
g.ebubbles = b;
- b->next = (struct bubble *) 0;
+ if (b) {
+ b->next = (struct bubble *) 0;
+ } else {
+ /* avoid "saving and reloading may fix this" */
+ g.program_state.something_worth_saving = 0;
+ /* during restore, information about what level this is might not
+ be available so we're wishy-washy about what we describe */
+ impossible("No %s to restore?",
+ (Is_waterlevel(&u.uz) || Is_waterlevel(&g.uz_save))
+ ? "air bubbles"
+ : (Is_airlevel(&u.uz) || Is_airlevel(&g.uz_save))
+ ? "clouds"
+ : "air bubbles or clouds");
+ g.program_state.something_worth_saving = 1;
+ }
}
static void
static boolean restgamestate(NHFILE *, unsigned int *, unsigned int *);
static void restlevelstate(unsigned int, unsigned int);
static int restlevelfile(xchar);
+static void rest_bubbles(NHFILE *);
static void restore_gamelog(NHFILE *);
static void restore_msghistory(NHFILE *);
static void reset_oattached_mids(boolean);
g.ffruit = loadfruitchn(nhfp);
restnames(nhfp);
- restore_waterlevel(nhfp);
restore_msghistory(nhfp);
restore_gamelog(nhfp);
/* must come after all mons & objs are restored */
}
restdamage(nhfp);
rest_regions(nhfp);
+ rest_bubbles(nhfp); /* for water and air; empty marker on other levels */
if (ghostly) {
stairway *stway = g.stairs;
return;
}
+/* restore Plane of Water's air bubbles and Plane of Air's clouds */
+static void
+rest_bubbles(NHFILE *nhfp)
+{
+ xchar bbubbly;
+
+ /* whether or not the Plane of Water's air bubbles or Plane of Air's
+ clouds are present is recorded during save so that we don't have to
+ know what level is being restored */
+ bbubbly = 0;
+ if (nhfp->structlevel)
+ mread(nhfp->fd, (genericptr_t) &bbubbly, sizeof bbubbly);
+
+ if (bbubbly)
+ restore_waterlevel(nhfp);
+}
+
static void
restore_gamelog(NHFILE* nhfp)
{
static void savelevchn(NHFILE *);
static void savelevl(NHFILE *,boolean);
static void savedamage(NHFILE *);
+static void save_bubbles(NHFILE *, xchar);
static void save_stairs(NHFILE *);
static void saveobj(NHFILE *,struct obj *);
static void saveobjchn(NHFILE *,struct obj **);
static void savetrapchn(NHFILE *,struct trap *);
static void save_gamelog(NHFILE *);
static void savegamestate(NHFILE *);
+static void savelev_core(NHFILE *, xchar);
static void save_msghistory(NHFILE *);
#ifdef ZEROCOMP
{
const char *fq_save;
xchar ltmp;
- d_level uz_save;
char whynot[BUFSZ];
NHFILE *nhfp, *onhfp;
int res = 0;
* parts of the restore code from completely initializing all
* in-core data structures, since all we're doing is copying.
* This also avoids at least one nasty core dump.
+ * [g.uz_save is used by save_bubbles() as well as to restore u.uz]
*/
- uz_save = u.uz;
+ g.uz_save = u.uz;
u.uz.dnum = u.uz.dlevel = 0;
/* these pointers are no longer valid, and at least u.usteed
* may mislead place_monster() on other levels
u.usteed = (struct monst *) 0;
for (ltmp = (xchar) 1; ltmp <= maxledgerno(); ltmp++) {
- if (ltmp == ledger_no(&uz_save))
+ if (ltmp == ledger_no(&g.uz_save))
continue;
if (!(g.level_info[ltmp].flags & LFILE_EXISTS))
continue;
}
close_nhfile(nhfp);
- u.uz = uz_save;
+ u.uz = g.uz_save;
+ g.uz_save.dnum = g.uz_save.dlevel = 0;
/* get rid of current level --jgm */
delete_levelfile(ledger_no(&u.uz));
}
savefruitchn(nhfp);
savenames(nhfp);
- save_waterlevel(nhfp);
save_msghistory(nhfp);
save_gamelog(nhfp);
if (nhfp->structlevel)
void
savelev(NHFILE *nhfp, xchar lev)
+{
+ boolean set_uz_save = (g.uz_save.dnum == 0 && g.uz_save.dlevel == 0);
+
+ /* caller might have already set up g.uz_save and zeroed u.uz;
+ if not, we need to set it for save_bubbles() */
+ if (set_uz_save) {
+ if (u.uz.dnum == 0 && u.uz.dlevel == 0) {
+ g.program_state.something_worth_saving = 0;
+ panic("savelev: where are we?");
+ }
+ g.uz_save = u.uz;
+ }
+
+ savelev_core(nhfp, lev);
+
+ if (set_uz_save)
+ g.uz_save.dnum = g.uz_save.dlevel = 0; /* unset */
+}
+
+static void
+savelev_core(NHFILE *nhfp, xchar lev)
{
#ifdef TOS
short tlev;
save_engravings(nhfp);
savedamage(nhfp); /* pending shop wall and/or floor repair */
save_regions(nhfp);
+ save_bubbles(nhfp, lev); /* for water and air */
+
if (nhfp->mode != FREEING) {
if (nhfp->structlevel)
bflush(nhfp->fd);
return;
}
+/* save Plane of Water's air bubbles and Plane of Air's clouds */
+static void
+save_bubbles(NHFILE *nhfp, xchar lev)
+{
+ xchar bbubbly;
+
+ /* air bubbles and clouds used to be saved as part of game state
+ because restoring them needs dungeon data that isn't available
+ during the first pass of their levels; now that they are part of
+ the current level instead, we write a zero or non-zero marker
+ so that restore can determine whether they are present even when
+ u.uz and ledger_no() aren't available to it yet */
+ bbubbly = 0;
+ if (lev == ledger_no(&water_level) || lev == ledger_no(&air_level))
+ bbubbly = lev; /* non-zero */
+ if (nhfp->structlevel)
+ bwrite(nhfp->fd, (genericptr_t) &bbubbly, sizeof bbubbly);
+
+ if (bbubbly)
+ save_waterlevel(nhfp); /* save air bubbles or clouds */
+}
+
/* used when saving a level and also when saving dungeon overview data */
void
savecemetery(NHFILE* nhfp, struct cemetery** cemeteryaddr)
restoreinfo.mread_flags = -1;
return;
} else {
- pline("Read %d instead of %u bytes.", rlen, len);
+ pline("Read %d instead of %u bytes.", (int) rlen, len);
+ display_nhwindow(WIN_MESSAGE, TRUE); /* flush before error() */
if (g.program_state.restoring) {
(void) nhclose(fd);
(void) delete_savefile();
struct monst *mon;
int i;
+#ifdef DEBUG
+ /* set DEBUGFILES=seethru in environment to see through bubbles */
+ if (g.seethru == 0) { /* init once */
+ g.seethru = (wizard && explicitdebug("seethru")) ? 1 : -1;
+ }
+#endif
+
/* Features that block . . */
if (IS_ROCK(lev->typ) || lev->typ == TREE
|| (IS_DOOR(lev->typ)
&& (lev->doormask & (D_CLOSED | D_LOCKED | D_TRAPPED))))
return 1;
+#ifdef DEBUG
+ if (g.seethru != 1) {
+#endif
if (lev->typ == CLOUD || IS_WATERWALL(lev->typ)
- || (lev->typ == MOAT && Underwater))
+ || (Underwater && is_moat(x, y)))
return 1;
+#ifdef DEBUG
+ } /* g.seethru */
+#endif
/* Boulders block light. */
for (obj = g.level.objects[x][y]; obj; obj = obj->nexthere)
&& is_lightblocker_mappear(mon))
return 1;
+#ifdef DEBUG
+ if (g.seethru != 1) {
+#endif
/* Clouds (poisonous or not) block light. */
for (i = 0; i < g.n_regions; i++) {
/* Ignore regions with ttl == 0 - expire_gas_cloud must unblock its
return 1;
}
}
+#ifdef DEBUG
+ } /* g.seethru */
+#endif
return 0;
}
void
block_point(int x, int y)
{
+#ifdef DEBUG
+ /* set DEBUGFILES=seethru in environment to see through clouds & water */
+ if (g.seethru == 0) { /* init once */
+ g.seethru = (wizard && explicitdebug("seethru")) ? 1 : -1;
+ }
+ if (g.seethru == 1) {
+ if (!does_block(x, y, &levl[x][y]))
+ return;
+ }
+#endif
+
fill_point(y, x);
/* recalc light sources here? */