Commit Graph

17020 Commits

Author SHA1 Message Date
SHIRAKATA Kentaro
82e6eacfbb split returning from throwit() into separate function 2024-07-13 19:38:23 -07:00
nhmall
0eb7f109e0 follow-up, program_state 2024-07-13 16:31:35 -04:00
PatR
72d2b0414c fix github issue #1257 - nymph theft while blind
Issue reported by Meklon2007:  some theft feedback during nymph
attacks refers to the attacker as "she" and others as "it" if hero
is blind.

The "she" references are intentional.  However, mixing them with
"it" references when a series of messages occurs is jarring.  This
changes "it" to "someone", which is still different from "she" but
hopefully enough less so to be tolerable.

That resulted in monkeys also being referred to as "someone" because
they're classified as humanoid.  Change x_monnam()'s AUGMENT_IT
handling, which chooses between "someone" and "something" when the
monster is not seen, to override humanoid for animals (affects 'Y'
class) and for mindless (affects zombies, mummies, and golems).
So an unseen monkey will be "it" again.

The final message for current item was relying on a cached monster
name value.  If an unseen nymph or monkey stole a worn blindfold
so that hero's vision was restored:  "It <stole> item" before the
changes and "Someone|Something <stole> item" after.  So update the
cached name if sight gets regained, to give "<Mon> stole <item>."
(If the item is worn armor and the thief is a nymph, it was and
still is "She stole <item>".)

The message for having any worn item be stolen, which got split
into two parts within the past year, was giving "<Mon> takes off
<alt weapon>".  When not dual-wielded, the alternate weapon isn't
really worn.  Rather than suppress it outright, change the message
for uwep/uswapwep/uquiver to say "disarms" instead of "takes off".
For accessories, change "takes off" to "removes".  Those are more
or less interchangeable these days but "removes" matches R instead
of T.

While testing, my pet evidently killed a nymph (I was blinded and
couldn't see it happen) while she stole my gloves and the next
message I got was "You finish taking off your suit."  The gloves
weren't worn anymore so equipname() defaulted to suit.  Get rid of
equipname() altogether and switch to armor_simple_name() which
doesn't rely on the worn-armor pointers.

Fixes #1257
2024-07-13 12:30:59 -07:00
nhmall
6c0ae092c6 distinguish global variables that get written to savefile
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
2024-07-13 14:57:50 -04:00
PatR
0e4083153c another EDITLEVEL increment
Since save and bones files just got clobbered, make another clobbering
change.  The 'queuedpay' field added to shop's ESHK(shkp)->bill[] was
replaced last week by a similar field in the transient itemizing bill.
At the time, the old field was left in place to avoid incrementing
EDITLEVEL.

shkp->mextra->bill[] is saved and restored, so its queuedpay field was
too.  Eliminate that no longer used field.

Unrelated:  bill[]->useup is declared as boolean but being assigned 1
or 0.  Change the assignments to use TRUE or FALSE.
2024-07-11 10:22:40 -07:00
Patric Mueller
b844632dfd bump EDITLEVEL for 0447a1f10
Commit 0447a1f10 accidentally broke save compatibility.
2024-07-11 16:35:24 +02:00
PatR
0b30a7b18a farlook vs "wall of lava"
Implement a couple of missing bits for wall of lava terrain.  It was
immune to being distorted by hallucination, unlike molten lava and
wall of water.  And the presence of wall of lava made molten lava,
after being shortened to "lava", no longer be listed as something
represented by the "}" character.

I started to renumber S_water, which would eliminate some hackery
from farlook's do_screen_description(), but that will require an
EDITLEVEL increment and make it necessary to reorder/renumber the
corresponding tiles so I stopped short.

This adds NHDT tags to the first line of defsym.h.
2024-07-09 15:49:48 -07:00
PatR
a59cd71540 nowrap_add() vs final score
The old nowrap_add() had different semantics from its replacement,
performing an assignment as well as an addition.  Now just does
the addition; its caller needs to perform the assignment.
2024-07-09 12:42:45 -07:00
Pasi Kallinen
cf44c3046e Join wall spines with walls of water and lava 2024-07-09 18:08:19 +03:00
PatR
ee08c05e03 quest leader|guardians seeing hero attack peaceful
If the quest leader observes the hero attacking a peaceful monster,
only become angry if that peaceful monster is a quest guardian.  And
when becoming angry, stop waiting for the hero to approach.

If a quest guardian observes the hero attacking any peaceful monster,
don't run away.
2024-07-09 02:48:05 -07:00
PatR
e6ccd734e5 yn_function() again
The new impossible() was testing for a NUL response incorrectly.
2024-07-09 01:55:55 -07:00
PatR
49a669a863 yn_function() sanity
Add the impossible with slightly less detail.  With any luck paxed's
magic debugger can track down what is happening with the canned
response queue.
2024-07-08 13:50:26 -07:00
PatR
0598880bb5 minor shk.c comment fix 2024-07-08 02:52:40 -07:00
PatR
8073c40477 redo nowrap_add()
Yahoo!'s mailer delivered the report about nowrap_add() to my spam
folder, apparently because it thinks that the signature attachments
"may contain harmful content".  :-(

nowrap_add() checks for signed overflow after the fact, so after
undefined behavior if that happens.

This rewrites nowrap_add() and moves it from end.c to integer.h.

I haven't generated any values big enough to exercise it, but the
algorithm is straightforward so I'll take it on faith.
2024-07-07 17:34:37 -07:00
PatR
0447a1f107 band-aid for fuzzer crash in doclassdisco()
This should prevent the unexplained situation in doclassdisco(the
back-tick command) from triggering a crash, but doesn't solve the
underlying problem.  And the new impossible() will be escalated to
panic() by the fuzzer, so will still cause it to die.

Still no idea why the class input letter 'c' ended up with value
'I', leading to 'oclass' being MAXOCLASSES and going out of array
bounds during during doclassdisco()'s final loop.
2024-07-07 16:43:55 -07:00
nhmall
fbda1183d7 comment bit 2024-07-07 10:40:03 -04:00
PatR
40716b7b0a fix #K4199 - dereference of null firstmatch pointer
Bumping into something (by fuzzer) with mention_walls On crashed
when trying to display output generated by do_screen_description().
I wasn't able to reproduce this but didn't spend much time trying.

Without a test case I'm only guessing that this fix solves the
problem.  If there was a monster phazing at the 'wall' location
or an object embedded there, the screen description wouldn't be
appropriate for trying to describe the terrain.  (I don't think that
the pass-walls monster csse would reach the affected code but the
embedded object case might.)

Use the background glyph calculated for the map, which yields the
intended "stone" instead of "wall" when outside a not-yet-mapped
wall, rather than the displayed glyph.
2024-07-06 17:00:30 -07:00
Pasi Kallinen
d4ac146c5f Pets avoid a possible boulder pushing location in Sokoban 2024-07-06 23:12:19 +03:00
PatR
a33f1edd24 YMonnam(monst)
Replace several upstart(y_monnam(mon)) with new YMonnam(mon) to
produce "Your little dog" and such.

Also change one or two Monnam(mon) to YMonnam(mon) and one pline(...)
to pline_mon(mon, ...).
2024-07-04 14:27:28 -07:00
PatR
0e46439814 fix add_one_tobill() 'fixme'
Not exhaustively tested.
2024-07-04 14:19:40 -07:00
PatR
10c85d68bb fix #K4172 - selling all into container
When using #loot to put items into a shop-owned container on a shop's
floor, you are asked "Sell it? [ynaq] (n)" for each item, but the 'a'
and 'q' choices only worked as y or n for the current item.  By the
next one, the preferred answer had been reset to default and ynaq was
asked again.

Set a flag in use_container() to have in_container() set the sell vs
don't sell state for the first item but not for any others.  Reset
the state at the end of use_container() instead of after each item in
in_container().

This bug was present in 3.6.x, also in 3.4.3, and probably earlier.
2024-07-03 23:28:05 -07:00
PatR
7fa328fda3 redo itemized shop billing for containers
Finish shop changes begun in 2674a9904d.

Fix the longstanding bug where shop paying with itemized buying would
reveal container contents if any unpaid items were inside containers,
regardless of whether the containers' contents were known yet, even
when the container was a locked box/chest or a cursed bag of holding.
Paying by menu made that be more noticeable but it has been present
ever since itemized paying was introduced.  I can't find any old bug
reports for it though.  I did find an old message of mine that claims
it's in bugzilla with a "#Hnnnn" tag.

This changes how buying containers and their contained items behaves.
It's now an all or nothing operation.  Itemized billing will list
the container but not contents, and to buy what is inside you need
to pay for the whole thing as a single unit.  If the container itself
is unpaid then its price is part of that item's total cost.  If it is
hero-owned than it is listed as an item to buy but doesn't increase
the cost derived from its unpaid contents.  (If you decline to pay,
hero will still own the container and still owe for unpaid contents.)
2024-07-02 14:52:49 -07:00
PatR
14d0e48e73 less verbose sanity checking
If the 'sanity_check' option triggers a warning, don't show the
"Program in disorder!  (Save and restore might fix this.)" and
"Report these messages to <devteam>." messages and also don't run
the crash report submission.

Doesn't affect the fuzzer because it escalates impossible() to
panic() before reaching those extra messages.
2024-07-01 00:44:42 -07:00
PatR
dc9d0e279f suppress sanity_check for invalid command ESC
If sanity_check starts spewing out warnings, don't run it again if
player types ESC when the program finally asks for the next command
(rather than --More--).

Also, some reformatting of the main command table.
2024-06-30 17:33:41 -07:00
PatR
02ddbf001b shk.c menu-pay comments 2024-06-28 09:04:55 -07:00
PatR
17e0385f0e shk.c and priest.c vs 'onefile' 2024-06-28 08:31:49 -07:00
PatR
2674a9904d github issue #1249 - menu pay of contained items
Issue reported by ars3niy:  unpaid containers containing one or
more other unpaid items appear on the menu for 'p' along with
separate menu entries for those other items.  Buying the container
buys the contents too, then if any of the contents were also picked
in the menu, an attempt will be made to pay for them separately.
Since they are no longer on the bill by that point, that triggers
an impossible warning.

This fixes that by paying for the container but not its contents.
(Temporarily, until more extensive changes get implemented.)  Then
those contents will still be on the bill.  It they've been chosen
in the 'p' menu, paying for them will work as expected.

This also fixes the pay menu for the case where the bill contains
any instances of a partly used up portion of some stack and also
the unpaid intact portion of the same stack.  With itemized billing,
you are allowed to buy the used up portion separately, then maybe
drop the unpaid portion.  (If you try to pay for the intact portion,
the shk won't accept the payment and will tell you to pay for the
used up portion first.)  With the recently added menu for billing,
they were lumped together as a single item and you had to pay for
their whole stack.

And it fixes a much older bug dealing with the cheapest item on
the shop bill.  If you don't have enough to pay for that, the rest
of buying gets skipped.  But stacks that had used up and intact
portions were lumping those together instead of separately checking
the two portions for possibly being the cheapest, so it was possible
to have enough gold to pay for one portion but be told that you
couldn't affort to pay for anything.  If it was the intact portion,
you wouldn't be able to buy it anyway, but if the cheapest item was
used up portion, being told you didn't have enough gold was wrong.

This commit does not fix the longstanding bug that itemized billing
reveals the contents of containers which haven't been opened.  It
was intended to do so but I've run out of steam.

(There is groundwork for that, where buying a container would
include payment for its unpaid contents without revealing what those
are, and they couldn't be purchased separately unless they get taken
out of the container.  Uncommenting '#define CONTAINED_BUYING' will
enable it, with updated pay menu handling but without being able to
pay for non-empty containers.)

Fixes #1249
2024-06-27 14:13:39 -07:00
PatR
0604b3ea22 github PR #1259 - counts in curses menus
Pull request from mkuoppal:  curses menu handling could go out of
bounds accessing array groupaccels[] if strange input gave a false
positive for STDC's isdigit().  Discovered by debug fuzzer.

Failure was triggering an error by the undefined behavior sanitizer.

[randomkey() ought to return int rather than char.]

Closes #1259
2024-06-22 23:40:27 -07:00
Mika Kuoppala
238d501cce win/curses: Check input range in menu selection fallthrough
curletter is screened to be in valid range in all
other switch/case branches except in the default case,
which is fallthrough.

Add check for valid range in here also, otherwise
array might be addressed with invalid offset.

This should fix the following, found
with UBSAN and #debugfuzzer:

../win/curses/cursdial.c:1558:49: runtime error: index -154 out of bounds for type 'char [256]'
0x5f3857eff140 in menu_get_selections ../win/curses/cursdial.c:1558
0x5f3857ef78c8 in curses_display_nhmenu ../win/curses/cursdial.c:801
0x5f3857edd390 in curses_select_menu ../win/curses/cursmain.c:768
2024-06-22 23:31:22 -07:00
PatR
54138bbbe3 'Ix' fix
This took me a while to track down.  I noticed it while drinking
unpaid potions but didn't expect the issue to be potion-specific.
Affects paying shop bill in addition to examining [former] inventory.

Start with a stack of 3 unpaid potions.
Iu
a - 3 potions of healing (unpaid)
Ix
no used up items

Drink one.
qa
Iu
a - 2 potions of healing (unpaid)
Ix
x - potion of healing

So, far everything's normal.  Note that 'x' is an arbitrary letter
used for expended items when shown in inventory style rather than an
inventory letter or menu choice.

Drink another.
qa
Iu
a - a potion of healing (unpaid)
Ix
x - potion of healing
x - potion of healing

Drink the last one.
qa
Iu
no unpaid items
Ix
x - potion of healing
x - potion of healing
x - potion of healing

In 3.4.3, these last two Ix cases would have had single lines of
'x - 2 potions of healing' and 'x - 3 potions of healing', respectively.

After this fix, they will again--unless potion stack 'a' was wielded,
readied as alt-weapon, or quivered.
2024-06-21 15:32:46 -07:00
PatR
bf1e3d3e56 more obj->where == OBJ_DELETED 2024-06-21 14:26:48 -07:00
PatR
d5d646a9e9 ancient comment typo/thinko
Change "it gets size gets reduced" to "its size gets reduced".
This was present in 3.4.3, doubtless even ealier.
2024-06-21 13:32:21 -07:00
PatR
07de17d747 obj->where == OBJ_DELETED
The list of possible object locations used when formatting obj->where
wasn't updated when the objs_deleted list was introduced.  If object
sanity checking ever tried to report it for something, it would have
been described as "unknown[9]" rather than as the intended "deleted".
2024-06-21 12:57:31 -07:00
PatR
0cc134bb95 \#force feedback
A fairly recent change to improve the message given when hero isn't
able to force a locked chest due to type of weapon or weapons being
wielded had one of the text substitutions backwards.  If wielding 1
dart, it gave
|You can't force anything with those weapon.
and if wielding more than 1,
|You can't force anything with that weapons.
2024-06-18 14:12:21 -07:00
PatR
a2c2e0b1c6 fix github issue #1250 and #1229 - ^A issues
Reported by ars3nly as "#1250: Repeating #sit causes a sitting loop",
with a followup comment describing how to reproduce easily, and by
Umbire as "#1229: Curses and extended command menus".  Repeat count
from previous command carried over to current command when ^A was
used to re-run the current one.

Reset 'last_command_count' every time a repeat count is obtained,
even when the new one is 0.  This is a much simpler fix than what
was used with several previous attempts, but it seems to be working.

The do-again code is convoluted, but the tricky bit was the fact
that this problem only happened when number_pad was On with repeat
counts entered as 'n<digits>'.  I still don't understand that aspect,
but it wasn't happening for count of simple '<digits>', making
reproducing it by someone who doesn't use number_pad be difficult....

Closes #1229
Closes #1250
2024-06-17 15:17:50 -07:00
Pasi Kallinen
078e22be2e Query menu for hide or spin web monster ability 2024-06-17 18:12:12 +03:00
PatR
c2f717cd65 altmeta vs number_pad
While testing a potential fix for issue #1250, I discovered that
using the altmeta option, to simulate typing Alt+x to get M-x via
typing 'ESC x', didn't work after a count prefixed by 'n' when in
number_pad mode.  It did work as intended for !number_pad when a
repeat count prefix is entered via digits without 'n'.

This doesn't solve #1250, which also occurs for number_pad but not
for !number_pad.
2024-06-17 04:23:34 -07:00
Pasi Kallinen
78bf0db5b3 Fix copypaste error 2024-06-15 19:04:57 +03:00
Pasi Kallinen
89ea47f702 Monsters can track hero through fixed teleport traps
Also make fixed teleport traps always trigger when entered.
2024-06-15 18:44:30 +03:00
PatR
4e1b6411bf comment thinko 2024-06-14 12:22:16 -07:00
Pasi Kallinen
951401e52a Allow fixed-destination teleport traps
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>.
2024-06-14 19:50:20 +03:00
Pasi Kallinen
a8099edca6 Accessibility: message locations 2024-06-14 13:33:48 +03:00
PatR
24e06171db fix issue #1256 - no_of_wizards incorrect \
if Wizard escapes the dungeon

Reported by vultur-cadens:  a fix to prevent quest feedback when quest
nemesis is removed from the game during bones creation introduced a
regression for an earlier fix that kept context.no_of_wizards up to
date if the Wizard of Yendor escapes the dungeon without dying.

Change 'wizdead()' to 'wizdeadorgone()' and call it from m_detach()
for mongone() as well as for mondead().

Fixes #1256
2024-06-13 12:18:17 -07:00
Pasi Kallinen
30d2eeea41 Accessibility: more monster message locations, pt 2 2024-06-13 19:41:41 +03:00
Pasi Kallinen
6c1a883f90 Accessibility: more monster message locations 2024-06-13 19:27:34 +03:00
Pasi Kallinen
a72b95e4bc Accessibility: more message locations
Add a new pline_mon() which sets the message location
to the monster location.

Add locations to several trap messages.
2024-06-13 19:04:33 +03:00
nhmall
a86b9e6899 update tested versions of Visual Studio 2024-06-13 2024-06-13 09:05:01 -04:00
PatR
73f46845ca refine wizard mode throne #sit 2024-06-12 12:35:08 -07:00
PatR
9896a81d6c wizard mode control of throne sitting outcome
When debugging, allow player to choose which outcome for #sit on a
throne (just 1 to 13; no menu; player has to check source to figure
out what the values mean).  Also when the chance to use up the throne
afterward comes up, prompt whether to honor it (similar to drying up
a fountain).

Added to ease testing for github issue #1250 but I still haven't been
able to produce anything odd with ^A after #sit whether or not it was
preceded by a counted action that got interrupted before the count
expired.
2024-06-11 14:15:33 -07:00
PatR
b9baaeb139 comment thinko 2024-06-10 12:09:19 -07:00