Makes Sokoban far less tedious when you don't have to worry about
monsters randomly popping up in the trap hallway while you're pushing
the boulder.
Adds a new exclusion zone for monster generation, and the goodpos
routine avoids the zones when GP_AVOID_MONPOS is used.
The role, race, gender, and alignment string values are 3 letters
preceded by a dash. I was looking at "name-race-role-gend-algn"
and mistakenly treated them as 4 letters preceded by a dash.
Fixing that changes PL_NSIZ_PLUS and there is one item of that size
written into save files, so the fix invalidates existing save files.
Instead of a menu listing
a - hero1
b - hero2
n - New game
q - Quit
show
a - hero1-role1-race1-gend1-algn1
b - hero2-role2-race2-gend2-algn2
n - New game
q - Quit
or
a - - hero1-role1-race1-gend1-algn1
b - X hero2-role2-race2-gend2-algn2
c - D wizard-role3-race3-gend3-algn3
n - New game
q - Quit
when any game in the list wasn't saved during normal play. (Those
are sorted by character name; the playmode is just coincidence.)
The dash for 'normal' doesn't look great but -/X/D are codes used in
entries written to paniclog. The whole playmode prefix doesn't look
particularly good but I suspect that most players relying on restore
via menu won't see it.
It should work when the character name has dashes in it but that
hasn't been properly tested.
The gender and alignment suffices reflect their value at the time of
save rather than at the start of the game. That might be considered
a bug but it was easiest.
Increments EDITLEVEL; existing save and bones files are invalidated.
display.h -- bring an unused macro up to date
detect.c -- always call newsym() when searching or secret door
detection finds a trap rather than just when not blind
glyphs.c -- some formatting
pager.c -- lookat() behaves very strangely when single-stepping
in the debugger (gdb); this didn't help
Issue reported by ars3niy: an arrow trap covered by more than
one stack of arrows was described by farlook as "unexplored area".
Later simplified to any object pile with plain arrows on top; the
trap turned out to be irrelevant aside from producing a pile of
arrow stacks.
This problem turns out to be due to an off by 1 error introduced
into the xxx_is_piletop() macros when generic objects were added in
mid-January of last year, and plain arrow is the first real object
so suffered from the bug. Misclassifying the glyph at a specific
location also confused the #terrain command but it's the same bug.
Knowing what was wrong (map glyph was UNEXPLORED when it should
have been ARROW) wasn't sufficient to figure out the problem.
Without the simplified way of reproducing the problem, I would
never have managed to use 'git bisect' to find the offending commit
because that would have been too time consuming.
Fixes#1289
Changes to setuhpmax() a couple of days ago to deal with sanity_check
for "current hero health as monster better than maximum" ended up
triggering sanity_check about "current hero health better than maximum"
when gaining experience level(s) while polymorphed.
being greater than u.mhmax
I think this will fix the situation where current HP as a monster
was greater than maximum HP as a monster, triggering a sanity_check
failure. No promises though.
[poisoned() needs some missing Upolyd handling.]
When 'tips' are enabled, the farlook tip displays some text at the
start of getpos(). But it clobbered the initial prompt, leaving
the screen in an ambiguous state (seemingly a normal map display,
but it is actually waiting for player to move the cursor) after
removing the tip's popup window.
Reissue the prompt. farlook's short but misleading prompt of
"Pick an object" is changed to "Pick a monster, object or location".
I would normally include a comma before "or" but omitting it makes
the longer text seem slightly less cluttered.
The other tips are all one-line, delivered via pline(). Prefix
all of their messages with "Tip:" (which the farlook one already
uses) as a hint for using OPTIONS=!tips to shut them off.
I don't always want to abort() on an impossible() when debug_fuzzing,
especially if the first impossible() encountered isn't related to the
bug I'm in the midst of trying to hunt down.
I often have breakpoints on impossible() anyway, and I'd like a simple
way to avoid the panic() call during a lengthy debug session.
Make iflags.debug_fuzzer an xint8 instead of a boolean.
Call abort() only if iflags.debug_fuzzer is set to 1.
That allows setting iflags.debug_fuzzer to 2 in order to bypass the
abort call, and make use of other breakpoints that have been set
to narrow down a particular issue.
Take care of most of include/*.h. I punted on extern.h.
For both src/*.c and include/*.h, I used mismatched checks of
width > 79 to decide which files to look at and then width > 78
to decide which lines to maybe revise, so I didn't look at a bunch
of the files.
I don't plan to go back and do it right. Shortening lines that are
80 or wider to less than 80 is the significant part. Otherwise
emacs puts a backslash in column 80 and the rest of the line of text
on the next line of the screen, making things harder to read.
This makes custom S_sw_tc etc. from Enhanced1 symset actually work,
yielding nice smooth outlines for swallowers and explosions. Or so I
think, I have only tested the former because when playing locally,
explosions disappear so fast I cannot see them.
While looking at where else glyph_to_swallow was used, I noticed that
parse_id subtracted S_sw_tl from glyph_to_swallow, even though it
returns 0 to 7. This looks like it would cause out-of-bounds access and
perhaps a segfault when trying to customise glyphs for individual
monster swallow glyphs (or whatever it is parse_id is used for), but
I haven't tried to confirm nor change this because who would ever do
such thing?
Even if nethack is meant to support compilers that do not know about
inline functions, this one was frankly too long to be a macro already,
and I'm about to make it longer still.
When looking at a monster that's inside a gas cloud, include that
fact in the output for farlook and for probing. When the monster
being examined is sensed rather than seen, you'll sense the presense
of the cloud as well as the monster even if the cloud can't be seen.
Do likewise for self when using look-here (':'). Bonus fix: zapping
wand of probing at self while engulfed reported that you were just
held by the engulfer.
Also fix an old comment typo/thinko.
Issue reported by ars3niy: a hasted shopkeeper always gets 2 moves
per turn and had a tendency to move away from the door and then move
right back, keeping it blocked.
I didn't view the ttyrec and didn't reproduce the situation, but I
have noticed something of the sort in the past. This reduces shk
speed so that there will usually be 2 moves per turn but not always,
increasing the likelihood of leaving the door unblocked when nearby
hero does not owe anything.
This change results in a slowed shopkeeper having speed dropped to 11
rather than 12. I suspect that the original 18 speed might have been
picked to guarantee slowed speed of at least 12, but if so, that was
in the days when speed 11 would have provided 11 consecutive moves
and then a turn guaranteed to not allow a move rather than the current
11 out of 12 chance to move each turn.
Fixes#1267
Allow farlook/quicklook to step through all engravings--corridor
ones as well as room ones--with repeated '`' when picking location
with getpos(), similar to how repeated '^' goes through all traps
no matter what display character they use.
It isn't possible to look at corridor engravings by using their
default symbol '#'. It is possible by using 'a' for "anything of
interest" but that matches lots of other stuff.
This tries to add an NHDT tags line to sym.h. That was followed by
'git nhadd' which did stage it for commit, but date/branch/revision
tag expansion isn't working. I did something similar to defsym.h
about 5 weeks ago; that one worked and I don't recall having needed
to do anything special.
Show visible regions among the other timed events. Turn number is
relative rather than absolute. Location is the internal bounding box
which tends to cover more area than just the gas cloud spots.
When multiple regions are present (common on Plane of Fire), they're
listed in arbitrary order. It would be better to order them by
timeout or by location or both, but the extra effort to do that seems
unjustified.
Extend paranoid_confirm:trap to also ask for confirmation when
attempting to enter a gas cloud region (scroll of stinking cloud,
breath from green dragons or iron golems, steam clouds from boiling
water, vapor left by fog cloud movement, no doubt several others).
Like with traps, can be overridden for a given move by using the
'm' prefix. Unlike traps, doesn't try to guess whether moving into
a region will be harmless.
Doesn't affect movement into cloud terrain (Plane of Air).
Update the Guidebook to describe the revised behavior of
paranoid_confirm:trap and to mention how #terrain deals with regions.
'any_visible_region()' got mixed in with this but isn't used yet.
Affects extern.h and region.c.
Messaging for vampires changing into fog clouds was inconsistent
in 3.6.x but already fixed in to-be-3.7.
However, maintaining an extra set of shape change messages (one
in newcham(), the other in vamp_shift() which turns out to only be
used when a vampshifter turns into a fog cloud in order to pass
under a closed door) was a nuisance. Redo vamp_shift() to use
newcham() feedback. Update that feedback to be accessibility-aware
and also to use "Your <mon>" instead of "The <mon>" when appropriate.
While in there, remove a couple of trailing spaces and eliminate
use of one dynamically constructed format string that necessitated
warning manipulation.
Issue reported by loggersviii: monsters killed by liches can
produce corpses which auto-revive as zombies, but liches aren't
able to harm zombies and get stuck in an endless slap fight when
those revive.
Liches have a touch attack for cold damage and they have a spell
casting attack. They don't use their spell attack in monster vs
monster combat, so cold resistant foes don't receive any damage.
Prevent them from becoming harmless by changing the cold damage to
physical damage if the target resists cold.
Fixes#1263
> If there's a trap on a no-dig level, the floor beneath it will always be "too hard to dig into", making it impossible to remove the trap.
>
> As you can still dig pits in these levels (just not holes), the floor under the trap itself resisting to become a pit seems inconsistent.
>
> Steps to reproduce:
>
> Go to no-dig level like Mine's End
> Make a trap
> Dig a pit next to it -> works
> Dig on the trap -> does not work
Return more information about the dig_check() results to caller (was
just a boolean).
Move the messaging that was in dig_check() into a separate
digcheck_fail_message() function that uses the expanded return
information.
Resolves#1254
.
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.
'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.
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.
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.
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.
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.
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
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".