After going back and forth between prompts causing message lines
to be overwritten and to be skipped, this yoyo might have finally
run out of string. Fingers crossed....
Fix a 'FIXME': don't follow a message with two spaces in anticipation
of combining with the next one, precede the next one with two spaces
when they're being combined. Keeps nethack's message window <mx,my>
coordinates in sync with curses' internal coordinates.
Back in February, my e991dd1b0c added
ESC (when there's no input) as an early return for curses' getline,
but it neglected to clean up some allocated memory.
Fix a problem introduced by f218e3f15e
and/or a19e64e470. Sometimes the line
after a prompt would be empty and the next message get shown on the
line after that. a19e64e470 was intended to fix the opposite problem
so probably overshot the mark....
Sometimes curses tears down and recreates all its windows (when the
display is resized, for instance) and after doing that it repopulates
the message window with data saved for use by ^P. But it was showing
the oldest messages available rather than the most recent ones.
There is still room for improvement. That process combines short
messages but the refresh is based on the available number of lines;
combining messages can result in lines at the bottom of the message
window being left blank. This could be fixed by reverse-scrolling the
window and inserting more messages at the top, or by combining short
messages in history data instead of at refresh time. The second seems
easier but won't handle changing the message window's width sensibly,
and neither method handles wrapped, long lines well. A More>> prompt
(possibly more than one) is issued if the refresh shows too many lines
(either because long messages already took multiple lines or because
the window has become narrower and ones which used to fit now need to
be wrapped).
The earlier fix removed a valid optimization which happened to be
implemented incorrectly. Put that back. It also left an invalid
optimization when applied to conditions. Remove that one.
I don't think either of these explains truncating 'y' off of "Hungry"
which was shown in one of the reports.
Reported as #H8609 (1679)
Some code recently added to render_status() for BL_CONDITION:
if (!tty_condition_bits)
continue;
was short-circuiting the required copy of NOW
values to BEFORE values for later comparison
further down in the for-loop.
tty_status[BEFORE][idx] = tty_status[NOW][idx];
This caused some fields to be bypassed for rendering
once no more tty_condition_bits were set because the
length comparisons would match.
Some prompts were being overwritten by the message that followed.
And clear_nhwindow(WIN_MESSAGE) gets called for just about every
keystroke so try to reduce the overhead I unwittingly added. The
"scroll up one line earlier than the next message" mentioned in
the prior commit is much more obvious that I realized and prompt
erasure might need to be redone.
Autodescribe feedback and multi-digit count prompts are always shown
on the last line of the message window and are suppressed from message
history (both ^P and DUMPLOG). When the message window is using all
available lines, the last one was being overwritten (until the count
or the feedback was completed or dismissed, then last line returned).
Adopt the suggestion that it be scrolled up a line instead of being
overwritten. [I haven't been able to reproduce the reported problem
where shorter overlaid text left some of longer underlying text visible
but that should now become moot.]
Bonus fix: while testing, I noticed that if your screen only has room
for a one-line message window and you used ESC to cancel 'pick a spot
with cursor' prompting before moving the cursor, the prompt was left
intact on the message line. tty erases it in that situation, but the
clear_nhwindow(WIN_MESSAGE) was a no-op for curses because it usually
doesn't erase old messages. This changes the curses behavior when the
core asks it to erase the message window: now it forces one blank line
of fake autodesribe feedback (causing the prompt or other most recent
message to scroll off top), then removes that fake feedback (leaving
a blank message line). For multi-line message window, the old messages
scroll up by one line sooner than they would when waiting for the next
real message but are otherwise unaffected.
There was a post-3.6.2 discussion on a forum where someone had
tried to copy the NetHack 3.6.2 exe file overtop of an
existing NetHack 3.6.0 playground, and then try to run it.
We have never suggested trying that, nor do we attempt to
provide any backward or forward compatibility between the
supporting files found in nhdat that would allow that. Any
particular version of NetHack expects to have matching
support files designed and matched to that version.
This adds optional support for helping to prevent the
opening of nhdat containing support files from an
unmatched version of NetHack.
If you #define VERSION_IN_DLB_FILENAME in your
platform's include/*conf.h file, it will use a
name such as nhdat362, instead of plain nhdat, and
will exit more gracefully than the fault/crash
mentioned in the discussion if it doesn't find the
file it is looking for.
Developers - please note that if you do
to cause NetHack to look for an nhdat* file with
the version info appended to the name, you will likely
have to modify your build/clean/spotless mechanics
beyond the C compile itself to properly deal with the
new generated file name.
The curses interface would assign menu selector characters a-z, A-Z,
and then 0-9, but trying to type 0-9 would start a count rather than
select an entry, and if the display was tall enough for more than 62
entries, the ones after '9' were ASCII punctuation characters.
Limit the number of entries per page to 52 + number_of_'$'_entries
(which should be 0 or 1) so that it won't run out of normal letters.
The perm_invent window, if enabled, ought to allow more than that
because it isn't used to make selections and might have an arbtirary
number of '#' overflow entries. But I'll leave that for somebody
else to tackle.
Tested by temporarily setting the limit to 26 instead of 52 since
I'm not able to display anything tall enough to exercise the latter.
DEC C in one of its non-ANSI modes didn't like
fieldorder = test ? &array1 : &array2;
It first complained that '&' applied to an array has no effect (which
was typically true in pre-ANSI environments) and once those '&'s are
ignored, the attempted assignment didn't match the variable's type.
That code was actually more complicated that it needed to be; slightly
simpler code works as intended.
Enable blink and dim for the TERMLIB + !NO_TERMS configuration of the
tty interface. Blink now works the same as in the curses interface
for status highlights. The terminal emulator I'm using has an escape
sequence for dim but it evidently doesn't do anything (same no effect
as with curses), so that isn't adequately tested.
The curses interface wouldn't build with HILITE_STATUS disabled. I
started adapting it to handle genl_status_update() but that was taking
too much effort with each niggling detail leading to another. This
goes the opposite direction: forcing the old STATUS_VIA_WINDOWPORT
behavior without having that #define available. That dragged along a
bunch of unexpected changes too.
Take care of a minor 'TODO' and make another stab at getting truncated
encumbrance and/or level-description to reset to full size when enough
space becomes available.
I've noticed many instances of the game pausing and not being sure why,
then pressing <space> and having it resume. The curses interface had
a tendency to put its equivalent of the --More-- prompt, >>, somewhere
where that wasn't visible, either off the right hand edge (possibly) or
underneath the window borders if those were enabled. Especially the
very last one it issues prior to exit. (An extra one compared to tty
behavior.)
This ended up being a pretty substantial overhaul of message window
handling. I wouldn't be surprised if it has off-by-one errors which
happen to be paired up and cancel each other out. ">>" is still drawn
in orange if guicolor is on, now in inverse video when that is off.
If it happens to be drawn at the same screen location in consecutive
instances, the first ">" will toggle between blink and not blink so
that there'll be no doubt as to whether the keypress registered when
dismissing it (moot if the text preceding it is different but there's
no attempt to be smart enough to check that, just screen placement).
Make the same fix to curses that was done for tty in 3.6.1: don't
let MSGTYPE entries be matched against prompt strings. Like tty,
curses was using ordinary pline() to issue prompts; something like
MSGTYPE=hide"yn"
could wreak havoc. Switch to custompline(OVERRIDE_MSGTYPE,...).
This changes the recently added msg_window:f for curses to start
viewing the old messages on the last page rather than the first. For
msg_window:Reversed (the default for curses) and for either direction
when all of the message history happens to fit on one page, there's
no change. But for multiple pages, the FIFO feedback now pads the top
of the first page with blank lines so that the last page is full, and
it starts out showing that last page first. So if you only want to go
back few or several messages, they will be in view immediately.
Old layout:
|first message (oldest) | |1st message of last page |
|2nd message of 1st page | | ... |
| ... | |final (most recent) mesg |
| ... | | (blank filler) |
|last message of 1st page | | (blank filler) |
| (1 of 2) => | | <= (2 of 2) |
and ^P started with first page visible and needed normal menu handling,
<space> or '>' or '|', to go forward to view the most recent messages.
New layout:
|1st message of last page | | (blank filler) |
|2nd message of last page | | (blank filler) |
| ... | |first message (oldest) |
| ... | | ... |
|final (most recent) | |last message of 1st page |
| <= (2 of 2) | | (1 of 2) => |
and ^P starts on last page (two of two in this example) but can go
back with '<' and '^'.
So if the total size takes one and third pages (which isn't uncommon
for the default number of kept messages), you'll see 3/4 of the most
recent messages on the initial screen, then you can page backward if
you want to see the other 1/4.
The page indicator is deliberately drawn a bit differently just to
draw attention to the fact you're starting on the last page. I'm not
sure whether that is actually worthwhile but it was trivial to do.
Support for scrolling within menus via first-/previous-/next-/last-
page keystrokes ("^<>|" by default) was added to X11's general menu
handling but the extended commands menu uses a special menu rather
than a general one. This clones the relevant code to add support for
those keys to extended commands.
The expansion of the extended commands list to include every command
has made picking extended commands out of X11's menu become tedious.
This uses the existing 'extmenu' option (previously tty-only) to
control whether all the commands are present or just the traditional
subset not bound to non-meta keystrokes ('adjust', 'chat', 'loot', &c).
The curses interface was using 'moves' as if it meant "moves" rather
than "turns". Typing ESC at >> (curses' terser version of --More--)
prompt would suppress messages for the rest of the current turn rather
than just the rest of the current move. So if the hero got an extra
move due to being Fast, there would be no feedback during that move.
window.doc states that the colormasks argument to status_update() is
only relevant for BL_CONDITION, but curses was relying on it to be
passed for BL_FLUSH as well. Yesterday's changes stopped the latter
and broke highlighting of status conditions. Other interfaces appear
to honor the description in window.doc.
Have the curses interface save and restore message history for use
by ^P. It doesn't spit the saved messages out into the visible
message window after restore; that's too distracting.
Noticed while testing statuslines on a small terminal window. Using
the cursor to pick locations that panned the map to view a new subset
would end up showing a new view of the regular map rather than a
different section of what was currently displayed. For farlook that
caused monsters to take on new hallucinatory forms which was fairly
inconsequential, but for #terrain and various forms of detection it
reverted to the ordinary map instead of showing the map features that
the player requested or the temporarily revealed monsters and such.
Most interfaces keep track of the whole map and just show their view
of the new subset when panning, similar to redisplay after being
covered up and then re-exposed, but tty isn't doing that. I made
same change to Amiga as to tty since the code it was using was very
similar. I haven't touched any of the other interfaces and assume
that they don't need this. I've verified that curses and X11 don't.
Implement the 'statuslines' option for tty. 2 and 3 line status are
similar to curses. Tty's version doesn't include insertion of extra
spaces for enhanced readability, or ignoring 'showexp' when space is
needed for other fields, or right justifying 'score' and suppressing
it when there isn't room for the entire number. It continues to have
abbreviated condition and encumbrance descriptions that curses lacks
which get used when the normal ones take up too much space.
'statuslines' can be set with 'O' so it is feasible to switch back
and forth between 2 and 3 lines on the fly. But only if the display
is at least 25 lines (actually ROWNO+4) or else CLIPPING is enabled
at build time.
This fixes the bug where after resorting to abbreviated condition
values it sometimes (always?) wouldn't switch back after more room
became available. Abbreviated encumbrance values had problems too
(lack of leading space and not changing value if encumbrance changed
to anything other than unencumbered) and this fixes that as well.
Catch up with curses and have hitpointbar work even if statushilites
is 0 to suppress other highlighting. Indirectly fixes #H8389 by
making the circumstance which triggered that bug no longer do so.
The curses interface maintains message history in a doubly linked list
with a capacity limit. Once capacity is reached, the list head is
advanced and the old head discarded, but it was leaving the new head's
'previous element' link pointing at that discarded element.
tmp_mesg = first_mesg->next_mesg;
(at this stage, tmp_mesg->prev_mesg points at first_mesg),
free(first_mesg);
first_mesg = tmp_mesg;
(with necessary 'first_mesg->prev_msg = NULL' missing). The situation
wasn't a significant problem because traversing the list was limited
by a counter. Going from tail back to head exhausted the counter
without ever accessing the stale pointer.
Since it wasn't noticeable, I haven't added a fixes entry for this.
I've also changed it to do fewer memory allocations and frees by
reusing the old list head instead of always allocating a new element
and freeing the one being replaced.
The unresolved "first problem" mentioned earlier in commit
382286cb99 was caused by stale values
in status fields which had become disabled. Polymorphing left an
old BL_XP value and returning to original form left an old BL_HD one.
They weren't displayed but the stale value was included in the line
length calculation, resulting in 4 or 5 columns being set aside for
a phantom value. That implicitly reduced the available length of the
line and could result in extra spaces separating other fields being
squeezed out while unused spaces remained at the end of the line.
Experience points, time, and score didn't trigger this problem because
they were being explicitly excluded if disabled. So stale values for
them when they had been enabled then later disabled didn't matter.
I noticed a couple of things wrong--that I was fairly sure that I
had working correctly before--and after fixing the second one, the
first has mysteriously disappeared.
First problem, which may or may not still be a problem: extra spaces
were being removed from the second line of 2-line status even though
there were still 4 or 5 available spaces to the right of the status
conditions. It was behaving as if it thought the line was narrower
than actual size, or conversely, that the sum of the widths of the
fields plus the extra spaces was bigger than it actually was.
Second problem, fixed here. The code to put '+' in the far right
column of the last status line when there is at least one condition
all the way off the display wasn't working right when windowborders
were displayed. That's down to curses wrapping to the next line but
user can't see it due to the window border overwriting. Single char
overflow stayed on same line, but two or more wrapped and then the
'x' coordinate didn't match tests for 'too wide'. Perform explicit
truncation instead of leaving that up to curses. Also truncate
encumbrance when warranted since it's feasible for it to overflow.
Anyone using a display narrower than 80 columns might still run
into odd status behavior because other fields than conditions and
encumbrance could go past the end of line. But they shouldn't be
wasting screen real estate with windowborders, and without borders,
curses will keep the cursor in the bottom right corner when the
program tries to go past, which should keep things reasonably sane.
curses uses 'reversed' (LIFO) style when displaying previous messages.
Use the existing (previously tty-only) 'msg_window' option to also
support 'full' (FIFO). The actual code needed as just a couple of
lines; tweaking options parsing and the documentation was more work.
Using ^P right after resize or 'O' of align_message, align_status,
statuslines, or windowborders would result in
'curses_display_nhmenu: attempt to display empty menu'
because some memory cleanup I added several weeks back was being
executed when the curses interface tore down and recreated its
internal windows.
This fixes ^P handling by making sure that that menu (which is just
text but uses a menu to support '>'/'<'/'^'/'|' scrolling) will never
be empty and it also fixes the window deletion to not throw away
message history until it's final deletion at exit time.
^P uses a popup window to display previous messages and it was never
deleting that window, just creating a new one each time. Same with
the routine which displays an external help file. Using either or
combination of both close to 5000 times would probably make internal
window creation get stuck in an infinite loop. Delete those windows
after they're used so it'll never be put to the test.
The memory cleanup I added for map/status/messages/invent was only
being preformed at end of game, not when saving. Fix that too.
I've overhauled the status display for curses. Horizontal layout
supports both 2 lines and 3 lines which can be changed dynamically
via using 'O' to set 'statuslines'. Fields are spread out a little
more than they used to be, making it more readable--at least to me--
but the extra spaces get squeezed out when lines become too long.
If 'showexp' is on and either conditions or hunger+encumbrance go
off the right edge, experience points are suppressed (but the option
is left on, so they'll come back once there is room).
For traditional 2-line hozizontal status, if hunger+encumbrance+
conditions go off the right edge even after experience points are
knocked out, there will be a '+' in the rightmost column if there
are any conditions that are all the way off. At present it doesn't
use the tty method of switching to abbreviated condition names to
reduce their legnth. I'll probably tackle that eventually if no one
beats me to it.
For 3-line horizonal status, there was an older implementation (but
disabled via #if 0) with gold and score moving to the third line.
(I'm not sure how status conditions were handled.) This one ignored
that and modified 2-line from scratch, moving alignment from line one
to line 2 and level description, time, and conditions from line 2 to
line 3. It looks like this (view with a fixed-width font...).
Wizard the Hatamoto St:16 Dx:15 Co:18 In:8 Wi:11 Ch:7 S:25
Lawful $:21 HP:25(25) Pw:6(6) AC:4 Xp:2/21 Hungry Burdened
Dlvl:1 T:36 Blind Lev
Score is actually right aligned with the edge but I've deleted several
spaces to keep the line shorter here. The status conditions line up
with the hunger slot as that shifts due to changes in gold/HP/power/AC/
experience, and conditions prefer that column even when hunger and/or
encumbrance are blank. Howver, if the number of conditions increase to
the point where they would go off the edge, the whole list shifts left
instead of trying to stay lined up with hunger. (It's just coincidence
that the lefthand parts of lines 2 and 3 seem to line up in this sample.
In general, they don't.)
The vertical layout has reordered most of the fields and now has a few
blank lines to separate those fields into some groups for readability.
Lines have the form of
Field-name : Value
and when highlights apply, now they only affect the value portion.
Single digit characteristics are padded with a leading space so that
all six of them line up (for "18/xx", "/xx" protrudes to the right).
HP and Pw are aligned with each other. Hunger and encumbrance share a
line. When there are more than three conditions, they're shown three
per line instead of wrapping across lines. And if too many lines are
present, it will squeeze out enough blank ones to fit.
To see the vertical status, you need a display size of at least 106
columns with 'windowborders' explicitly off, or 110 with them on; also
set option 'align_status' to 'right' or 'left'. (With borders on,
including the default 'auto' setting, the vertical status appears at
width of 108 columns, but does so by hiding 2 columns of the map; using
110 columns avoids that.) Resizing from outside the game or changing
align_status via 'O' both cause dynamic reconfiguration of the layout;
there's no need to save, make config changes, then restore.
More groundwork for overhauling the status display for curses, plus
a few functional changes. It was doing a full status update for
every changed field (except conditions), instead of waiting for a
flush directive after gathering multiple changes at a time. Since
it already does gather every change, the fix to wait is trivial.
This decouples 'hitpointbar' from 'statushilites'. When highlighting
is off, it uses inverse video only. When on, it behaves as before:
using inverse video plus the most recent color used to highlight HP
(which can vary if that has rules to highlight changes or percentage
thresholds) but ignoring any HP attribute(s). This also enables the
latent 'statuslines' option and changes 'windowborders' option from
being settable at startup only to changeable during play.
'statuslines' can have a value of 2 (the default) or 3 and applies to
'align_status:bottom' or 'top'; it's ignored for 'left' and 'right'.
At the moment, setting it to 3 only allows status condition overflow
to wrap from the end of line to 2 to the beginning of line 3, and if
window borders are drawn they'll clobber the last character on line 2
and first one on line 3. There's no point in trying to fix that
because it will go away when the main status overhaul changes go in.
Condition wrapping for vertical orientation (left or right placement)
was already subject to the same phenomenon and will be superseded too.
This also changes the meaning of the 'windowborders' value so could
impact players using source from git (or possibly beta binaries for
Windows, but not for OSX where curses interface wasn't included).
Old:
0 = unspecified, 1 = On, 2 = Off, 3 = Auto (On if display is big
enough, Off otherwise; reevaluated after dynamic resizing);
Unspecified got changed to 3 during curses windowing initialization.
New:
0 = Off, 1 = On, 2 = Auto;
0 gets changed to 2 for default value at start of options processing.
So old value of 2 is changing meaning and explicit old value of 3 is
becoming invalid. Implicit 3 changes to default 2. Explicit 3 could
be the subject of a fixup but there isn't much point since 2 can't
have a similar fix. Users who are using old 2 or explicit 3 will need
to update their run-time config files.
This adds 'statuslines' to the Guidebook and moves some other recently
added documentation of curses options from among the general options
(section 9.4) to "Window Port Customization options" (section 9.5).
None of them have been added to dat/opthelp which seems to be missing
all the wincap options.
Originally I made a lot of changes (mostly moving C99 declarations to
start of their blocks) to the old '#if 0' code at end of cursstat.c,
but have tossed those, except for one subtle bug that assumed 'int'
and 'long' are the same size.
Miscellaenous stuff either groundwork for or noticed while updating
curses status. The status changes themselves need some more testing.
One or two of the comments refer to that revised status which hasn't
been checked in yet.
Honor hilite_status rules specifying color even if curses-specific
option 'guicolor' is off.
Update status from scratch when 'O' is used to manipulate hilite_status
rules.