Wizard mode shows the number of points needed to reach the next level
(unless already maxxed out at 30) for ^X and end of game disclosure.
Do it in normal play for the latter too. (I think it would ok to do
that for ^X too but haven't gone that far.)
Even when it was wizard mode only, the phrasing for past tense had a
minor grammar bug, and it could make the line a little too long for
tty and curses (not sure about others) when level was high, resulting
in wrapped text. That looked bad for tty, which first tries removing
indentation (just 1 space in this case), making that line outdented
as well as wrapped. So change the phrasing slightly when experience
level is 'too high'. I had a version which formatted, measured, and
re-formatted if necessary but that was overkill; simple hardcoded
rephrasing suffices particularly when measuring was against assumed
display width (80) rather than actual width.
Make some progress on a couple of next minor release checklist
items, hopefully without introducing too many new bugs. This
is just the initial commit, and work continues.
Checklist items:
Savefiles compatible between Windows versions, whether 64-bit
or 32-bit in little-endian field format.
Selection of file formats:
historical (structlevel saves),
lendian (little-endian, fieldlevel saves),
and just for proof-of-concept, ascii fieldlevel saves
(the ascii is huge! 10x bigger than little-endian).
For the fieldlevel save, all complex data structures recursively
get broken down until until it is one of the simple types that
can't be broken down any further, and that gets when it gets
written to the output file.
New files needed for this build:
hand-coded:
include/sfprocs.h
src/sfbase.c - really a dispatcher to one of the
output/input format routines.
src/sflendian.c - little-endian output writer/reader.
src/sfascii.c - ascii text output writer/reader.
auto-coded (generated):
include/sfproto.h
src/sfdata.c
This is just one approach. I'm sure there are countless others
and they have different pros and cons.
For producing the auto-coded files a utility called
universal-ctags, that is actively maintained and evolving,
was used to do all the heavy-lifting of parsing the
NetHack C sources to tabulate the data fields, and store
them in an intermediate file called util/nethack.tags
(not required for building NetHack if you already have a
generated include/sfproto.h and src/sfdata.c)
util/readtags (also not required for building NetHack
itself) will decipher the nethack.tags file and produce
the functions that can deal with the NetHack struct data
fields.
You can obtain the source for universal-ctags by cloning it
from here:
https://github.com/universal-ctags/ctags.git
The combination universal-ctags + util/readtags has been
tried and tested under both Windows and Linux, so it is
not tied to a particular platform.
Note: util/readtags will work only with universal-ctags
output, so other ctags are unlikely to work as-is.
Universal-ctags can be build from source very easily
under Linux, or under Windows using visual studio.
Recent fuzzer tweak had an unintended side-effect: NUL character is
used to indicate a mouse click and we weren't setting up fake value
for one of those. Go back to avoiding NUL when obtaining a random
value for user's keystroke.
Prevent the fuzzer from randomly toggling the 'silent' option. If
you use the default value of True then this eliminates most--but not
all--of the beeping that happens when it is running. I'm not sure
where the remaining beeps are coming from.
Modify the random keystroke selection to implement some bias towards
direction keys that I thought had already been there, plus a higher
chance to entering digits to initiate number responses.
Hero polymorphed into a vampire or v.lord can use #monster to switch
to vampire bat or fog cloud [or wolf for lord] but it was a one shot
polymorph. Remember when current form is a shape-shifted vampire and
allow #monster in shifted form to pick another shifted form or the
vampire form.
Genocide of the alternate shape forces back to base vampire. Genocide
of base vampire does too, then reverts to human (or dwarf, &c) as
vampires go away. Being killed while shafe-shifted reverts all the
way to human rather than to vampire. [Just realized: interaction
with Unchanging wasn't taken into consideration so hasn't been tested.]
Since 'youmonst' isn't saved and restored, I had to add a field to 'u'
to hold youmonst.cham during save/restore.
Tested with 3.6.2+ and seemed to be working (except saving while
shape-shifted restored as ordinary bat/cloud/wolf because new u.mcham
wasn't there to hold youmonst.cham yet). Builds with 3.7.0- but not
execution tested yet (I didn't want to clobber my current playground).
The key binding overhaul broke '&' reporting for movement commands.
This is somewhat clumsy but seems to be working as intended.
(M-0 with 'number_pad' set to 2 (or 4) appears to be broken. It's
supposed to be a synonym for 'I' but brings full inventory ('i')
for me. I have to set 'altmeta' On and type ESC followed by 0, but
that sequence does work to get M-0.)
Add a bc sanity check. It seems to work ok--in other words, not
trigger--under normal punishment. I don't have any test cases to
exercise its warnings.
This dragged in a couple of minor bc changes that were pending. I
should have cleared those out before tackling the sanity checking.
Preserve temporary fake object's previous dknown value by storing it
as a flag value within the m_ap_type field of the posing monster, and
recalling it when it is needed.
This is intended to help eliminate observable differences in price display
between real objects and mimics posing as objects.
98% of this is just switching the code to utilize macro M_AP_TYPE(mon)
everywhere to ensure that the flag bits are stripped off when needed.
Extend 'putstr(WIN_MESSAGE, attribute, string)'s attribute so that
'custompline(SUPPRESS_HISTORY, ...)' can work with ^P's message
history like DUMPLOG history, in order to keep autodescribe feedback
and intermediate prompts for multi-digit count ('Count: 12', 'Count:
123') prompts out of recall history. The old autodescribe behavior
could easily push all real messages out of the recall buffer when
moving the cursor around for getpos, and the count behavior looked
silly for a four or five digit gold count if you set the msg_window
option to 'full' or 'combination' and viewed them all at once.
Other interfaces may want to follow suit, but this doesn't force them
to make any changes. I added a hook for "urgent messages" that might
be rendered in bold or red or some such and/or override the use of
ESC at --More-- from suppressing further messages, but there aren't
any custompline(URGENT_MESSAGE, ...) calls (potentially "You die...",
for instance) to exercise it. Other people have implemented similar
feature it different ways and I'm not sure whether this one is really
the way to go since the core needs to categorize each message that it
deems to be urgent. MSG_TYPE:stop may be sufficent, although MSG_TYPE
matching can entail a lot of regexp execution overhead at run-time.
Lock context wasn't being cleared if it was for a container and that
container got destroyed. Case discovered was forcelock() ->
breakchestlock() -> delobj() (sometimes the container is destroyed
rather than just breaking its lock) followed by #wizmakemap (replace
current level) and maybe_reset_pick() trying to check whether
xlock.box was being carried. But being interrupted, destroying the
container or dropping it down a hole to ship it to another level, then
attempting to resume picking the lock would also find a stale pointer.
new domove_core() assessment results
potentially smudge engravings
Proceed to wipe engraving after domove_core() now, but only under
all of the following conditions:
- you can reach the floor
- preceding domove_core() move attempt was marked as
having succeeded in domove_core()
- there is actually an engraving there to impact at
your original spot, or your new spot, or both
The check I added to make sure that a monster was at the hero's
coordinates before deciding to move one or the other would have been
confused by a long worm's tail. Check that they're at that spot but
not by comparing monst.<mx,my> coordinates with <ux,uy>.
Also, don't have wiz_makemap() assume that each level of the Wizard's
Tower has the same boundary coordinates. Keep track of whether hero
is inside that tower before discarding the old level.
Both u_on_rndspot() and losedogs() might result in having a monster
and the hero be at the same location. Have wiz_makemap() use the
same fixup for that as goto_level().
The need for resetting lock picking when swapping in a new level made
me wonder whether other things should be reset too, and there were a
bunch: digging, travel destination, polearm target, being in water,
being swallowed or held, hiding. Hero placement was ignoring arrival
region. Also, it turned out to be pretty easy to fix the FIXME about
steed.
Fixes#172
Casting teleport-away via ^T used different requirements for energy,
strength, and hunger than casting it via 'Z'. The strength and hunger
requirements were more stringent, the energy one more lenient. When
it rejected a cast attempt due to any of those, it used up the move,
but 'Z' didn't.
When testing my fix, I wanted an easier way than a debugger to control
how ^T interacts with wizard mode, so finally got around to a first
cut at being able to invoke it via wizard mode but not override those
energy/strength/hunger requirements. It uses the 'm' prefix to ask
for a menu. 'm^T' gives four options about how to teleport. (There
are other permutations which aren't handled.)
Also noticed while testing: ^T wouldn't attempt to cast teleport-away
if you didn't know the corresponding spellbook. 'Z' will attempt that
because it is possible to forget a book and still know its spell.