level teleporters vs Ft.Ludios
From newsgroup discussion where slash'em changes have revealed a latent nethack bug: prevent placing level teleporters in single- level branches. The Knox level doesn't have any level teleporters (or random traps) but wizard mode wishing could create them there. They wouldn't do anything because the only possible destination would be the same level. Pushing a boulder onto one used to trigger an infinite loop (and still does in slash'em, which has other single-level branches besides Ft.Ludios) trying to relocate it. Boulder pushing was changed 15 years ago to prevent the infinite loop and to avoid giving "the boulder disappears" message when a level teleporter failed, but rolling boulder traversal lacked that same change--it wasn't vulnerable to looping but could give an inaccurate message claiming that the boulder disappeared when it actually didn't. Fixing this is a bit late; rolling boulder trap creation was recently changed to not choose a path that rolls over teleportation or level tele traps any more.
This commit is contained in:
@@ -449,6 +449,9 @@ turn off input autocompletion for '#twoweapon' since simple 'X' invokes it;
|
|||||||
likewise for #wizdetect (^E), #wizgenesis (^G), #wizidentify (^I),
|
likewise for #wizdetect (^E), #wizgenesis (^G), #wizidentify (^I),
|
||||||
#wizlevelport (^V), #wizmap (^F), and #wizwish (^W); probably ought
|
#wizlevelport (^V), #wizmap (^F), and #wizwish (^W); probably ought
|
||||||
to do so for #overview (^O) too but that one still autocompletes
|
to do so for #overview (^O) too but that one still autocompletes
|
||||||
|
if a branch has only one level (Fort Ludios), prevent creation of any level
|
||||||
|
teleporters there (level definition doesn't have any but wizard mode
|
||||||
|
wishing could attempt to place one)
|
||||||
|
|
||||||
|
|
||||||
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
|
Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
|
||||||
|
|||||||
@@ -618,6 +618,7 @@ extern boolean On_W_tower_level(d_level *);
|
|||||||
extern boolean In_W_tower(int, int, d_level *);
|
extern boolean In_W_tower(int, int, d_level *);
|
||||||
extern void find_hell(d_level *);
|
extern void find_hell(d_level *);
|
||||||
extern void goto_hell(boolean, boolean);
|
extern void goto_hell(boolean, boolean);
|
||||||
|
extern boolean single_level_branch(d_level *);
|
||||||
extern void assign_level(d_level *, d_level *);
|
extern void assign_level(d_level *, d_level *);
|
||||||
extern void assign_rnd_level(d_level *, d_level *, int);
|
extern void assign_rnd_level(d_level *, d_level *, int);
|
||||||
extern unsigned int induced_align(int);
|
extern unsigned int induced_align(int);
|
||||||
|
|||||||
@@ -1848,6 +1848,17 @@ goto_hell(boolean at_stairs, boolean falling)
|
|||||||
goto_level(&lev, at_stairs, falling, FALSE);
|
goto_level(&lev, at_stairs, falling, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* is 'lev' the only level in its branch? affects level teleporters */
|
||||||
|
boolean
|
||||||
|
single_level_branch(d_level *lev)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* TODO: this should be generalized instead of assuming that
|
||||||
|
* Fort Ludios is the only single level branch in the dungeon.
|
||||||
|
*/
|
||||||
|
return Is_knox(lev);
|
||||||
|
}
|
||||||
|
|
||||||
/* equivalent to dest = source */
|
/* equivalent to dest = source */
|
||||||
void
|
void
|
||||||
assign_level(d_level *dest, d_level *src)
|
assign_level(d_level *dest, d_level *src)
|
||||||
|
|||||||
22
src/hack.c
22
src/hack.c
@@ -178,6 +178,9 @@ moverock(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ttmp) {
|
if (ttmp) {
|
||||||
|
int newlev = 0; /* lint suppression */
|
||||||
|
d_level dest;
|
||||||
|
|
||||||
/* if a trap operates on the boulder, don't attempt
|
/* if a trap operates on the boulder, don't attempt
|
||||||
to move any others at this location; return -1
|
to move any others at this location; return -1
|
||||||
if another boulder is in hero's way, or 0 if he
|
if another boulder is in hero's way, or 0 if he
|
||||||
@@ -236,16 +239,14 @@ moverock(void)
|
|||||||
newsym(rx, ry);
|
newsym(rx, ry);
|
||||||
return sobj_at(BOULDER, sx, sy) ? -1 : 0;
|
return sobj_at(BOULDER, sx, sy) ? -1 : 0;
|
||||||
case LEVEL_TELEP:
|
case LEVEL_TELEP:
|
||||||
case TELEP_TRAP: {
|
/* 20% chance of picking current level; 100% chance for
|
||||||
int newlev = 0; /* lint suppression */
|
that if in single-level branch (Knox) or in endgame */
|
||||||
d_level dest;
|
newlev = random_teleport_level();
|
||||||
|
/* if trap doesn't work, skip "disappears" message */
|
||||||
if (ttmp->ttyp == LEVEL_TELEP) {
|
if (newlev == depth(&u.uz))
|
||||||
newlev = random_teleport_level();
|
goto dopush;
|
||||||
if (newlev == depth(&u.uz) || In_endgame(&u.uz))
|
/*FALLTHRU*/
|
||||||
/* trap didn't work; skip "disappears" message */
|
case TELEP_TRAP:
|
||||||
goto dopush;
|
|
||||||
}
|
|
||||||
if (u.usteed)
|
if (u.usteed)
|
||||||
pline("%s pushes %s and suddenly it disappears!",
|
pline("%s pushes %s and suddenly it disappears!",
|
||||||
upstart(y_monnam(u.usteed)), the(xname(otmp)));
|
upstart(y_monnam(u.usteed)), the(xname(otmp)));
|
||||||
@@ -264,7 +265,6 @@ moverock(void)
|
|||||||
}
|
}
|
||||||
seetrap(ttmp);
|
seetrap(ttmp);
|
||||||
return sobj_at(BOULDER, sx, sy) ? -1 : 0;
|
return sobj_at(BOULDER, sx, sy) ? -1 : 0;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break; /* boulder not affected by this trap */
|
break; /* boulder not affected by this trap */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1377,7 +1377,8 @@ mktrap(int num, int mazeflag, struct mkroom *croom, coord *tm)
|
|||||||
kind = NO_TRAP;
|
kind = NO_TRAP;
|
||||||
break;
|
break;
|
||||||
case LEVEL_TELEP:
|
case LEVEL_TELEP:
|
||||||
if (lvl < 5 || g.level.flags.noteleport)
|
if (lvl < 5 || g.level.flags.noteleport
|
||||||
|
|| single_level_branch(&u.uz))
|
||||||
kind = NO_TRAP;
|
kind = NO_TRAP;
|
||||||
break;
|
break;
|
||||||
case SPIKED_PIT:
|
case SPIKED_PIT:
|
||||||
|
|||||||
@@ -892,7 +892,7 @@ level_tele(void)
|
|||||||
/* if in Knox and the requested level > 0, stay put.
|
/* if in Knox and the requested level > 0, stay put.
|
||||||
* we let negative values requests fall into the "heaven" loop.
|
* we let negative values requests fall into the "heaven" loop.
|
||||||
*/
|
*/
|
||||||
if (Is_knox(&u.uz) && newlev > 0 && !force_dest) {
|
if (single_level_branch(&u.uz) && newlev > 0 && !force_dest) {
|
||||||
You1(shudder_for_moment);
|
You1(shudder_for_moment);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1532,7 +1532,7 @@ random_teleport_level(void)
|
|||||||
int nlev, max_depth, min_depth, cur_depth = (int) depth(&u.uz);
|
int nlev, max_depth, min_depth, cur_depth = (int) depth(&u.uz);
|
||||||
|
|
||||||
/* [the endgame case can only occur in wizard mode] */
|
/* [the endgame case can only occur in wizard mode] */
|
||||||
if (!rn2(5) || Is_knox(&u.uz) || In_endgame(&u.uz))
|
if (!rn2(5) || single_level_branch(&u.uz) || In_endgame(&u.uz))
|
||||||
return cur_depth;
|
return cur_depth;
|
||||||
|
|
||||||
/* What I really want to do is as follows:
|
/* What I really want to do is as follows:
|
||||||
|
|||||||
36
src/trap.c
36
src/trap.c
@@ -352,10 +352,12 @@ maketrap(int x, int y, int typ)
|
|||||||
|| (u.utraptype == TT_LAVA && !is_lava(x, y))))
|
|| (u.utraptype == TT_LAVA && !is_lava(x, y))))
|
||||||
reset_utrap(FALSE);
|
reset_utrap(FALSE);
|
||||||
/* old <tx,ty> remain valid */
|
/* old <tx,ty> remain valid */
|
||||||
} else if (IS_FURNITURE(lev->typ)
|
} else if ((IS_FURNITURE(lev->typ)
|
||||||
&& (!IS_GRAVE(lev->typ) || (typ != PIT && typ != HOLE))) {
|
&& (!IS_GRAVE(lev->typ) || (typ != PIT && typ != HOLE)))
|
||||||
|
|| (typ == LEVEL_TELEP && single_level_branch(&u.uz))) {
|
||||||
/* no trap on top of furniture (caller usually screens the
|
/* no trap on top of furniture (caller usually screens the
|
||||||
location to inhibit this, but wizard mode wishing doesn't) */
|
location to inhibit this, but wizard mode wishing doesn't)
|
||||||
|
and no level teleporter in branch with only one level */
|
||||||
return (struct trap *) 0;
|
return (struct trap *) 0;
|
||||||
} else {
|
} else {
|
||||||
oldplace = FALSE;
|
oldplace = FALSE;
|
||||||
@@ -2778,6 +2780,9 @@ launch_obj(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((t = t_at(g.bhitpos.x, g.bhitpos.y)) != 0 && otyp == BOULDER) {
|
if ((t = t_at(g.bhitpos.x, g.bhitpos.y)) != 0 && otyp == BOULDER) {
|
||||||
|
int newlev = 0;
|
||||||
|
d_level dest;
|
||||||
|
|
||||||
switch (t->ttyp) {
|
switch (t->ttyp) {
|
||||||
case LANDMINE:
|
case LANDMINE:
|
||||||
if (rn2(10) > 2) {
|
if (rn2(10) > 2) {
|
||||||
@@ -2801,20 +2806,22 @@ launch_obj(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LEVEL_TELEP:
|
case LEVEL_TELEP:
|
||||||
|
/* 20% chance of picking current level; 100% chance for
|
||||||
|
that if in single-level branch (Knox) or in endgame */
|
||||||
|
newlev = random_teleport_level();
|
||||||
|
/* if trap doesn't work, skip "disappears" message */
|
||||||
|
if (newlev == depth(&u.uz))
|
||||||
|
break;
|
||||||
|
/*FALLTHRU*/
|
||||||
case TELEP_TRAP:
|
case TELEP_TRAP:
|
||||||
if (cansee(g.bhitpos.x, g.bhitpos.y))
|
if (cansee(g.bhitpos.x, g.bhitpos.y))
|
||||||
pline("Suddenly the rolling boulder disappears!");
|
pline("Suddenly the rolling boulder disappears!");
|
||||||
else
|
else if (!Deaf)
|
||||||
You_hear("a rumbling stop abruptly.");
|
You_hear("a rumbling stop abruptly.");
|
||||||
singleobj->otrapped = 0;
|
singleobj->otrapped = 0;
|
||||||
if (t->ttyp == TELEP_TRAP)
|
if (t->ttyp == TELEP_TRAP) {
|
||||||
(void) rloco(singleobj);
|
(void) rloco(singleobj);
|
||||||
else {
|
} else {
|
||||||
int newlev = random_teleport_level();
|
|
||||||
d_level dest;
|
|
||||||
|
|
||||||
if (newlev == depth(&u.uz) || In_endgame(&u.uz))
|
|
||||||
continue;
|
|
||||||
add_to_migration(singleobj);
|
add_to_migration(singleobj);
|
||||||
get_level(&dest, newlev);
|
get_level(&dest, newlev);
|
||||||
singleobj->ox = dest.dnum;
|
singleobj->ox = dest.dnum;
|
||||||
@@ -2838,9 +2845,12 @@ launch_obj(
|
|||||||
}
|
}
|
||||||
dist = -1; /* stop rolling immediately */
|
dist = -1; /* stop rolling immediately */
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
if (used_up || dist == -1)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (used_up || dist == -1)
|
||||||
|
break; /* from 'while' loop */
|
||||||
}
|
}
|
||||||
if (flooreffects(singleobj, g.bhitpos.x, g.bhitpos.y, "fall")) {
|
if (flooreffects(singleobj, g.bhitpos.x, g.bhitpos.y, "fall")) {
|
||||||
used_up = TRUE;
|
used_up = TRUE;
|
||||||
|
|||||||
Reference in New Issue
Block a user