fountain and sink bookkeeping

This replaces most of commit 0ca2af4d8b
from a couple of days ago with something more robust.  That change
actually introduced redundant code that caused fountain and/or sink
count to be off instead of preventing it.

Revise set_levltyp() to update level.flags.nfountains and
level.flags.nsinks if setting the type to or from fountain or sink.
A bunch of places that were setting levl[x][y].typ directly needed
to be revised to use set_levltyp() instead.  set_levltyp() itself
hadn't been updated to handle LAVAWALL (to force such to be lit).
This commit is contained in:
PatR
2023-06-12 15:07:34 -07:00
parent 299d49aebf
commit bf47cc878e
8 changed files with 100 additions and 85 deletions

View File

@@ -1399,6 +1399,7 @@ extern void add_subroom(struct mkroom *, int, int, int, int, boolean, schar,
extern void free_luathemes(boolean);
extern void makecorridors(void);
extern void add_door(coordxy, coordxy, struct mkroom *);
extern void count_level_features(void);
extern void clear_level_structures(void);
extern void level_finalize_topology(void);
extern void mklev(void);

View File

@@ -96,6 +96,7 @@ enum levl_typ_types {
#define ZAP_POS(typ) ((typ) >= POOL)
#define SPACE_POS(typ) ((typ) > DOOR)
#define IS_POOL(typ) ((typ) >= POOL && (typ) <= DRAWBRIDGE_UP)
#define IS_LAVA(typ) ((typ) == LAVAPOOL || (typ) == LAVAWALL)
#define IS_THRONE(typ) ((typ) == THRONE)
#define IS_FOUNTAIN(typ) ((typ) == FOUNTAIN)
#define IS_SINK(typ) ((typ) == SINK)

View File

@@ -363,27 +363,26 @@ polymorph_sink(void)
return;
sinklooted = levl[u.ux][u.uy].looted != 0;
gl.level.flags.nsinks--;
levl[u.ux][u.uy].doormask = 0; /* levl[][].flags */
/* gl.level.flags.nsinks--; // set_levltyp() will update this */
levl[u.ux][u.uy].flags = 0;
switch (rn2(4)) {
default:
case 0:
sym = S_fountain;
levl[u.ux][u.uy].typ = FOUNTAIN;
set_levltyp(u.ux, u.uy, FOUNTAIN); /* updates level.flags.nfountains */
levl[u.ux][u.uy].blessedftn = 0;
if (sinklooted)
SET_FOUNTAIN_LOOTED(u.ux, u.uy);
gl.level.flags.nfountains++;
break;
case 1:
sym = S_throne;
levl[u.ux][u.uy].typ = THRONE;
set_levltyp(u.ux, u.uy, THRONE);
if (sinklooted)
levl[u.ux][u.uy].looted = T_LOOTED;
break;
case 2:
sym = S_altar;
levl[u.ux][u.uy].typ = ALTAR;
set_levltyp(u.ux, u.uy, ALTAR);
/* 3.6.3: this used to pass 'rn2(A_LAWFUL + 2) - 1' to
Align2amask() but that evaluates its argument more than once */
algn = rn2(3) - 1; /* -1 (A_Cha) or 0 (A_Neu) or +1 (A_Law) */
@@ -392,7 +391,7 @@ polymorph_sink(void)
break;
case 3:
sym = S_room;
levl[u.ux][u.uy].typ = ROOM;
set_levltyp(u.ux, u.uy, ROOM);
make_grave(u.ux, u.uy, (char *) 0);
if (levl[u.ux][u.uy].typ == GRAVE)
sym = S_grave;
@@ -413,29 +412,36 @@ static boolean
teleport_sink(void)
{
coordxy cx, cy;
int cnt = 0;
struct trap *trp;
struct engr *eng;
unsigned alreadylooted;
int trycnt = 0;
do {
cx = rnd(COLNO - 1);
cy = rn2(ROWNO);
trp = t_at(cx, cy);
eng = engr_at(cx, cy);
} while ((levl[cx][cy].typ != ROOM || trp || eng || cansee(cx, cy))
&& cnt++ < 200);
#if 0 /* this isn't incorrect but it is extremely unlikely that spots
* on the level's edge will be ROOM so picking such wastes tries */
cx = rnd(COLNO - 1); /* 1..COLNO-1 */
cy = rn2(ROWNO); /* 0..ROWNO-1 */
#else /* use this instead */
cx = 1 + rnd((COLNO - 1) - 2); /* 2..COLNO-2 */
cy = 1 + rn2(ROWNO - 2); /* 1..ROWNO-2 */
#endif
if (levl[cx][cy].typ == ROOM
&& !t_at(cx, cy) && !engr_at(cx, cy)
&& (!cansee(cx, cy) || distu(cx, cy) > 3 * 3)) {
/* this ends up having set_levltyp() count all sinks and
fountains on the level twice but that is not a problem */
alreadylooted = levl[u.ux][u.uy].looted;
/* remove old sink */
set_levltyp(u.ux, u.uy, ROOM); /* was SINK so updates nsinks */
levl[u.ux][u.uy].looted = 0;
newsym(u.ux, u.uy);
/* create sink at new position */
set_levltyp(cx, cy, SINK); /* now SINK so also updates nsinks */
levl[cx][cy].looted = alreadylooted ? 1 : 0;
newsym(cx, cy);
return TRUE;
}
} while (++trycnt < 200);
if (levl[cx][cy].typ == ROOM && !trp && !eng) {
/* create sink at new position */
levl[cx][cy].typ = SINK;
levl[cx][cy].looted = levl[u.ux][u.uy].looted;
newsym(cx, cy);
/* remove old sink */
levl[u.ux][u.uy].typ = ROOM;
levl[u.ux][u.uy].looted = 0;
newsym(u.ux, u.uy);
return TRUE;
}
return FALSE;
}

View File

@@ -148,7 +148,8 @@ gush(coordxy x, coordxy y, genericptr_t poolcnt)
pline("Water gushes forth from the overflowing fountain!");
/* Put a pool at x, y */
levl[x][y].typ = POOL, levl[x][y].flags = 0;
set_levltyp(x, y, POOL);
levl[x][y].flags = 0;
/* No kelp! */
del_engr_at(x, y);
water_damage_chain(gl.level.objects[x][y], TRUE);
@@ -226,9 +227,9 @@ dryup(coordxy x, coordxy y, boolean isyou)
pline_The("fountain dries up!");
}
/* replace the fountain with ordinary floor */
levl[x][y].typ = ROOM, levl[x][y].flags = 0;
set_levltyp(x, y, ROOM); /* updates level.flags.nfountains */
levl[x][y].flags = 0;
levl[x][y].blessedftn = 0;
gl.level.flags.nfountains--;
/* The location is seen if the hero/monster is invisible
or felt if the hero is blind. */
newsym(x, y);
@@ -430,9 +431,9 @@ dipfountain(register struct obj *obj)
artiname(ART_EXCALIBUR), lady);
}
update_inventory();
levl[u.ux][u.uy].typ = ROOM, levl[u.ux][u.uy].flags = 0;
set_levltyp(u.ux, u.uy, ROOM); /* updates level.flags.nfountains */
levl[u.ux][u.uy].flags = 0;
newsym(u.ux, u.uy);
gl.level.flags.nfountains--;
if (in_town(u.ux, u.uy))
(void) angry_guards(FALSE);
return;
@@ -543,11 +544,11 @@ breaksink(coordxy x, coordxy y)
{
if (cansee(x, y) || u_at(x, y))
pline_The("pipes break! Water spurts out!");
gl.level.flags.nsinks--;
levl[x][y].typ = FOUNTAIN, levl[x][y].looted = 0;
/* updates level.flags.nsinks and level.flags.nfountains */
set_levltyp(x, y, FOUNTAIN);
levl[x][y].looted = 0;
levl[x][y].blessedftn = 0;
SET_FOUNTAIN_LOOTED(x, y);
gl.level.flags.nfountains++;
newsym(x, y);
}

View File

@@ -26,7 +26,7 @@ static void mktrap_victim(struct trap *);
static struct mkroom *find_branch_room(coord *);
static struct mkroom *pos_to_room(coordxy, coordxy);
static boolean cardinal_nextto_room(struct mkroom *, coordxy, coordxy);
static boolean place_niche(struct mkroom *, coordxy *, coordxy *, coordxy *);
static boolean place_niche(struct mkroom *, int *, coordxy *, coordxy *);
static void makeniche(int);
static void make_niches(void);
static int QSORTCALLBACK mkroom_cmp(const genericptr, const genericptr);
@@ -599,7 +599,10 @@ cardinal_nextto_room(struct mkroom *aroom, coordxy x, coordxy y)
}
static boolean
place_niche(register struct mkroom *aroom, coordxy *dy, coordxy *xx, coordxy *yy)
place_niche(
struct mkroom *aroom,
int *dy,
coordxy *xx, coordxy *yy)
{
coord dd;
@@ -639,8 +642,8 @@ makeniche(int trap_type)
{
register struct mkroom *aroom;
struct rm *rm;
int vct = 8;
coordxy dy, xx, yy;
int dy, vct = 8;
coordxy xx, yy;
struct trap *ttmp;
while (vct--) {
@@ -721,6 +724,24 @@ makevtele(void)
makeniche(TELEP_TRAP);
}
/* count the different features (sinks, fountains) in the level */
void
count_level_features(void)
{
coordxy x, y;
gl.level.flags.nfountains = gl.level.flags.nsinks = 0;
for (y = 0; y < ROWNO; y++)
for (x = 0; x < COLNO; x++) {
int typ = levl[x][y].typ;
if (typ == FOUNTAIN)
gl.level.flags.nfountains++;
else if (typ == SINK)
gl.level.flags.nsinks++;
}
}
/* clear out various globals that keep information on the current level.
* some of this is only necessary for some types of levels (maze, normal,
* special) but it's easier to put it all in one place than make sure

View File

@@ -69,24 +69,36 @@ is_solid(coordxy x, coordxy y)
/* set map terrain type, handling lava lit, ice melt timers, etc */
boolean
set_levltyp(coordxy x, coordxy y, schar typ)
set_levltyp(coordxy x, coordxy y, schar newtyp)
{
if (isok(x, y)) {
if ((typ < MAX_TYPE) && CAN_OVERWRITE_TERRAIN(levl[x][y].typ)) {
if (isok(x, y) && newtyp >= STONE && newtyp < MAX_TYPE) {
if (CAN_OVERWRITE_TERRAIN(levl[x][y].typ)) {
schar oldtyp = levl[x][y].typ;
boolean was_ice = (levl[x][y].typ == ICE);
levl[x][y].typ = typ;
levl[x][y].typ = newtyp;
/* TODO?
* if oldtyp used flags or horizontal differently from
* from the way newtyp will use them, clear them.
*/
if (typ == LAVAPOOL)
if (IS_LAVA(newtyp))
levl[x][y].lit = 1;
if (was_ice && typ != ICE)
if (was_ice && newtyp != ICE)
spot_stop_timers(x, y, MELT_ICE_AWAY);
if ((IS_FOUNTAIN(oldtyp) != IS_FOUNTAIN(newtyp))
|| (IS_SINK(oldtyp) != IS_SINK(newtyp)))
count_level_features(); /* level.flags.nfountains,nsinks */
return TRUE;
}
#ifdef EXTRA_SANITY_CHECKS
} else {
impossible("set_levltyp(%i,%i,%i) !isok", x, y, typ);
impossible("set_levltyp(%d,%d,%d)%s%s",
(int) x, (int) y, (int) newtyp,
!isok(x, y) ? " not isok()" : "",
(newtyp < STONE || newtyp >= MAX_TYPE) ? " bad type" : "");
#endif /*EXTRA_SANITY_CHECKS*/
}
return FALSE;
@@ -99,14 +111,17 @@ set_levltyp_lit(coordxy x, coordxy y, schar typ, schar lit)
boolean ret = set_levltyp(x, y, typ);
if (ret && isok(x, y)) {
/* LAVAPOOL handled in set_levltyp */
if ((typ != LAVAPOOL) && (lit != SET_LIT_NOCHANGE)) {
if (lit != SET_LIT_NOCHANGE) {
#ifdef EXTRA_SANITY_CHECKS
if (lit < SET_LIT_NOCHANGE || lit > 1)
impossible("set_levltyp_lit(%i,%i,%i,%i)", x, y, typ, lit);
impossible("set_levltyp_lit(%d,%d,%d,%d)",
(int) x, (int) y, (int) typ, (int) lit);
#endif /*EXTRA_SANITY_CHECKS*/
if (lit == SET_LIT_RANDOM)
if (IS_LAVA(typ))
lit = 1;
else if (lit == SET_LIT_RANDOM)
lit = rn2(2);
levl[x][y].lit = lit;
}
}

View File

@@ -3485,11 +3485,6 @@ wizterrainwish(struct _readobjnam_data *d)
if (madeterrain) {
feel_newsym(x, y); /* map the spot where the wish occurred */
if (oldtyp == FOUNTAIN && lev->typ != FOUNTAIN)
gl.level.flags.nfountains--;
else if (oldtyp == SINK && lev->typ != SINK)
gl.level.flags.nsinks--;
/* hero started at <x,y> but might not be there anymore (create
lava, decline to die, and get teleported away to safety) */
if (u.uinwater && !is_pool(u.ux, u.uy)) {
@@ -3509,14 +3504,9 @@ wizterrainwish(struct _readobjnam_data *d)
}
}
/* fixups for replaced terrain that aren't handled above;
for fountain placed on fountain or sink placed on sink, the
increment above gets canceled out by the decrement here;
otherwise if fountain or sink was replaced, there's one less */
if (IS_FOUNTAIN(oldtyp))
gl.level.flags.nfountains--;
else if (IS_SINK(oldtyp))
gl.level.flags.nsinks--;
/* fixups for replaced terrain that aren't handled above */
if (IS_FOUNTAIN(oldtyp) || IS_SINK(oldtyp))
count_level_features(); /* update level.flags.nfountains,nsinks */
/* horizontal is overlaid by fountain->blessedftn, grave->disturbed */
if (IS_FOUNTAIN(oldtyp) || IS_GRAVE(oldtyp)
|| IS_WALL(oldtyp) || oldtyp == IRONBARS

View File

@@ -32,7 +32,6 @@ static void flip_vault_guard(int, struct monst *,
coordxy, coordxy, coordxy, coordxy);
static void sel_set_wall_property(coordxy, coordxy, genericptr_t);
static void set_wall_property(coordxy, coordxy, coordxy, coordxy, int);
static void count_features(void);
static void remove_boundary_syms(void);
static void set_door_orientation(int, int);
static boolean shared_with_room(int, int, struct mkroom *);
@@ -989,25 +988,6 @@ set_wall_property(coordxy x1, coordxy y1, coordxy x2, coordxy y2, int prop)
}
}
/*
* Count the different features (sinks, fountains) in the level.
*/
static void
count_features(void)
{
coordxy x, y;
gl.level.flags.nfountains = gl.level.flags.nsinks = 0;
for (y = 0; y < ROWNO; y++)
for (x = 0; x < COLNO; x++) {
int typ = levl[x][y].typ;
if (typ == FOUNTAIN)
gl.level.flags.nfountains++;
else if (typ == SINK)
gl.level.flags.nsinks++;
}
}
static void
remove_boundary_syms(void)
{
@@ -6559,7 +6539,7 @@ lspo_finalize_level(lua_State *L UNUSED)
if (L)
flip_level_rnd(gc.coder->allow_flips, FALSE);
count_features();
count_level_features();
if (L && gc.coder->solidify)
solidify_map();
@@ -7016,7 +6996,7 @@ load_special(const char *name)
flip_level_rnd(gc.coder->allow_flips, FALSE);
count_features();
count_level_features();
if (gc.coder->solidify)
solidify_map();