A wand of stasis prevents teleportation (even in some cases where
it would normally not be prevented, e.g. the hero teleporting a
monster, or covetous monsters teleporting). This is intended to
provide an alternative tactic against covetous monsters (and their
AI has been adjusted to handle being under a stasis effect), but
might also be useful in other situations. It does not prevent
teleportation of objects, only the hero / monsters, and does not
at present prevent level teleportation (although I'm not sure about
this and it might well change in the future).
This breaks save compatibility, but is being pushed together with
other save-breaking changes to avoid the need for multiple bumps to
EDITLEVEL.
In previous versions of NetHack, this wasn't a problem because
teleport traps didn't *generate* on such levels, but more recent
changes have made this situation possible (e.g. a demon lord
migrates to a Gehennom filler level that already had a teleport
trap on it).
This change causes a "wrenching sensation", like stepping onto a
teleport trap with magic resistance does.
Reported via contact form but misclassified as spam. Applyin a magic
has a chance to teleport the hero to be adjacent to a pet rather than
vice versa, but it could do so even on no-teleport levels.
It is possible to create a bidirectional teleportation trap by making a
pair of teleportation traps with a fixed destination of each other's
coordinate. Moving or hurtling onto such a trap correctly materializes
the hero on top of the other trap without triggering it, but for some
reason I didn't dig into, sitting down to trigger the first trap does
also trigger the second one at the destination end, causing you to
counterintuitively teleport twice and end up back where you started.
Fix this by stopping tele_trap() from doing anything if it's called
recursively, using a static variable like spoteffects() does for the same
purpose. I had to adjust a bit of other tele_trap code to remove its
sole early return.
Commit ba731a346b "fix shop steal when
teleporting your engulfer" mentioned that there was no longer any
message given if engulfer+hero got teleported. Add such. It is a
bit lame but the situation is rare enough that it should suffice.
Picking up a shop item and not paying it, getting swallowed
by a monster, and then teleporting the monster out of the shop
with you in it, the shopkeeper didn't notice the theft.
But the object was not marked as paid either.
Also prevent giving a message of the swallower disappearing
and appearing when it was teleported. (Although now there's
no message given, so something should be added ...)
Ensure that the destination selection for intentional
teleport begins at the hero, rather than starting at
a place on the map stored from a prior travel command.
Makes Sokoban far less tedious when you don't have to worry about
monsters randomly popping up in the trap hallway while you're pushing
the boulder.
Adds a new exclusion zone for monster generation, and the goodpos
routine avoids the zones when GP_AVOID_MONPOS is used.
The g? structs had a mix of variables that were written to
the savefile, and those that were not.
For better clarity and to distinguish those that end up in
the savefile, relocate some g? variables that get written
directly to the savefile into different structs.
This updates EDITLEVEL, although technically it probably
didn't need to, since savefile contents are not changing.
Details:
gb.bases -> svb.bases
gb.bbubbles -> svb.bbubbles
gb.branches -> svb.branches
gc.context -> svc.context
gd.disco -> svd.disco
gd.dndest -> svd.dndest
gd.doors -> svd.doors
gd.doors_alloc -> svd.doors_alloc
gd.dungeon_topology -> svd.dungeon_topology
gd.dungeons -> svd.dungeons
ge.exclusion_zones -> sve.exclusion_zones
gh.hackpid -> svh.hackpid
gi.inv_pos -> svi.inv_pos
gk.killer -> svk.killer
gl.lastseentyp -> svl.lastseentyp
gl.level -> svl.level
gl.level_info -> svl.level_info
gm.mapseenchn -> svm.mapseenchn
gm.moves -> svm.moves
gm.mvitals -> svm.mvitals
gn.n_dgns -> svn.n_dgns
gn.n_regions -> svn.n_regions
gn.nroom -> svn.nroom
go.oracle_cnt -> svo.oracle_cnt
gp.pl_character -> svp.pl_character
gp.pl_fruit -> svp.pl_fruit
gp.plname -> svp.plname
gp.program_state -> svp.program_state
gq.quest_status -> svq.quest_status
gr.rooms -> svr.rooms
gs.sp_levchn -> svs.sp_levchn
gs.spl_book -> svs.spl_book
gt.timer_id -> svt.timer_id
gt.tune -> svt.tune
gu.updest -> svu.updest
gx.xmax -> svx.xmax
gx.xmin -> svx.xmin
gy.ymax -> svy.ymax
gy.ymin -> svy.ymin
Related note:
There are some pointer variables that are heads of chains that were not
moved from 'g?' to 'sv?', because they are not actually written to the
savefile directly, but the objects/monst/trap/lightsource/timer in the
chains they point to are. That can be changed, if desired.
Examples: gi.invent, gm.migrating_objs, gb.billobjs, gm.migrating_mons,
gf.ftrap, gl.light_base, gt.timer_base
Add a theme room with multiple visible teleportation traps
which will always teleport to specific locations in the same level.
Teleport trap change from xNetHack by copperwater <aosdict@gmail.com>.
A monster swallowed hero, hero teleported the monster away, but the level
was full of monsters, so the swallower ended up not moving anywhere,
so it was in the same location as hero, but hero wasn't swallowed by it.
Move the monster into limbo instead.
When makemon was called with all-zero arguments (e.g. for random
monster generation over time), ptr==NULL means "a random monster".
This was being forwarded to mon==NULL in makemon_rnd_goodpos, and
then mtmp==NULL in goodpos, which means "an object, not a monster".
Because objects can be generated under monsters, this meant that an
attempt to create a random monster could end up choosing a location
that already had a monster, which would then cause the monster
generation to fail.
This mostly wasn't noticeable in normal play: it effectively
reduced the monster generation rate depending on how many locations
outside LOS happened to contain a monster. Normally that's a very
small proportion, so the bug had no obvious effects: but when there
are very few locations outside LOS (i.e. the player can see almost
every location on the level), the bug effectively caused monster
generation to stop once those locations became occupied by
non-moving (e.g. hiding) monsters, something that became observable
in games where the player decided to dig out and light almost an
entire level.
This commit fixes the problem by adding a new flag to goodpos that
requests that it not choose a position that already has a monster.
This bug was diagnosed, and this fix committed, by ais523; but
nhmall wrote almost all of the code implementing the fix.
Adds a new boolean option, spot_monsters. If on, every time
the hero notices a monster which was out of sight before,
a message is given. Combine with accessiblemsg to get the
monster location:
(3north): You see a newt.
Breaks saves and bones.
This makes sense to me, and further increases the challenge of erinyes
being summoned on astral after donning a helm of opposite alignment (if
the player has abused his alignment to the extent that erinyes have
teleport control), since they wouldn't be able to be banished across the
map simply by breaking a wand of teleport control.
The tracks left by hero were cleared when player saved and
restored the game, or changed levels. Now the tracks are
saved in the dungeon level, so changing levels keeps the tracks
left by hero in that level.
Also increased the length of tracks from 50 to 100, and
simplify the tracking function.
Thing not done: fade out old tracks when returning to a level.
Breaks saves and bones.
Instead of just accepting an attribute, it's now possible to
use a color, or both color and attribute, for example:
OPTIONS=menu_headings:inverse
OPTIONS=menu_headings:red
OPTIONS=menu_headings:red&underline
Default is still just inverse.
This lets the player change the menu heading color without
needing to use menu colors for them.
Also makes it so the core uses NO_COLOR instead of 0, for all
the menu lines which don't have any prefedefined color.
Tested for tty, curses, x11, qt, and win32
Pull request from entrez: clean up the if/else-if/else logic in
level_tele().
Trying things a little differently this time. This is an extra log
message for commit 4876b70b9b.
Closes#1115
Add a new debugging option, 'montelecontrol', that allows a wizard-
mode player to choose a teleporting monster's destination. If player
picks a bad spot, confirmation will be requested. If accepted, the
spot will be used even though the consequences could be bad; that's
on the player. If rejected, the destination will be assigned as if
no control had been attempted rather than try again.
The fuzzer isn't allowed to override a bad spot if it tries to pick
one. That would probably trigger a sanity_check warning; the fuzzer
causes impossible warnings to behave as if panic, so accepting a bad
spot would just be fuzzer suicide. It is allowed to randomly set the
option and maybe--though extremely unlikely--randomly pick a valid
controlled destination.
Replace tests against tutorial_dnum with 'In_tutorial()' predicate.
Give a message when entering the tutorial (via level change mechanism).
Likewise, give a message when resuming regular play.
If player uses #quit or ^C in the tutorial, ask whether to cut the
tutorial short and resume regular play; skip "Really quit?" if the
answer is yes. Behavior is a bit odd for ^C + yes; it just sits there
until player types something.
Reported by Noisytoot: going from level tut-1 to tut-2 returned the
hero's starting equipment too soon, and exiting the tutorial from
tut-2 let the hero keep any equipment acquired within the tutorial.
Entering and leaving the tutorial was being handled by lua code in
the level description of tut-1 and adding a second level messed that
up. I didn't see any way of handing that with level-specific lua
code so I made it become the core's responsibility. gotolevel()
knows when the hero is moving from one dungeon branch to another so
it can recognize entry to or exit from the tutorial easily.
While fixing this, prevent #invoke of the Eye of the Aethiopica from
offering the tutorial as a candidate destination (was feasible if it
had been entered at start of game).
Not fixed: levels visited in the tutorial become part of #overview.
Show location as "Tutorial:1" instead of "Dlvl:1" on status lines.
Only tested with tty; some interfaces handle location themselves and
may need their own fixup for this.
Fixes#1046
The safe_teleds() change that restored picking random destination
attempts prior to making an exhaustive search contained a typo tjat
accidentally only accepted invalid positions instead of valid ones.
So unless it randomly picked 40 good spots, erroneously rejecting
all of them and then falling back to the try-everywhere situation
(which has its own testing without any typo), it would yield strange
results by placing the hero in walls or solid rock via choosing the
first inappropriate spot it tried.
Not part of that bug but related, sort of: for rloc(), use
rloc_pos_ok() instead of goodpos() during the exhaustive search as
well as during the random tries, but hang on to the first (after
randomization) position that passes goodpos() for a last resort.
The collect_coords() flag for 'skip-inaccessible' intended to be a
quick way to filter out walls and solid rock was using !ACCESSIBLE()
which also rejects water and lava locations. So such spots wouldn't
be picked by either safe_teleds() or rloc() when they were finding
a spot for aquatic or lava-tolerant forms. Instead of duplicating
a bunch of code to decide whether the hero's current form or the
teleporting monster should avoid !ACCESSIBLE() for a reason other
than having Passes_walls, make collect_coords(CC_SKIP_INACCS) use
!ZAP_POS() which rejects walls and rock but allows pools.
Make recently added collect_coords() global even though it is still
only being used in teleport.c.
Add CC_SKIP_INACCS flag to only collect accessible locations so that
there's no need for a custom filter callback or of collecting spots
that will always be rejected when put to use. Caller needs to check
Passes_walls/passes_walls() to decide whether that is suitable.
Merge some of the old safe_teleds() with the new, making it try
randomly 40 times before collecting coordinates for an exhaustive
selection. Prior to the recent change which added collect_coords()
it was trying 400 times and giving up if that didn't find a good spot.
Start using collect_coords() for rloc() as well as for safe_teleds().
Only try to pick a spot randomly 50 times now instead of 1000. If
those all fail, it does an exhaustive search of a randomized list of
candidates instead of old left-to-right, top-to-bottom map traversal.
Has not had nearly as much testing as safe_teleds() underwent.
rloc() was explicilty ignoring map column 1 for some reason. Unlike
reserved column 0, column 1 is part of every level and should be
considered for monster teleport destination even though most levels
don't utilize it.
The teleport to safety routine would try to find a viable spot 400
times, the first 200 rejecting trap locations and the last 200
accepting such. While testing various escape from lava variations
recently, I noticed that I could fail to reach safety even when there
was an open spot immediately adjacent to me. I added a BandAid(tm)
to make another 400 tries if the first attempt failed, but that was
clumsy and still didn't guarantee picking a viable spot.
This adds a new routine, collect_coords(), which will gather a list
of coordinates for the entire map. safe_teleds() goes through them
one by one until either finding a spot or exhausting the possibilies,
without randomly trying and retrying the same spot multiple times and
without missing other potential spots, also without just scanning the
map from left to right and top to bottom or similar.
Various other things which retry over and over, and especially the
ones which make a bunch of random attempts and then fallback to trying
every spot on the map, could be switched over to this, at least for
the falling back phase. Right now collect_coords() is local to
teleport.c but that could be easily changed.
The try-at-random method is much quicker when there are lots of
available spots but the gather-shuffled-candidates method is
guaranteed to succeed if success is possible. The way safe_teleds()
is presently using it collects the list with all locations within
2 steps first, then those within 3 to 4, then 5 to 6, and so on out,
randomized within each block of ranges. So the destination will be
within one step of being as close to the starting spot as possible
but not always immediately adjacent when that happens to be available.
Previously the fuzzer usually stayed in the Dungeons of Doom,
only very rarely going to other dungeon branches. Now, it will
randomly choose a dungeon branch and a level in that branch
to level teleport to.
sound_verbal(char *text, int32_t gender, int32_t tone, int32_t vol,
int32_t moreinfo);
-- NetHack will call this function when it wants to pass text of
spoken language by a character or creature within the game.
-- text is a transcript of what has been spoken.
-- gender indicates MALE or FEMALE sounding voice.
-- tone indicates the tone of the voice.
-- vol is the volume (1% - 100%) for the sound.
-- moreinfo is used to provide additional information to the soundlib.
-- there may be some accessibility uses for this function.
It may be useful for accessibility purposes too.
A preliminary implementation has been attempted for macsound to test
the interface on macOS. No tinkering of the voices has been done.
Use of the test implementation requires the following at build time with make.
WANT_SPEECH=1
That needs to be included on the make command line to enable the test code,
otherwise just the interface update is compiled in.
I don't know for certain when AVSpeechSynthesizer went into macOS, but older versions
likely don't support it, and would just leave off the WANT_SPEECH=1.
If built with WANT_SPEECH=1, the 'voices' NetHack option needs to be enabled.
It was a bit strange, when I first started up the test, to hear Asidonhopo,
the shopkeeper, talking to me as I entered his shop and interacted with him.
While fuzzing, I saw a sanity checking error complaining about
a ceiling hider being on top of a pool; the rock piercer was
teleported on top of the pool while it was hiding in the ceiling.
Try to be a bit more consistent when a monster is hiding in ceiling,
and if it's valid for it to be on a pool.
A number of C compiler suites have a math.h library that includes a yn()
function name that conflicts with NetHack's yn() macro:
"The y0(), y1(), and yn() functions are Bessel functions of the second kind,
for orders 0, 1, and n, respectively. The argument x must be positive. The
argument n should be greater than or equal to zero. If n is less than zero,
there will be a negative exponent in the result."
At one point, isaac64.h included math.h, although that has since been removed.
Some libraries used in NetHack (Qt for one) do include math.h and that required
build work-arounds to avoid the conflict.
Rename the NetHack macro from yn() to y_n() and avoid the math.h conflict
altogether, eliminating the need for that particular work-around.
If a pet picks up a no_charge item in a shop and gets teleported out,
clear that flag. (It's already being cleared for non-pets as soon as
they pick any item up.)