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.
When travel destination is one step away the code stops probing
for a path and reverts to normal movement, but it wasn't handling
the case where the one step was an impossible diagonal except for
hero being a grid bug. If the situation was a diagonal that's
too narrow to squeeze through, travel would end and regular move
would fail.
I've rejected the suggested fix and done it differently, without
attempting to figure out why the change to end_running() would
have been wrong. Clearly it was code that called end_running()
which needed to be fixed.
The test case was
..x|.
..|@.
.....
while carrying enough that directly moving from '@' to 'x' will
not be allowed. '@' would move one step south west and then stop
because findtravelpath() had ended travel due to single step move.
A similar case is
###
|x-#-
|0@.|
where 'x' is a doorway with intact open door and '0' is a boulder.
Prior to this fix, player would get "a boulder blocks the way" and
not move. After, '@' will move northeast then northwest then west
to get into orthogonal position and finally south into the doorway.
Even though it definitely fixes both mentioned test cases, I won't
be surprised if this results in regressions for other situations.
Fixes#487
A polymorphed hero who exploded when attacking thin air would use a
radius based on experience level rather than the fixed radius that
the monster form itself used. When exploding at a monster it didn't
wake other monsters at all.
Fixes#465
We have a struct called mkroom and a function called mkroom()
so c++ complains about the mkroom() function hiding the
initializer for the struct.
Similarly, we have a struct called attack and a function
called attack().
There may be a more elegant way of eliminating those two
warnings, but renaming mkroom() to do_mkroom() and
attack() to do_attack() was straightforward enough.
Being able to swap places with peaceful monsters instead of just
with pets made it possible to cause them to flee. Shopkeepers
wouldn't abandon the shop door but temple priests would attack
if hero tried to chat while they were fleeing.
add MALE, FEMALE, and gender-neutral names for individual monster species
to the mons array. The gender-neutral name (NEUTRAL) is mandatory, the
MALE and FEMALE versions are not.
replace code uses of the mname field of permonst with one of the three
potentially-available gender-specific names.
consolidate some separate mons entries that differed only by species into a
single mons entry (caveman, cavewoman and priest,priestess etc.)
consolidate several "* lord" and "* queen/* king" monst entries into
their single species, and allow both genders on some where it makes some
sense (there is probably more work and cleanup to come out of this at some
point, and the chosen gender-neutral name variations are not cast in stone
if someone has better suggestions).
related function or macro additions:
pmname(pm, gender) to get the gender variation of the permonst name. It
guards against monsters that haven't got anything except NEUTRAL naming
and falls back to the NEUTRAL version if FEMALE and MALE versions are
missing.
Ugender to obtain the current hero gender.
Mgender(mtmp) to obtain the gender of a monster
While the code can safely refer directly to pmnames[NEUTRAL] safely in the
code because it always exists, the other two (pmnames[MALE] and
pmnames[FEMALE] may not exist so use:
pmname(ptr, gidx)
where -ptr is a permonst *
-gidx is an index into the pmnames array field of the
permonst struct
pmname() checks for a valid index and checks for null-pointers for
pmnames[MALE] and pmnames[FEMALE], and will fall back to pmnames[NEUTRAL] if
the pointer requested if the requested variation is unavailable, or if the
gidx is out-of-range.
Allow code to specify makemon flags to request female or male (via MM_MALE
and MM_FEMALE flags respectively)to makedefs, since the species alone doesn't
distinguish male/female anymore. Specifying MM_MALE or MM_FEMALE won't
override the pm M2_MALE and M2_FEMALE flags on a mons[] entry.
male and female tiles have been added to win/share/monsters.txt.
The majority are duplicated placeholders except for those that were
separate mons entries before. Perhaps someone will contribute artwork in the
future to make the male and female variations visually distinguishable.
tilemapping via has the MALE tile indexes in the glyph2tile[]
array produced at build time. If a window port has information that the
FEMALE tile is required, it just has to increment the index returned
from the glyph2tile[] array by 1.
statues already preserved gender of the monster through STATUE_FEMALE
and STATUE_MALE, so ensure that pmnames takes that into consideration.
I expect some refinement will be required after broad play-testing puts it to
the test.
consolidate caveman,cavewoman and priest,priestess monst.c entries etc
This commit will require a bump of editlevel in patchlevel.h because it alters
the index numbers of the monsters due to the consolidation of some. Those
index numbers are saved in some other structures, even though the mons[] array
itself is not part of the savefile.
Window Port Interface Change
Also add a parameter to print_glyph to convey additional information beyond
the glyph to the window ports. Every single window port was calling back to
mapglyph for the information anyway, so just included it in the interface and
produce the information right in the display core.
The mapglyph() function uses will be eliminated, although there are still some
in the code yet to be dealt with.
win32, tty, x11, Qt, msdos window ports have all had adjustments done to
utilize the new parameter instead of calling mapglyph, but some of those
window ports have not been thoroughly tested since the changes.
Interface change additional info:
print_glyph(window, x, y, glyph, bkglyph, *glyphmod)
-- Print the glyph at (x,y) on the given window. Glyphs are
integers at the interface, mapped to whatever the window-
port wants (symbol, font, color, attributes, ...there's
a 1-1 map between glyphs and distinct things on the map).
-- bkglyph is a background glyph for potential use by some
graphical or tiled environments to allow the depiction
to fall against a background consistent with the grid
around x,y. If bkglyph is NO_GLYPH, then the parameter
should be ignored (do nothing with it).
-- glyphmod provides extended information about the glyph
that window ports can use to enhance the display in
various ways.
unsigned int glyphmod[NUM_GLYPHMOD]
where:
glyphmod[GM_TTYCHAR] is the text characters associated
with the original NetHack display.
glyphmod[GM_FLAGS] are the special flags that denote
additional information that window
ports can use.
glyphmod[GM_COLOR] is the text character
color associated with the original
NetHack display.
Support for including the glyphmod info in the display glyph buffer
alongside the glyph itself was added and is the default operation.
That can be turned off by defining UNBUFFERED_GLYPHMOD at compile time.
With UNBUFFERED_GLYPHMOD operation, a call will be placed to map_glyphmod()
immediately prior to every print_glyph() call.
This got out of hand pretty quickly. can_reach_floor() had
different criteria than trap activation. Objects dropped at a
hole locations that don't fall through were treated as if they
were at the bottom of an abyss, so couldn't be examined or
picked up.
This a bunch of changes; it is bound to introduce some new bugs.
Don't let hero at water or lava location swap places with a
pet that can't survive there. This was a regression to 3.4.3
behavior introduced when displacer beast monster was added.
I can't remember whether the regression was intentional at the
time, but guess not because I'm fairly sure that I would have
included a comment about it.
Using 'ladder' as a variable conflicts with 'struct flag flags'
because of a macro in rm.h. Also remove or hide a couple of
unused variables.
The hack.c diff is unrelated; just a reformatting bit that I had
laying around.
Monsters with rust attacks (rust monster) and corrosion attacks
(black pudding, gray ooze) can eat or otherwise destroy iron bars
but xorns could only move through the iron bars spot without being
able to eat the metal there. Change xorn to eat bars instead of
phazing through them. Lets rock moles eat bars too.
Hero polymorphed into a rust monster would eat bars if trying to
move to their location but couldn't do so if already there (maybe
was in xorn form and now in rust monster form). Xorns could pass
through them but not eat them. Allow hero metallivores to eat
bars at the current location via 'e', similar to eating food off
the floor. Hero as rock mole behaves like rust monster.
The previous teleport scroll fix was mislabeled with this pull
request number. Too late to fix that now; should have been
Closes#307
Now... Interaction between voluntarily busy hero (resting,
searching, and so on) with approaching monsters to decide whether
to stop had some inconsistencies.
Really closes#386
Adds two monsters originally from slash'em. I used the slash'em
tiles this time, also its code as a starting point but made various
revisions. Both the tiles could benefit from some touch-ups.
displacer beast: blue 'f'. Attempting a melee hit (ie, trying to
move to its spot) has a 50:50 chance for it to swap places with you.
Fairly tough monster to begin with, then half your ordinary attacks
effectively miss and if you try to face a mob by retreating to a
corridor or backing into a corner you can end up being drawn back
into the open. I added bargethrough capability, and also it won't
be fooled about hero's location by Displacement. [It only swaps
places during combat when contact is initiated by the hero, not
when attacked by another monster or when attacking.]
genetic engineer: green 'Q'. Its attack causes the target to be
polymorphed unless that target resists. Hero will almost always
have magic resistance by the time this monster is encountered, but
it can make conflict become risky by hitting and polymorphing other
monsters. Slash'em flagged it hell-only but I took that flag off;
I also took away its ability to teleport. Slash'em polymorphs the
hero if a genetic engineer corpse is eaten; that's included and I
introduced that for monsters too.
I added both of these to the list of candidates for monster spell
'summon nasties' and for post-Wizard harassment.
I also gave all the 'f's infravision. Probably only matters if the
hero polymorphs into a feline.
Displacer beast is originally from AD&D which depicts it as a six-
legged cougar with a pair of tentacles; it has Displacement rather
be able to affect an attacker's location. I think genetic engineer
is original to slash'em where it expands Q class but seems mainly to
be the base monster for Dr.Frankenstein (a unique monster with a
one-level side-branch lair in slash'em's incarnation of Gehennom).
When a failed #untrap attempt while mounted caused hero to be moved
onto the trap, it neglected to set the steed's coordinates to match.
If 'sanity_check' was On, that would trigger warnings about steed's
anomalous position. Eventually a normal move would put steed's
coordinates back in sync with the hero's.
The pull request code set u.usteed->{mx,my} directly. I've used
u_on_newpos() instead. I also replaced some direct manipulations of
u.{ux,uy} with u_on_newpos() so that if clipping is in effect it will
be updated.
Fixes#340
Pets can no longer be displaced out of a trap, because that was
inconsistent with peaceful monsters refusing to be displaced out of a
trap. The untaming-via-displacement-out-of-trap code is removed.
Pets also now have a better survival instinct: they follow the code for
peaceful displacement into a bad position, and refuse to swap places.
This means it's no longer possible to accidentally kill a pet by
levitating/walking over water and displacing it.
A side effect of making is_safepet() count peacefuls. Now checks
directly for a trapped, peaceful monster and says "they can't move out
of the trap"; this is inconsistent with pet behavior, and pet behavior
should probably be changed to be in line with it (ie they can't be
displaced out of a trap at all.)
Also refactor the code here a bit: a bunch of different if statements
have the exact same resetting code and steed resetting code in them.
Change this to a boolean flag and put the resetting code in one place
checked by that flag.
Shouldn't be able to displace priests since that could theoretically
eventually force them out of their temple, which would probably cause
problems. The Oracle doesn't usually move anyway, but seems like she
should "not want to swap places" in any circumstance.
Changes domove() code to allow displacing peaceful monsters.
Specifically, is_safepet() now returns true if the monster is peaceful.
Peacefuls are slightly pickier than pets about whether they consent to
being displaced: they will not displace if a goodpos() check fails for
the displaced space, or if there is a trap on the displaced space, or if
they are your quest leader. is_safepet should probably be renamed to
something else.
In the process of doing this, some other changes were made: the code now
checks whether the player and monster should be swapping places at all
first (previously it ran some code for displacing pets out of traps
first, which was a little weird if the displacement didn't actually
happen.)
In the original commit for this, I needed to guard the spoteffects()
call made in domove with a clause testing whether the player actually
moved; it was previously possible to fail to displace a monster and then
re-trigger a trap on the space you were still standing on. However, the
devteam has apparently put in an if (u.umoved) clause in the same place
and serving the same purpose.
When moving onto a different terrain type, the logic for whether to
block or unblock levitation and flying (for the case of moving in
or out of walls and solid stone with Passes_walls while levitating)
was correct but the XOR logic for whether to do a status update
because of such a change was incorrect. So stepping from room floor
to furniture or to doorway and vice versa or from corridor to doorway
and vice versa was requesting a status update when there was no need
for one.
Some other code must be requesting a status update when it is needed
for this (or possibly even more often than that?) because the status
line does seem to show the current state of Lev and Fly accurately.
Otherwise this should have been noticed when switch_terrain() was
first implemented.
Handling botl updates for 'time' was inconsistent. Set the flag to
do that when moves is incremented (where the update is suppressed if
running) or when running stops short.
losehp() would cancel running/traveling if called when in normal form
but not if called when polymorphed, so theoretically you could take
damage and keep on running. I don't have a test case to verify that.
Setting or clearing u.ustuck now requires that context.botl be set,
so make a new routine to take care of both instead of manipulating
that pointer directly.
Several conditions result in stale data on the status line when
starting or stopping because things which didn't used to affect it
haven't been setting context.botl to force an update. This wasn't
systematic; there are bound to be lots more.
Introduce eight achievements that can be attained by more players.
Entered Gnomish Mines - self explanatory
Entered Mine Town - the town portion, not just the level
Entered a shop - any tended shop on any level
Entered a temple - likewise for temple
Consulted the Oracle - bought at least one major or minor oracle
Read a Discworld Novel - read at least one passage
Entered Sokoban - like mines
Entered the Big Room - not always possible since not always present
The novel and bigroom ones aren't always achieveable since novels are
only guaranteed if a book or scroll shop gets created and bigroom is
only guaranteed in wizard mode. No one ever claimed that every
possible achievement can be attained in a single game. (If one for
entering the Fort Ludios level--or perhaps entering the Fort itself--
eventually gets add, that won't be possible in every game either.)
The mine town one probably needs some tweaking. Two of the town's
seven variants have no town boundary (despite a rectangular area of
pre-defined map) and at present simply arriving on either of those
levels is enough to be credited with the entered-town achievement.
Bump EDITLEVEL because u.uachieved[] has increased in size. This
time it has been expanded to the maximum that xlogfile's bitmask of
achievements can handle, enough for up to 9 more achievements without
another EDITLEVEL increment.
Move 'implicit_uncursed' and 'mention_walls' from iflags to flags to
make their current setting persist across save/restore. Invalidates
existing save files.
Trying to move into a wall or solid rock fails and doesn't do anything
(unless the 'mention_walls' option is On) and doesn't use a turn, and
trying to move off the edge of the map window also doesn't do anything
(except for 'mention_walls') but that did use a turn. Don't.
Polymophed into a giant and moving onto a boulder's location could
yield "you easily pick it up" (without actually doing so) followed
by "you see a boulder here". It would happen if autopickup was Off,
or if the 'm' move-without-autopickup prefix was used, while either
boulder was included in pickup_types (including when that is set
for 'all') or hero had thrown that particular boulder and
pickup_thrown was On. The check for whether auto-pick should try
on an object relied on its caller verifying that autopickup was On.
pickup() does that for
pickup() -> autopick() -> autopick_testobj()
but moverock() wasn't doing that for
moverock() -> autopick_testobj()
so the logic controlling moverock's message was subverted.
I first thought that logic itself was incorrect and changed the
message. This keeps the new message even though it turned out not
to be cause of the problem.
Fixes#279
The change to make "ouch! you bump into a door" use up a turn didn't
end running, so when it happened while running useless turns took
place and that message was delivered repeatedly until some other
action interrupted the hero. It didn't matter whether autoopen is
enabled.
Fixes#277
Report complained that having autoopen not work when fumbling was
inconvenient and mentioned that the "ouch! you bump into a door"
result didn't take any time. This updates the documentation to
state that autoopen won't work while fumbling (so the inconvenient
behavior persists) but changes movement so that bumping into a door
now takes time. (Despite "ouch!", it doesn't inflict any damage.)
Also, document the recently added autounlock option.
When GOLDOBJ was activated unconditionally, several texts started referencing
"money" instead of "gold".
As we don't have the intention to introduce a complex coin system with
different denominations, change it back and also some other places that
reference "money".
This adds a boolean option, autounlock, defaulting to true. When this is
set to TRUE, messages stating that some door or container is locked are
automatically followed by a prompt asking if you would like to unlock
it, if you are carrying an unlocking tool (key, lock pick, or credit
card).
Architecturally, this extends the pick_lock function to take three
additional arguments (door coordinates or a box on the ground you are
autounlocking).
The code that selects an unlocking tool will always look first for a
skeleton key, then a lock pick, then a credit card. Since curses, rust,
and other attributes don't really have an effect on the viability of the
unlocking device, it didn't seem to warrant making a more complex
function for that.
Add hallucinatory trap names
This adds many funny, realistic, and nonsensical traps to the game, to
be shown when the player is hallucinating.
Architecturally, the biggest change is merging the what_trap macro and
the "defsyms[trap_to_defsym(ttyp)].explanation" pattern into a single
function "trapname", which returns the name of the trap, handling the
hallucination case. There is also a second parameter used for overriding
hallucination in the occasional cases where the actual trap name should
always be returned.
In addition, the what_trap and random_trap macros are now obsolete and
not used anywhere, so they are removed.
reinstate anti-rng abuse bit on hallucination
updates to hallucinatory trap names and fixes37.0 entry
Poly'd hero hiding on the ceiling was told "you can't go down here"
if using '>' at a spot that didn't have down stairs, trap door, hole,
or pit. Let '>' bring a ceiling hider out of hiding; lurker above
resumes flying, piercer falls to floor or whatever is underneath it.