The description of the default map display was out of date:
Sinks aren't conditional anymore, and were changed from '#' to '{'.
Statues were still listed as '`' but aren't displayed as that.
Room and corridor engravings weren't mentioned.
Wall of water and wall of lava weren't mentioned.
Drawbridge portcullis ('#' when closed) and span ('.' when open)
weren't mentioned.
This splits introductory "- and |" into two entries.
I've forced CR font (similar to TeX's tt font) for the initial
character of all the entries.
The formatting of letters for monsters left something to be desired
so I've tried to redo it. The 'roff edition seems ok (as least when
there's no page break in the middle of it) but I'm not sure how the
LaTeX version will fare. I didn't try to include the trailing "and"
on the first two lines the way the 'roff version does since I wasn't
sure how to accomplish that.
Fix misleading indentation that the polyfood() macro inherited from
the misformated polyfodder() macro.
Fix a case where a non-pet monster would avoid eating a cockatrice
corpse but would eat Medusa's corpse.
Pull request from entrez: rename polyfodder() to polyfood() and
use it instead of is_shapeshifter() when deciding whether a pet will
avoid eating a corpse or an egg because of the risk of polymorphing.
The PR was in response to issue #1183 by Umbire: pets that avoid
eating shapechanger corpses didn't avoid genetic engineer corpses.
polyfodder()/polyfood() includes genetic engineer corpses because
they have an attack for AD_POLY damage. But pets weren't caring
because is_shapeshifter() doesn't include that.
There's no particular need to rename polyfodder() to polyfood() but
I've used the PR as-is instead of reversing the name change.
Closes#1184Closes#1183
The previous is_shapeshifter() test meant that pets were still perfectly
willing to eat genetic engineer corpses and be polymorphed. Use
polyfood() instead.
Fixes#1183.
'Polyfodder' is already a term frequently used by players to describe
items which are useful to hang on to specifically to zap polymorph at
(for example, extra unicorn horns, which can be turned into magic
markers). Using it as the name of a macro which tests whether a food
item will polymorph the creature consuming it is somewhat confusing, and
I think 'polyfood' is a lot clearer.
player's input as a comma-separated list of option:value settings
Several compound values that aren't amenable to menus prompt for a
line of input and pass it to parseoptions() as if it came from the
run-time configuration file. That shouldn't be treating commas as
option separators.
The fix is trival, at least for handling the text properly without
introducing new warnings to complain about rejections. Some options
notice an unwanted comma and complain, others don't notice and the
extra text gets ignored.
The look-at-screen code was setting bhitpos before calling
look_at_monster() but that hasn't been needed for around 25 years.
Fix is trivial.
However, having such a low level routine as show_glyph() call
do_screen_description() seems like a recipe for trouble.
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
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
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.
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.
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.
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.
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.
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.
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.
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, ...).
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.
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.)
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.
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.
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
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
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
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.
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".
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.
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#1229Closes#1250
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.
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>.