Commit Graph

10665 Commits

Author SHA1 Message Date
PatR
9bdc41b263 more mhitu.c style/formatting 2026-04-03 08:37:14 -07:00
Pasi Kallinen
8c55e7df1c Code style nit 2026-04-03 12:59:09 +03:00
Pasi Kallinen
b2a80ab87a Prevent phaseable monster hiding deep in undiggable walls
The outer edge of a random dungeon level can have undiggable
walls.  Phaseable monsters, such as earth elementals, could
hide deep inside that boundary.  Turn the walls beyond the
first layer of non-diggable walls also non-phaseable.
2026-04-02 15:43:13 +03:00
Pasi Kallinen
542d1db5f4 Another fix for #K4317 - monster grudge
Undead monsters created by the level creation routine do not grudge
other (zombifiable) monsters created during the level creation.

This of course doesn't prevent the grudge happening with monsters
created during gameplay.

Invalidates saves and bones.
2026-04-01 19:24:43 +03:00
nhmall
e377591240 follow-up: avoid issue with onefile concatenation 2026-04-01 10:59:22 -04:00
nhmall
ab459265b0 top line fixup 2026-04-01 09:09:39 -04:00
nhmall
686618f34d move early arg and enum dump processing to own file 2026-04-01 08:32:13 -04:00
Pasi Kallinen
3917f5493d More mcastu code reorg
Instead of using two separate functions with switch-cases for
wizard and clerical spell lists, define the spell lists
as arrays and use a single function to pick a spell
from the lists.

Adds levels to the monster spells, using the switch-case values,
with some minor fudging.
2026-04-01 13:17:09 +03:00
PatR
c8e97527f1 Schroedinger's live cat
Give a little experience when releasing live housecat from Schroedinger's
Box, similar to recent change giving experience when opening the Box
produces a dead cat.
2026-03-31 20:48:01 -07:00
nhmall
90db8d7e2d clear up some warnings appearing with gcc 15.2.0
allmain.c: In function ‘debug_fields’:
allmain.c:1133:16: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
 1133 |     while ((op = strchr(opts, ',')) != 0) {
      |                ^
cfgfiles.c: In function ‘find_optparam’:
cfgfiles.c:590:10: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  590 |     bufp = strchr(buf, '=');
      |          ^
cfgfiles.c:591:10: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  591 |     altp = strchr(buf, ':');
      |          ^
end.c: In function ‘should_query_disclose_option’:
end.c:482:14: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  482 |     if ((dop = strchr(disclosure_options, category)) != 0) {
      |              ^
invent.c: In function ‘loot_classify’:
invent.c:175:7: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  175 |     p = strchr(classorder, oclass);
      |       ^
o_init.c: In function ‘dodiscovered’:
o_init.c:774:33: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  774 |     if (!flags.discosort || !(p = strchr(disco_order_let, flags.discosort)))
      |                                 ^
o_init.c: In function ‘doclassdisco’:
o_init.c:907:33: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  907 |     if (!flags.discosort || !(p = strchr(disco_order_let, flags.discosort)))
      |                                 ^
objnam.c: In function ‘the’:
objnam.c:2200:19: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
 2200 |         if (((tmp = strrchr(str, ' ')) != 0 || (tmp = strrchr(str, '-')) != 0)
      |                   ^
objnam.c:2200:53: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
 2200 |         if (((tmp = strrchr(str, ' ')) != 0 || (tmp = strrchr(str, '-')) != 0)
      |                                                     ^
options.c: In function ‘optfn_disclose’:
options.c:1529:17: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
 1529 |             dop = strchr(disclosure_options, c);
      |                 ^
pager.c: In function ‘add_cmap_descr’:
pager.c:1212:28: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
 1212 |                     || ((p = strchr(x_str, ' ')) != 0 && !strcmpi(p, " ice"))
      |                            ^
2026-03-31 10:16:28 -04:00
Pasi Kallinen
9bf90ee07f Replace magic number with define 2026-03-30 19:07:18 +03:00
Pasi Kallinen
f4173adc88 Use define for player name length in botl
This defines the cut-off how many characters of the player's name
is shown in the bottom status line.
Also increase the limit from 10 characters to 16.
2026-03-30 16:41:37 +03:00
Pasi Kallinen
9fba4af323 Status hilite menu retry limit
Fuzzer got stuck here for a very long time, so bail out
from the status hilite menu after few failed tries.
2026-03-30 13:29:12 +03:00
PatR
1813e27098 farlook of quest stairs down
When access to the quest isn't available yet, describe the stairs down
as "blocked staircase down" instead of the usual "staircase down".
Applies to mimics posing as stairs too.

Does not apply to the stairs when standing on them and using lookhere.

I was going to use "locked staircase down" but that would imply that a
key or unlocking magic could be applicable.
2026-03-29 14:02:21 -07:00
Pasi Kallinen
e3e01267aa Debug panic when level has too many (sub)rooms 2026-03-29 18:47:55 +03:00
PatR
f49ac037f9 release wish history memory
The memory leak only happened at end of game so wasn't a big deal.
2026-03-25 15:54:44 -07:00
Pasi Kallinen
f8bd79354c Add a history menu for wizwish
Use the 'm' Prefix to make wizwish show the history menu.
Also entries wished via WIZKIT are added to the history.

While debugging, I often need to wish the same thing multiple
times, and typing or pasting it with mouse is annoying...
2026-03-25 13:46:07 +02:00
Pasi Kallinen
41b2087436 Hero has a small chance of catching items thrown at them 2026-03-25 09:35:39 +02:00
Pasi Kallinen
d78af5cec7 Add monster spell header file mcastu.h
Move the monster spell definitions there, and use hackery
(similar to objects.h) to generate enum and data from
the header file.

I have not tested Windows, VMS, or Amiga builds.
2026-03-24 17:26:24 +02:00
nhmall
d929adc85c warning fix in sp_lev.c
In function 'create_monster',
    inlined from 'lspo_monster' at sp_lev.c:3385:5:
sp_lev.c:2169:32: warning: 'tmpmons.m_lev_adj' may be used uninitialized [-Wmaybe-uninitialized]
 2169 |             if (mtmp->m_lev + m->m_lev_adj > 49)
      |                               ~^~~~~~~~~~~
sp_lev.c: In function 'lspo_monster':
sp_lev.c:3217:13: note: 'tmpmons.m_lev_adj' was declared here
 3217 |     monster tmpmons;
      |             ^~~~~~~
2026-03-24 10:05:48 -04:00
nhmall
aa90cd5cd8 Merge branch 'amiga-port' of https://github.com/ingpaschke/NetHack into NetHack-3.7 2026-03-24 05:37:17 -04:00
Ingo Paschke
2d597cb9fa Revive Amiga port for NetHack 3.7
Update the Amiga Intuition window port (AMII/AMIV) for the 3.7
window_procs API. Key changes:

- Update all window function signatures for 3.7
- Add assembly trampolines for AmigaOS register-based callbacks
- Convert all K&R function definitions to C99
- Add cross-compilation build system (cross-pre1/pre2/post.370)
  using bebbo's m68k-amigaos-gcc with -noixemul -std=gnu17 -m68000
- Clipping fixes: viewport centering, simplified ScrollRaster,
  duplicate Ctrl-R suppression, glyph buffer invalidation
- Add menucolor support in menu rendering
- Move native txt2iff.c and xpm2iff.c to outdated/
- Add nethack.cnf and README.amiga
2026-03-23 20:48:06 +01:00
Ingo Paschke
1ca15d99ff Fix clipping: move cliparound after vision_recalc
cliparound() was called before rhack(), triggering a map redraw with
stale vision data followed by a second correct redraw. Move it after
vision_recalc() so the map is redrawn once with correct data.
2026-03-23 20:46:31 +01:00
Pasi Kallinen
d80e194735 Split mcast_spell into separate functions 2026-03-23 14:51:48 +02:00
Pasi Kallinen
c0f5d2dd92 Allow monster level adjustments in special levels
Add a new parameter to des.monster, m_lev_adj, which is a level
adjustment for the monster.  This only applies to the monster's
level, so basically only affects the spellcasting, it does not
change the monster's hit die or inventory.

Change one of the shamans in Orctown to be 3 levels higher.
2026-03-23 12:51:50 +02:00
nhmall
1ba9be09fa pre-C23 build fix
Prior to C23, a statement is required after a label.
2026-03-23 06:42:11 -04:00
Pasi Kallinen
d6cbaca654 Reorganize mcastu code
Merge the two different enums, so all monster spells are in one
list.  This should have no functional difference, but cleans up
the code a bit.
2026-03-23 11:42:54 +02:00
PatR
10eedbe2f0 fix #S15496 - #chronicle oversight
Add a livelog/#chronicle message for quest leader opening the quest.

A similar message for being expelled doesn't seem possible to be
triggered.

Log the initial visit to each level in the quest branch.  They record
when the hero actually visits the quest levels, beyond the new one
about permission to do the quest.
2026-03-22 22:01:40 -07:00
nhmall
f9cebc5890 avoid a warning with Xcode build 2026-03-22 11:33:26 -04:00
Pasi Kallinen
8e4c7f9fb5 Change some command keys
Change 'v' from #versionshort to #chronicle.
Change 'V' from #history to #versionshort.
History can still be accessed either directly with the extended command,
or via the help menu.
Versionshort now accepts the m-prefix, and then shows the longer version.
2026-03-21 17:56:37 +02:00
nhmall
1eadfa962d don't include size_t * and time_t * in _Generic for now
There are behavior differences with macOS and other plaforms that
would need to be addressed if they were included.
2026-03-21 11:11:28 -04:00
Pasi Kallinen
9fc1514253 Amulet of magical breathing increases energy regen
Normally energy regen is 1d2 or 1d3, so wearing the amulet increases
that to 1d4 or 1d5.  The only way to get energy regeneration is via
The Eye of the Aethiopica, so you cannot have both at the same time.
2026-03-21 13:20:14 +02:00
Pasi Kallinen
63a78edcfa Add toggle extended command
Allows the user to configure a key binding to toggle any boolean
option, for example:

BIND=':toggle(price_quotes)
BIND=v:toggle(autodig)

The option must be settable in-game.
2026-03-21 12:17:37 +02:00
Alex Smith
62a50413d3 New debug command #wizobjprobs for listing item probabilities
As of the change to allow for item probabilities that don't add up
to 1000, it's become a little difficult to figure out the exact
probabilities from the source code, which makes it hard to balance
item generation. Adding a tool to list the probabilities helps.

Part of the problem is that changing an item's probability without
balancing it elsewhere is usually wrong: doing that would in effect
take the probability equally from (or add the probability equally
to) all other items in the class, which might break the balancing
of those items due to the probability change.

As such, it is usually better to make an intentional decision about
which items should be less and which items should be more likely to
generate, then change them in a balancing way (meaning that the
probabilities of objects that weren't intentionally changed remain
unchanged). Doing a complex such change makes arithmetic errors
fairly likely, though, so it's useful to have a command that verifies
that it's been done correctly.

This command is primarily intended as a development aid, so it's
included only in debug builds and pre-release builds (the same as
other similar commands like #wizmondiff).
2026-03-21 05:05:48 +00:00
Alex Smith
26fd5e7d73 Armor probability tweaks
Armor is now slightly more likely to generate outside Gehennom (at
the expense of gems that generate via random generation rather than
mineralisation or level rules).

Basic nonmagical armor (especially body armor) has had its generation
probabilities reduced *relative to other armor*, but outside
Gehennom, it is no less likely to generate (because armor in general
is now more likely to generate outside Gehennom). It is slightly less
likely to generate in Gehennom (but isn't typically needed in
quantity there).

Armor that has extrinsics is more likely to generate, both due to
having increased probability relative to other armor, and due to the
increased proportion of generated items being armor: this is the
primary goal of this change.

The intention behind this change is to increase the chance that
players naturally find useful armor (especially armor that they might
not have been planning to use, but that they can adapt their strategy
to make use of), rather than needing to wish for it: the chance of
finding useful armor is higher both in the Dungeons (due to the
increased probability) and in Gehennom (because it is more biased
towards armor that might be useful at that stage of the game).  In
practice, in 3.6.x (prior to this change and to wishing changes), it
was quite common for players to wish up an entire set of armor at the
Castle, ignoring almost everything they'd found so far that game; I'm
hoping this change encourages more wish variety rather than spending
the majority of wishes on armor.
2026-03-21 04:52:51 +00:00
Pasi Kallinen
f85b5b1c9d Fix segfaults from the key binds change
Should've checked rest_on_space behaviour ...
2026-03-20 22:49:29 +02:00
Pasi Kallinen
c595f241e6 Change key binds from array to linked list
Key bindings were stored as a fixed-size array, indexed by the input
character, pointing to the extended commands.  This changes that into
a linked list of an intermediary struct Cmd_bind, storing the input key
and the pointer to the command.

This is just code cleanup for future enhancements, and should have
no effect on gameplay.
2026-03-20 19:57:44 +02:00
nhmall
b33b09bcac whitespace cleanup: tabs to spaces 2026-03-19 20:22:24 -04:00
PatR
e38377e899 some reformatting for read.c 2026-03-19 16:29:11 -07:00
PatR
aafc414a9f remove inaccurate comment
Floating eyes aren't the only monsters that pass the is_floater() test.
Other 'e's aned 'y's do too.
2026-03-19 15:55:08 -07:00
nhmall
9aa2f61058 limits.h follow-up 2026-03-19 10:02:10 -04:00
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
Alex Smith
6886b199be Teleport traps don't teleport you on no-teleport levels
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.
2026-03-19 01:01:46 +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
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
9877e52025 Ring of stealth prevents hero from leaving tracks 2026-03-18 18:04:33 +02:00
Pasi Kallinen
18cb293766 Replace magic number with define 2026-03-18 17:12:01 +02:00
Pasi Kallinen
66e7123678 Categorize more commands as general commands
"general commands" tend to be ones which take no game time,
or are non-diegetic.
2026-03-17 13:38:12 +02:00