Commit Graph

3748 Commits

Author SHA1 Message Date
Alex Smith
e6d44c68e8 Overhaul of priest donations
The old code had two main problems: a) it was very difficult for
unspoiled players to figure out how it worked (because donating too
much got you a bad result, and the exact amount you needed depended
on magic numbers that weren't stated in game, and because you had to
hide your visible gold to get a good result); b) for players who
knew the mechanics, it was somewhat exploitable and also somewhat
tedious to make use of (due to needing to hide visible gold before
donating).

This change preserves the spirit of the previous code whilst making
things more transparent for new players and less tedious for existing
players: the donation amounts for the various effects are still
roughly the same (but randomized), but the amounts you need to donate
for clairvoyance and for protection are explicitly stated (and as
before, the alignment reset is done by donating an unnecessarily
large amount and isn't explicitly stated as an option).  If you have
a lot of visible gold, you still need to donate a sizeable proportion
of it to get a useful effect, but now you get a larger reward to
compensate for the larger donation (to the extent that doing this
gives comparable results to doing it as a series of small donations,
removing the incentive to hide your gold before donating).

There's also something here for those players who like to squeeze
every last point of optimality out of a game: the "obvious" donation
strategy gives decent results, but players who are really willing to
dig into the mechanics may be able to find a way to get slightly
better results on average (which if I've balanced this correctly,
will lead to a very long and complicated spoiler).

One other change is that this is now based on your peak rather than
current level, to fix an exploit in which the character was drained
down to level 1 to donate a very large amount of gold (improving by
20 AC points) and then immediately restored back to the previous
experience level using a blessed potion of restore ablity.

This breaks save compatibility, but is being pushed together with
other save-breaking changes to avoid the need for multiple bumps to
EDITLEVEL.
2026-03-19 05:10:09 +00:00
Alex Smith
4d043a6f9c Monsters require experience using wands before they can hit with them
It takes time for an early-game monster to acclimatize itself to the
power of an attack wand: in much the same way as a nervous human may
quite possibly miss with their first attempt to use a gun in combat,
an early-game monster will always miss on its first use of an attack
wand (but from then on will understand how they work and get over
their nerves, and will hit as normal).

This is a balance change based on observed results from tournaments:
guarding against deaths to early-game attack wands requires an
unusually cautious playstyle which isn't much fun (and might not
always be possible even for the best players), so it is quite common
for them to be the cause of random deaths that it wasn't worth trying
to avoid. Although trying to dodge a monster who found an attack wand
is fun, you only actually get that fun if something makes you aware
of the danger: the monster missing with the wand is a clear way to
demonstrate the danger and let the player know that now is the right
time to take precautions.

This change could theoretically have broken saves, but probably
doesn't due to there having been a spare bit in struct monst.  Just
in case, it is being pushed together with other save-breaking changes
to avoid the need for multiple bumps to EDITLEVEL.
2026-03-19 03:55:10 +00:00
Alex Smith
07fc4904c6 Add a new wand, the wand of stasis
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.
2026-03-19 01:16:30 +00:00
nhmall
85e3d721fb Increase the value range of corpse weights
This breaks save compatibility, but is being pushed together with
other save-breaking changes to avoid the need for multiple bumps to
EDITLEVEL.
2026-03-18 23:32:26 +00:00
Alex Smith
5d3a1e684b Add shields of shock resistance and of drain resistance
These both appear as "wooden shield" when unidentified; so does the
small shield.
2026-03-18 23:27:54 +00:00
Alex Smith
ea023677c7 Work around MSVC false positive warning in price-tracking code
The MSVC warning assumes that any attempt to write - followed by an
unsigned number is an attempt to create a negative number (which
produces the wrong data type if you type, e.g., -2147483648).
However, it's also warning when an explicit U is given, e.g. in -1UL,
which is clearly not an attempt to express a negative value.

Suppress the warning by rewriting the number in question as (0UL-1UL)
with a binary rather than unary minus.
2026-03-18 23:27:54 +00:00
Alex Smith
4fae45220e Remember price quotes that have been seen for types of item
These are displayed in discoveries, and a new 'price_quotes' option
allows them to be displayed for un-IDed objects in other contexts
too (the idea is that you turn on the option while identifying
objects and off for general play).

Invalidates existing save files.
2026-03-18 21:08:22 +00:00
Pasi Kallinen
619de1ec0e Make monster destroy armor -spell erode armor first
Instead of outright destroying the armor, the spell will instead
first erode the armor.  The spell hits 2-4 times, so if it hits
the same armor 4 times, it will get destroyed.  This does not
hit erodeproof armor.

Also change the scroll of destroy armor, so that blessed one will
destroy a cursed armor, if hero is only wearing that.
2026-03-16 11:24:19 +02:00
PatR
a561538c2a cursed potions of invisibility for monsters
Extend the recently changed behavior for cursed potion of invisibility.

Monsters won't drink potions of invisibility if already invisible so
can't accidentally or voluntarily make themselves visible again, but
let player make them become visible by hitting them with thrown or
wielded cursed potion of invisibility.

They don't have any concept of temporary invisibility that might let
them remain invisible while losing permanent invisibility, so they
just lose the latter and immediately become visible.
2026-02-12 10:33:09 -08:00
nhmall
fcd9f5468c some coordxy follow-up
Return a couple of variables that actually held a direction back
to int from coordxy.

bhit() takes int params instead of coordxy.

boomhit() takes int params instead of coordxy.

xytod() renamed to xytodir(), and takes int params (promotion will handle
coordxy params).

dtoxy(coord *, int) renamed to dirtocoord(coord *, int).
2026-02-11 09:40:25 -05:00
Pasi Kallinen
37168ab188 Boomerangs can hit multiple monsters
The number of monsters it can hit depends on the enchantment.
2026-02-04 22:35:22 +02:00
Pasi Kallinen
060c3de8eb Lua tests: code coverage for applying some items
Add a new debug flag prevent_pline, which prevents all messages
from going out to the UI. This prevents the tests from stopping
for -more-.
Add rudimentary tests for applying whistles, camera, and stethoscope.
2026-01-27 17:33:08 +02:00
Pasi Kallinen
11bed1f55b Lua tests: generation of each object
Test generation of every object, both via des.object and obj.new.
Expose FIRST_OBJECT and LAST_OBJECT numbers to lua.
Add lua nh.int_to_objname, a function to convert integer value to
object base name and class.
Allow creating new nethack lua object by specifying id and class.
2026-01-26 18:00:33 +02:00
Alex Smith
253aea33fc Elemental Planes balance adjustments
The previous commit caused air elementals to become almost totally
nonthreatening in the endgame (even the buffed ones on the Plane of
Air). This commit fixes that (whilst still leaving them somewhat
weaker than they were before against characters with good AC), by
doubling the damage of home-plane air elementals.

The damage of the other home-plane elementals was doubled too,
because they were mostly nonthreatening previously. On Fire, this
has no real effect as almost any character would be fire-resistant
by this point. On Earth, it makes the elementals more of a threat,
when they previously weren't.

However, it made Water too difficult (albeit more fun, because it
became important to avoid letting water elementals swam you). As
such, water elementals are made slightly slower to compensate.
(My own playtesting indicates that 6 is slightly too fast, but 4 is
too slow, so I'm hoping that 5 is the correct value.)
2026-01-15 00:10:59 +00:00
Michael Meyer
c92014bf3c Follow-up to engraving punctuation
Skip the terminal period only if there is true punctuation at the end of
the engraving, not degraded text.  This feels a bit janky because the
way engravings are malloced and structured uses this manual offset to
access the space allocated for text.  I used a macro to unify all those
accesses so that it will be harder to screw it up if something changes
in that respect, since repeating (ep + 1) as a magic number across
engrave.c seems quite brittle.
2026-01-13 10:32:08 -08:00
nhmall
6459d44461 some spelling and inconsistency corrections; comments and elsewhere 2026-01-11 11:31:34 -05:00
nhmall
c016367d8c an old comment typo 2026-01-11 10:11:30 -05:00
Pasi Kallinen
d0b9846367 Move item actions into separate src file
Haven't tested compilation on Windows and VMS ...
2026-01-11 14:46:25 +02:00
Pasi Kallinen
96394394b8 Fix comment typo 2026-01-11 11:42:16 +02:00
nhmall
4b5b146674 should_displace() does not modify mfndposdata struct pointed at by data param 2026-01-04 11:54:28 -05:00
nhmall
44fcbb180d bump COPYRIGHT_BANNER_A 2026-01-04 11:16:45 -05:00
Pasi Kallinen
f2cd27551d Just pass a mfndposdata pointer to should_displace 2026-01-04 16:34:28 +02:00
Pasi Kallinen
37df18b59c Use struct to pass data out of mfndpos
Instead of passing two array pointers to mfndpos, fold the data
into a specific struct.
2026-01-04 14:00:43 +02:00
nhmall
7dc4512bb3 follow-up: just use existing carrying() 2025-12-09 15:25:10 -05:00
nhmall
6ba053669e follow-up for #name permanent inventory update
Using extended #name for an object on the floor (for example)
wasn't updating the permanent inventory to reflect the updated
object type name if there was also one in inventory.
2025-12-09 14:17:42 -05:00
Alex Smith
a0ccb4b0cb Show spells and skills in the dumplog
These are often an important part of a character's build. There's
no purpose in listing them in disclose because the player generally
already knows what spells and skills they had and doesn't need them
identified, but they're useful when looking at someone else's game
or reminiscing over a past game.
2025-12-02 19:19:18 +00:00
Alex Smith
656f58b099 A basic version of starting character rerolling
This adds a "reroll" option that lets players reroll their
character's attributes and starting inventory. Although I generally
think doing this makes the game worse, a) some players are going to
do it regardless and b) if a player is going for a challenge game,
rather than to win, it may be required. So in the absence of an
option like this, players repeatedly start and quit games instead,
creating a large number of junk logfile entries and generally
causing problems for other players on the same shared machine
(because repeatedly reloading the game is very CPU-intensive).

This should in theory be windowport-agnostic (although in practice
it may not be). Tested on tty, X11 and curses; on tty and X11 it
works fine (although X11 treats the change in attributes as
something that needs a status highlight), on curses it is slightly
jankier in terms of what other windows are drawn in the background
(but still plays correctly and I suspect this is a pre-existing
bug).

To form a complete implementation, we will need to consider the
following:

- Should there be a delay on a) starting the game and/or b)
  rerolling? If so, what should it be (maybe configurable via
  sysconf?)

- Should we take more steps to discourage players from rerolling?
  It would be bad if players see the option exists and turn it on
  just because it exists, or (worse) treat it as condoning the
  particular style of play.

- Should we take steps to detect that players are rerolling
  manually and a) tell them to use the option instead, b) tell them
  that this is not an intended way to play (and may make the game
  less enjoyable and/or prevent them getting the practice they need
  to eventually win)?

Breaks save and bones files.
2025-11-30 06:49:14 +00:00
Alex Smith
e428046ffa Restructure u_init to allow for inventory rerolling
This doesn't implement inventory rerolling, just adds the
infrastructure: it's now possible to call u_init_inventory_attrs
multiple times and the starting inventory/attributes replace those
from the previous call rather than compounding.
2025-11-30 04:52:13 +00:00
Alex Smith
4d55e1de79 Make saving grace also work against repeated damage sources
For example, being hit by the bounce of a wand of fire means that
the main character could take damage twice in a turn, which would
kill even through saving grace; and scrolls and potions could burn
up after that and finish off the last HP, even if the wand only hit
once. This commit changes it to track all damage done during the
turn, and prevent HP dropping below 1 from damage until the next
player action or the next turn boundary, whichever comes first.
2025-11-26 05:45:45 +00:00
Alex Smith
8c29b20010 Accurately track which items have been discovered, even if not #named
This fixes a couple of bugs: a long-standing bug in which writing a
scroll by label could fail even if you've already seen a scroll with
that label (due to the game not tracking whether or not you've seen a
scroll if it doesn't have a name); and a somewhat newer bug in which
spellbooks auto-identified by Wizard knowledge were marked as having
been encountered (rather than as known but not encountered).

Breaks save file compatibility, but not bones files.
2025-11-25 22:42:38 +00:00
PatR
7b5d7d7ae6 fix issue #1462 - objects embedded in trees
Issue reported by chappg:  on arboreal levels, when an object was
located at a stone location treated as a tree location, examining
the object would report it as embedded in stone.

The Ranger quest has arboreal levels where STONE becomes TREE, and
items that would become embedded in stone will be in trees instead.
(Sometimes kicking a tree would drop fruit onto an adjacent tree,
effectively embedding it.  For testing, it's easier just to poly
into a xorn, walk onto the tree spot, and drop something.)  The item
description code for farlook and quicklook wasn't checking for that.

The fix also corrects another bug:  an item located at a normal tree
location would just be described as itself with no mention of the
tree at all.  Attempting to walk onto it would report the terrain
and not let you move there (assuming not in xorn form), like trying
to walk into a wall.

Fixes #1462
2025-11-24 12:37:08 -08:00
Alex Smith
fce66245ca Don't attempt to cache encumber_msg result
There was only one point in the code at which this caching was
being done, and it was incorrect: it's possible for the result of
near_capacity to change during a monster turn because monster
actions can change either inventory weight or carry capacity.

The bug was particularly relevant in cases where a character
polymorphed into a slow weak monster gets attacked by a monster
that moves at normal speed: due to the polyform being slow, the
normal-speed monster gets in a lot of attacks and causes a
rehumanization, but due to the polyform being weak, it was
burdened at the start of the monster turn, and so when that
penalty is (due to the bug) applied to the next turn it can
mean that the character misses the next turn too, and may end up
dying as a result.
2025-11-24 02:07:23 +00:00
nhmall
d5658018ac alternative to display_inventory for window-port
Several window ports that support perm_invent were
using a call back to the core display_inventory()
function.

While calling from the window port back to core functions
is arguably not ideal in the first place, it was recently
brought to light that code NetHack-3.7 code changes to
display_inventory() actually caused it to stop repopulating
the perm_invent window as intended under certain circumstances.

For now, provide an alternative function, repopulate_perminvent(),
that hopefullshould still work the way it did previously.

There will likely be some additional changes after this to
further improve things, at some point.

For now though, this
Resolves #1454
2025-11-08 14:26:07 -05:00
nhmall
0f01260605 Revert "blank perm_invent after repeating spell cast"
This reverts commit 7763f4c5b0.
2025-11-06 18:44:12 -05:00
nhmall
7763f4c5b0 blank perm_invent after repeating spell cast
add mechanics to ensure display_inventory() refreshes perm_invent as
expected when update_inventory() is called from a repeat command.

Resolves #1454
2025-11-06 01:34:52 -05:00
nhmall
8b69a5aabb a few constants that don't need to be octal at all 2025-10-23 11:18:42 -04:00
PatR
966145a61d item action 'T' against covered armor
Using 'i'+menu choice for suit+'T' to try to take off a suit that is
covered by a cloak (or shirt covered by suit and/or cloak) wouldn't
do anything.  It should report that you need to take off the outer
garment first and then not take the chosen item off.

There is probably a simpler fix.  It took me a long time to figure
where things were going wrong and them cobble this together.

A big chunk of the diff for invent.c is just identation, surrounding
a one-line change there.
2025-10-20 13:29:42 -07:00
PatR
6f8c1127ed item action 'W' for armor
In the context-sensitive menu when picking an item of armor from an
inventory listing, distinguish between wear-this-armor from could-
wear-this-armor-if-something-else-wasn't-already-worn-in-its-slot.
2025-10-13 23:13:15 -07:00
nhmall
38161f3e4a relocate customization application
Resolves #1450
2025-10-06 11:49:31 -04:00
nhmall
e8e1868b70 part3 of 3 for GitHub issue #1441
> Perhaps related: when a wand of sleep hits a disguised already-sleeping
> mimic (about which it is a separate question if they should go into
> disguise when sleeping, is it supposed to be automagic or conscious
> effort for them? but I digress), the mimic is not revealed (should it?)
> but the message says "hits a mimic".

Adjust restrap() so that a revealed mimic won't disguise itself while
sleeping. This seems to be in keeping with mimic lore.

Also, normal shop sounds (chime of register etc.) will wake a
mimic up from indeterminate sleep.

Closes #1441
2025-09-05 12:37:40 -04:00
nhmall
602678aa5a follow-up: function name 2025-09-05 09:26:00 -04:00
nhmall
774129df11 another #1441 follow-up bit 2025-09-04 22:52:36 -04:00
nhmall
fe357a6f04 avoid telling hero they missed a monster they aren't aware of
Resolves #1441
2025-09-04 22:13:07 -04:00
nhmall
6fade4b184 don't split pudding that got jousted into a hole 2025-09-01 10:34:38 -04:00
nhmall
6c3e70ad77 remove stray tabs from *.c files and config.h 2025-08-19 08:35:39 -04:00
nhmall
070730d845 Qt6 wasn't exiting as expected after saving the game
Reported by email to devteam on Feb 1, 2025.
2025-08-17 01:33:24 -04:00
nhmall
b876381b72 add support for [[maybe_unused]] if available 2025-08-07 21:47:40 -04:00
nhmall
a0275e2696 consolidate some duplication into sfmacros.h
:  #include "sfmacros.h"
:  from sfstruct.c and sfbase.c
2025-08-06 12:56:07 -04:00
PatR
695c6ef3ac fix issue #1434 - engulfed gas spore explosion
Issue reported by Umbire:  a gas spore that got swallowed and killed
didn't die but exploded anyway, with the explosion affecting the map
instead of being contained in the swallower.

There was code to handle that but it wasn't being executed.  This fix
feels unclean but seems to work.

I couldn't reproduce the survival of the gas spore but since that
isn't wanted I won't worry about it.

Fixes #1434
2025-08-05 13:30:17 -07:00
PatR
c08f79b26e more photographing monsters
Don't record hallucinated monsters as having been seen up close or as
photographed.

Treat a tourist's starting pet has having been photographed prior to
bringing the camera and dog or cat into the dungeon.

No extra points to tourist when first long worm tail is photographed.

EDITLEVEL is incremented again, for extra context to track starting
pet.
2025-07-29 15:45:11 -07:00