Skip to content

Commit

Permalink
Fix: portal to quest would sometimes not spawn if entry portal spawne…
Browse files Browse the repository at this point in the history
…d on Aphrodite's Garden or the Desolate Forest levels.

This was a weird one, initially caught while fuzzing. This started out
as an issue with a gold vault spawning on either Aphrodite's garden
level or the desolate forest level, and that vault being chosen to spawn
a portal to Fort Ludios. Fixed that (I think 😁) and then noticed some
more weirdness with the number of rooms spawning on those levels not
being as high as they should be. One thing led to another, discovered I
did not have to have a special ordinary room spawn on either of those
special levels, a portal to the quest could generate just fine without
it. Then started to see 'portal on top of portal??' impossible messages
when a quest portal would spawn on those special levels - sometimes, but
not all the time.

This meant there was a non-zero chance that the game could become
unwinnable under certain, albeit rare, conditions. This went from a
nuicance during fuzzing to a serious issue.

Took a bit of doing, but terrapin and I narrowed it down to some issues
in the somexyspace() function. Discovered that the creation of grass in
those special level rooms would mess with portal generation. Added a
couple exclusions for grass (and sand while there), and while poking
around in somexyspace(), noticed that the check for (xy_flags & 2) was
slightly wrong, and had been this way for a looooong time.

So long story short - fixed quest portal placement for those special
levels, normal rooms are no longer required there, fixed somexyspace().

I need a drink.

Co-authored-by: saltwaterterrapin <[email protected]>
  • Loading branch information
k21971 and saltwaterterrapin committed Mar 8, 2025
1 parent 56fdf14 commit a5114ee
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 17 deletions.
2 changes: 1 addition & 1 deletion dat/forest.des
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ OBJECT:random, random
}

# normal room, in case of branch portal/stairs placement
ROOM: "ordinary", random, random, random, random
ROOM: "forest", random, random, random, random
{
[30%]:TERRAIN:random, 't'
[30%]:TERRAIN:random, 't'
Expand Down
2 changes: 1 addition & 1 deletion dat/nymph.des
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ OBJECT:random, random
}

# normal room, in case of branch portal/stairs placement
ROOM: "ordinary", random, random, random, random
ROOM: "garden", random, random, random, random
{
[30%]:TERRAIN:random, 'T'
[30%]:TERRAIN:random, 'T'
Expand Down
2 changes: 2 additions & 0 deletions doc/evilhack-changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4360,4 +4360,6 @@ The following changes to date are:
- Fix: impossible "trying to break non-equipped glass obj?" when
attacking with certain objects
- Fix: grammar feedback when spells like barkskin or stoneskin wear off
- Fix: portal to quest would sometimes not spawn if entry portal spawned
on Aphrodite's Garden or the Desolate Forest levels

6 changes: 3 additions & 3 deletions src/mklev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1342,9 +1342,7 @@ coord *mp;

if (!somexyspace(croom, mp, 2)) {
if (!somexyspace(croom, mp, 0)
&& !In_hell(&u.uz)
&& !Is_nymph(&u.uz)
&& !Is_forest(&u.uz))
&& !In_hell(&u.uz))
impossible("Can't place branch!");
}
}
Expand Down Expand Up @@ -2227,6 +2225,8 @@ xchar x, y;
return;

if (!(u.uz.dnum == oracle_level.dnum /* in main dungeon */
&& u.uz.dnum == nymph_level.dnum /* Aphrodite's garden */
&& u.uz.dnum == forest_level.dnum /* Desolate Forest */
&& !at_dgn_entrance("The Quest") /* but not Quest's entry */
&& (u_depth = depth(&u.uz)) > 10 /* beneath 10 */
&& u_depth < depth(&medusa_level))) /* and above Medusa */
Expand Down
3 changes: 1 addition & 2 deletions src/mkmaze.c
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,7 @@ d_level *lev;
u_on_newpos(x, y);
break;
case LR_PORTAL:
if (!(rtype == GARDEN || rtype == FOREST))
mkportal(x, y, lev->dnum, lev->dlevel);
mkportal(x, y, lev->dnum, lev->dlevel);
break;
case LR_DOWNSTAIR:
case LR_UPSTAIR:
Expand Down
37 changes: 27 additions & 10 deletions src/mkroom.c
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,9 @@ struct mkroom *croom; /* NULL == choose random room */
continue;

sroom->rtype = GARDEN;
maderoom = TRUE;
level.flags.has_garden = 1;

/* create grass */
for (pos.x = sroom->lx; pos.x <= sroom->hx; pos.x++) {
for (pos.y = sroom->ly; pos.y <= sroom->hy; pos.y++) {
Expand All @@ -696,8 +699,6 @@ struct mkroom *croom; /* NULL == choose random room */
levl[pos.x][pos.y].typ = GRASS;
}
}
maderoom = TRUE;
level.flags.has_garden = 1;

/* create trees/fountains */
tried = 0;
Expand Down Expand Up @@ -769,6 +770,9 @@ struct mkroom *croom; /* NULL == choose random room */
continue;

sroom->rtype = FOREST;
maderoom = TRUE;
level.flags.has_forest = 1;

/* create grass */
for (pos.x = sroom->lx; pos.x <= sroom->hx; pos.x++) {
for (pos.y = sroom->ly; pos.y <= sroom->hy; pos.y++) {
Expand All @@ -777,8 +781,6 @@ struct mkroom *croom; /* NULL == choose random room */
levl[pos.x][pos.y].typ = GRASS;
}
}
maderoom = TRUE;
level.flags.has_forest = 1;

/* create dead trees and shallow water */
tried = 0;
Expand Down Expand Up @@ -1068,26 +1070,41 @@ int xy_flags;
{
int tryct = 0;
boolean isok;

do {
isok = TRUE;
if (croom && !somexy(croom, pos))
isok = FALSE;
if ((xy_flags & 16))
mazexy(pos);
if ((xy_flags & 1) && (IS_POOL(levl[pos->x][pos->y].typ)
|| IS_FURNITURE(levl[pos->x][pos->y].typ)))
if ((xy_flags & 1)
&& (IS_POOL(levl[pos->x][pos->y].typ)
|| IS_FURNITURE(levl[pos->x][pos->y].typ)))
isok = FALSE;
if (((xy_flags & 2) && (levl[pos->x][pos->y].typ != CORR))
|| (levl[pos->x][pos->y].typ != ROOM))
if ((xy_flags & 2)
&& ((levl[pos->x][pos->y].typ != CORR)
|| (levl[pos->x][pos->y].typ != ROOM)
|| (levl[pos->x][pos->y].typ != SAND)
|| (levl[pos->x][pos->y].typ != GRASS)))
isok = FALSE;
if ((xy_flags & 4) && (sobj_at(BOULDER, pos->x, pos->y)))
isok = FALSE;
if ((xy_flags & 8) && bydoor(pos->x, pos->y))
isok = FALSE;
} while ((!isok || !SPACE_POS(levl[pos->x][pos->y].typ)
|| occupied(pos->x, pos->y)) && (++tryct < 500));
if ((tryct < 500) && isok)
|| !(levl[pos->x][pos->y].typ == ROOM
|| levl[pos->x][pos->y].typ == CORR
|| levl[pos->x][pos->y].typ == ICE
|| levl[pos->x][pos->y].typ == SAND
|| levl[pos->x][pos->y].typ == GRASS)
|| occupied(pos->x, pos->y))
&& (++tryct < 2000));

/* make tryct greater than the total number of possible
tile spaces on any given map */
if ((tryct < 2000) && isok)
return TRUE;

return FALSE;
}

Expand Down

0 comments on commit a5114ee

Please sign in to comment.