The revised #overview (displaying info in a menu instead of text
window), works as intended for tty and curses, during play and also
during end of game disclosure.
For X11 it worked during play but during disclosure it issued a
call to impossible() for every line of overview output and there
was no overview shown. ("add_menu: called before start_menu",
sent to stdout where the player may not have a chance to see it.)
For Qt things were worse, working during play but with indentation
that isn't what is intended, and during disclosure it crashed in
add_menu().
This avoids the impossible and the crash, by changing how the core
treats the menu rather than by updating how FOO_add_menu() deals
with the offending previous usage.
I suspect that to fix the Qt indentation, #overview might need to
be changed to behave like #attributes: use a menu during play but
a text window during disclosure. It has a hack for text windows
to switch from the default font to a fixed-width one if any line
in the text contains four consecutive spaces. Either menu windows
aren't doing the same thing, or the two-column layout they use to
render their text is messing that up. (I haven't looked.)
... if the boulder is in a position they want to move to.
Shopkeepers, priests, and the quest leader can break one boulder
and then need to take several turns before being able to break
another. Riders can break a boulder every turn.
Require the 'm' prefix to treat #overview as a menu with selectable
entries. During end-of-game disclosure it shows every level that was
visited, but during play it only shows levels which have annotations
(typically automatically generated ones). The menu wasn't offering
any chance to add an annotation to levels without such, so force
'm #overview' to show every visited level. Continue to avoid that
(and also avoid the clutter introduced by menu entry selector letters)
for normal #overview.
... unless explicitly specified to generate at a specific point or
within a specific area. But if they are permitted to generate anywhere
on the level, and it contains water, they always end up in the water. I
noticed this when trying to explicitly specify ghouls to generate
anywhere on a level with a minimal amount of water.
This was due to the definition of "amphibious" being conflated with
"breathless", such that all breathless monsters counted as amphibious.
There are plenty of breathless monsters in the game that decidedly don't
normally inhabit water, such as undead, but they would pass the
amphibious() check in pm_to_humidity and thus the game decides that they
must generate in wet terrain if there is any available.
This fix takes the approach of changing amphibious() so that it no
longer checks the M1_BREATHLESS flag and only considers M1_AMPHIBIOUS,
then updating the places where amphibious() and Amphibious are used
accordingly. I also added a new macro cant_drown() which wraps up
swimming, amphibiousness, and breathlessness because these three things
are frequently checked together in the context of whether something
should drown.
Places where amphibious() or Amphibious did NOT have an extra
breathless() or Breathless check added on, and thus where behavior has
been changed:
- The pm_to_humidity function (to fix the bug).
- Player vs water in goodpos; it didn't seem like being polymorphed into
a breathless non-amphibious monster should make it fair game to
randomly teleport into water even though it's technically safe.
- Awarding extra experience when killing an eel. (So the hero will get
the extra experience if they are polymorphed into a breathless
non-amphibious monster and don't have magical breathing. Very much an
edge case.)
Aklyses uniquely have a tether attached to one's wrist, which would stay
secure even when one's fingers are slippery. Therefore, it doesn't make
for the weapon to be dropped completely, tether and all, when Glib.
However, the game doesn't have a way to model a weapon that's on the
floor at your feet but still attached to your arm. (Technically the
uball and uchain code does do this sort of thing already, but it can't
be used here - what if you have an aklys slipping out of your grasp and
are punished at the same time?) So the next best thing is to give
aklyses immunity to slipping from one's hand.
This aims to fix the issue in which there are way more wands of speed
monster in the game than the player has any use for. Usually, one is
enough for the whole game (unless a player has a lot of pets they want
to speed up) but since they're an item monsters can generate with, it's
typical to see eight to twelve of them, useless for anything besides
polypiling into other wands since they just grant an intrinsic that's
usually obtained early in the game.
By making the wand effect (on the player) temporary very fast speed, it
becomes desirable to keep the wand around again. By adding a lightweight
source of very fast speed, it also helps diversify character builds away
from always relying on speed boots. And importantly, it becomes an item
the player can use situationally early in the game to enhance their odds
in a fight or run away from danger.
Meanwhile, the speed-intrinsic-granting has been moved to the potion,
and the potion is still superior to the wand in the duration of very
fast speed it grants (100 + d10 turns for an uncursed potion or 160 +
d10 turns for a blessed one, versus 50 + d25 turns for the wand).
Since monsters don't have a concept of very fast speed, this doesn't
change the wand or potion effects on them. They will still gain
permanent intrinsic speed by either method.
Issue reported by AndrioCelos: if hero was killed by a wand zapped
by a monster, the cause of death was "killed by <wand damage>
imagined by <the monster>" instead of intended "killed by <wand
damage> zapped by <the monster>". Report mentioned that the monster
was unseen but that wasn't relevant.
This worked when monster wand zaps were briefly changed to use
-9..-0 but got broken when that was reverted to using -39..-30 (due
to -0 being the same as 0 so making zapper of wands of magic missile
ambiguous).
Fixes#1011
If for some reason a shop room has multiple doors, try all of them
to find a good shop door, instead of just the first one, and failing
if it wasn't a good one.
This only matters for wizard-mode with SHOPTYPE environment var.
Prevent hug attacks (owlbear, python, pit fiend, several others),
attacks for wrap damage (eel and kraken, trapper and lurker above),
attacks for stick-to damage (mimic, lichen), and attacks for digestion
damage (purple worm) from succeeding against unsolid monsters (ghosts,
lights, vortices, most elementals).
Polymorph of an engulf or hold target into unsolid form has been
addressed by a couple of previous updates.
More towards not being able to swallow or hold an non-solid creature.
When engulfed, expel hero if the new form is unsolid or too big.
Give a message if hero is being held (rather than engulfed) and has
to be released.
Adding a call to expels() required some code reordering because it
calls spoteffects().
Give the hint about using '#monster' at the very end of polymon().
Wizmode testing shop generation, I encountered impossible
"Where is shopdoor" - the room that was being turned into
a shop had a subroom sharing the outer south wall with
the parent room, with a random niche placed such that it was
actually in the subroom wall. The niche door was still added
to the parent room, and the shop generation was trying to use
that door as the shop entrance.
As random niches are only used in room-and-corridor style
levels, subrooms aren't that common, so I just opted to
skip the niche generation if it was going to be placed
without being directly attached to the room it wanted.
The niche generation could be changed at a later date to actually
add the niche door to the correct room instead, if we ever feel
it's necessary to have random niches in subrooms.
As an interesting side note, I had no idea random niches were
only placed in the south or north walls of rooms, never to
east or west.
Something I noticed while looking over the recent report about hug
attacks. If a monster carrying one or more boulders (picked up while
in giant form) polymorphs into a non-giant, it will drop them. If
that happens while it's trapped, it could be killed. newcham() would
use a stale pointer to continue traversing its inventory after it was
dead and its possessions had been dropped.
I managed to get a chameleon-as-giant carrying boulders and some other
stuff trapped in a bear trap and then transform, but the few attempts
I made never killed off its next form so I wasn't able to induce a
crash to verify that a problem would actually occur.
The fix in commit 14d003c4ba prevented
current energy from ending up 1 point above maximum energy but it
didn't preserve the intent of splitting the drain with up to half
coming out of maximum and the remainder out of current. This restores
that intent but now only does so when maximum is more than the full
drain amount rather than when it is more than the up-to-half portion,
becoming less harsh when hero's max energy is very low. If current
is also very low then max energy will be reduced anyway, but by less.
Some unrelated formatting of invent.c has gotten mixed in.
Revises #1003
Issue reported by vultur-cadens: when trap effect of an anti-magic
field reduced maximum energy, the result might end up with current
energy being one point higher than new maximum.
Fixes#1003
to program_state.input_state
Rename program_state.getting_a_command and give it an int value
instead of treating it as boolean. Start using to it for Qt to
suppress commands initiated from the drop down menus in the title bar
if nethack isn't expecting to get a command from the user. Those menu
choices inject extended command text into the input stream and should
be avoided when entering text or waiting for a menu to be dismissed.
These menus would be better if they grayed out unavailable choices
when pulled down instead of accepting any choice and then treating
that no good. I'm not going to try to figure out to do that with Qt.
And this workaround for the menus doesn't have any affect on the
toolbar buttons below the title bar. Those execute core commands
directly and when I tried to use jacket routines instead, the tool bar
stopped showing up so I've given up.
This won't fix the reported problem of getting stuck in a loop.
- Move secondary preprocessor defines down further in config.h
so that they can be overridden via [platform]conf.h which is
included from global.h, specifically:
LIVELOGFILE when LIVELOG is defined
DUMPLOG_FILE when DUMPLOG is defined
- Minimize platform-specific, or compiler-specific code in hack.h and decl.h.
- reorganize src/decl.c to align with include/decl.h.
- a new header file cstd.h added, containing calls to C99
standard header files.
- hack.h, decl.h, and decl.c have been cleaned up and had code
moved so that things line up as follows:
hack.h defines values that are available to all
NetHack source files, contains enums for use in all
NetHack source files, and contains a number of
struct definitions for use in all NetHack source files.
It does not contain variable declarations or variable
definitions.
decl.h contains the extern declarations for variables that
are defined in decl.c. These variables are global and
available to all NetHack source files. The location of
the variables within decl.h was random, so give it some
order for now.
decl.c contains the definition of the variables declared in
decl.h, and initializes them where appropriate. The
variable definitions are laid out in much the
same order as their declarations in decl.h.
- wintty.h: There were some varying terminal-related prototypes in
system.h, and that was the only thing left that demanded that
system.h be included. Those have been replaced by an #include
<term.h> in include/wintty.h to get the more current (and hopefully
more correct) prototypes, rather than hardcoding them in NetHack
sources.
For edge-case platform compatiblity, there is no #include <term.h>
if the build defines NO_TERMCAP_HEADERS. In that case one set of
hardcoded prototypes is still used in include/wintty.h.
The added #include "term.h" is also bypassed for NO_TERMS builds (builds
that don't link to terminfo/termcap at all, but still present a tty
interface using platform or window-port specific functions to fulfill
the same role as that of terminfo/termcap).
- some scattered, unnecessary #include "integer.h" were removed from
various files, since that's always included in current NetHack-3.7
sources, either directly from config.h or indirectly from #include
"hack.h".
- system.h references removed.
- new cstd.h added; the #include "system.h" references in Makefiles
and project files (Xcode, visual studio), were replaced
with #include "cstd.h" references. A "make depends" is probably
warranted.
Also:
- Use of <term.h>, which defines clear_screen() as a macro, conflicts
with an actual function with that name in win/tty/termcap.c. The most
straight-forward course of action was to rename the NetHack function,
and change the references to it, from clear_screen() to
term_clear_screen(), so that was done.
Previously when monster was interested to pick up an item,
the code went through the whole object chain, so going through
all the items on the level. This caused problems with some games,
for example where the player created thousands of meatballs
in separate stacks.
Changed the code so it now looks at the map locations inside
the search radius, and the stacks in those map locations,
skipping locations as early as possible.
This should have no difference in behavior, except on Amiga;
as it is no longer supported, I removed all the amiga-specific
config-line handling. If anyone steps forward to support
that platform in the future, it shouldn't be too hard to
add those back.
- add a themeroom with random buried zombifying corpses
- disturbing buried zombies makes them revive much faster
- lua des.object() now returns the object it created
The prior behavior was an ugly kludge that treated rumors as
only-eligible-to-appear-from-fortune-cookies if they contained the
string "fortune" or "pity". This commit gets rid of that system and
introduces a system where each rumor can be specified as such by
prefixing it with "[cookie]".
In order to avoid changing existing behavior, I have applied the
[cookie] prefix to all the rumors which were affected under the old
rules. However, several rumors seem like they were misclassified under
the old rules, and I am in favor of changing them as follows:
Should be made cookie-only (they only really make sense in context of
eating a fortune cookie):
- "Ouch. I hate when that happens."
- "PLEASE ignore previous rumor."
- "Suddenly, the dungeon will collapse..."
- "Unfortunately, this message was left intentionally blank."
Should be made non-cookie-only (they make sense if they appear as
engravings, Oracle messages, or artifact whispers):
- "They say that a gypsy could tell your fortune for a price."
- "They say that fortune cookies are food for thought."
Every other case I'm aware of where a visible monster that the hero can
see turns invisible has some associated message, but not when zapping it
with the wand of make invisible and lacking see invisible. This adds a
message "[monster] vanishes!" in this case, which is the same as the
most common message one gets for zapping a monster with teleportation,
by design to keep the wand's identity ambiguous.
Technically, prior to this commit, there was a leak of information if
one zapped a wand that made a monster disappear: the identity of the
want could be determined immediately by the presence or absence of a
vanishing message. Practically, though, it was easy to check if there
was a new invisible monster in the same spot or not.
Also, zapping teleportation at a monster and having it land somewhere
visible to the hero now has attention explicitly drawn to it with a
message (in prior versions it didn't, so it could potentially be a
different monster appearing by some other means), so I additionally made
the wand of teleportation automatically identify itself when the hero
sees the monster appear as a result of their zap.
Reverse part of commit 4021a63bcf
"wand/spell/breath killer reason" so that wand of magic missile
zapped by a monster isn't ambiguous about who is responsible.
Setting protection from shape changers via wizintrinsics
caused a sanity error on the next turn, if any monsters affected
by it were in changed shaped on the level.
Call rescham after setting the timeout, so the monsters will
immediately revert to their own form.
Making the zeroing out of memory used by an object that is about to
be freed unconditional, and do the same for monsters. Should never
matter aside from an undetectable amount of extra overhead.
If standing at the location of a closed door while having Passes_walls
ability, attemping to open that door with 'o' reported "you don't find
anything here to loot".
Using 'o' to loot for direction ./>/< is intentional but it ignored
the possibility of there also being a closed door present. When the
latter applies, only switch from open to loot for direction '>' so
that '.' (and '<') will open the door. Doesn't matter for creatures
that can ooze under a closed door--trying to use 'o' gets rejected for
them because they lack hands.
Also allow hero in Passes_walls form to use 'c' when at open door spot.
(For creatures that ooze under doors, 'c' is rejected just like 'o'.)
Unrelated: fix a typo in a recently added comment.
While fuzzing, the game got stuck here trying to generate
a random stoneable monster for a Medusa level statue.
I didn't investigate deeper why it failed to find a matching
monster type.
Fix the duplicate feedback given when landing on one or more items
after teleporting out of lava.
This also avoids "you find yourself back on solid water" if you are
able to survive at a water location and safe_teleds() puts you on one.
relocate surface(), ceiling(), and avoid_ceiling() to dungeon.c
adjacent to has_ceiling() etc.
astral and fire, like airlevel and waterlevel return FALSE
for has_ceiling()
if a caller does happen to call ceiling() on fire level,
return "flames above"
if a caller does happen to call ceiling() on quest level,
return a more-generic "expanse above", instead of the
word "ceiling"
add "stairs" return to surface()
remove recent update to engrave.c to special-case "stairs"
since surface() will return that now
Consolidate a couple of makemon() calls. I thought that this would
end up being clearer but it isn't actually much of an improvement.
Should be no change in behavior.