Reported by entrez, some putstr() to text window got changed to
add_menu_str(). I didn't test with curses; with tty some headers
ended up in limbo: "Artifacts" header for '` a y' (wizard mode show
artifacts, something I had forgotten even existed) and also monster
class headers for 'm #vanquished by-class' (available to everyone).
Qt lost them too, but at least it didn't panic.
Not due to over-simplification: end of game disclosure suppresses
header line highlighting, except when disclosing final inventory.
Change it to do so, although it would be simpler overall to just not
bother with any menu_headings highlight suppression.
Reported by entrez: it was possible for #overview to show a line of
just "." if a temple was known and its altar was unknown and no other
features such as thrones or fountains were known on the level.
It now lists "M temples and N altars" when both are present and the
case that yielded "." becomes "a temple". That's an improvement but
there might be edge cases it gets wrong. A listing of "a temple and
an altar" is ambiguous because there isn't any way to tell whether the
altar it mentions is inside the temple. That seems acceptable to me.
I think it should include more alignment information about temples and
altars, instead of just adding "to <your god>" when all known altars
are of hero's alignment, but this doesn't attempt to address that.
Adds a new lua command
des.exclusion({ type = "teleport", region = { x1,y1, x2,y2 } });
which allows defining "exclusion zones" in the level, areas where
random teleports (or falling into the level) will never place the hero.
Does not prevent targeted teleportation into the area.
Breaks saves and bones.
When returning to play from within the tutorial, remove the level files
similar to how they're discarded for the rest of the dungeon when going
into the endgame. It turned out to be a bit messier than anticipated.
The dungeon.c bit is sufficient for #overview, which now hides regular
level 1 while in the tutorial and hides all tutorial levels once exited.
Those will still appear in end-of-game disclosure.
The FIXME comment noted that builds_up would return an incorrect false
value for a dungeon branch that builds upwards but is only 1 level, but
that this is a latent problem because no such branch exists in NetHack.
Such a branch does exist in xNetHack, and it causes the debug fuzzer to
crash ("mon_arrive: no corresponding portal" because it can't find the
correct-direction stairs), so I figured I might as well fix it upstream.
Reported by Noisytoot: going from level tut-1 to tut-2 returned the
hero's starting equipment too soon, and exiting the tutorial from
tut-2 let the hero keep any equipment acquired within the tutorial.
Entering and leaving the tutorial was being handled by lua code in
the level description of tut-1 and adding a second level messed that
up. I didn't see any way of handing that with level-specific lua
code so I made it become the core's responsibility. gotolevel()
knows when the hero is moving from one dungeon branch to another so
it can recognize entry to or exit from the tutorial easily.
While fixing this, prevent #invoke of the Eye of the Aethiopica from
offering the tutorial as a candidate destination (was feasible if it
had been entered at start of game).
Not fixed: levels visited in the tutorial become part of #overview.
Show location as "Tutorial:1" instead of "Dlvl:1" on status lines.
Only tested with tty; some interfaces handle location themselves and
may need their own fixup for this.
Fixes#1046
Engraving in an empty doorway and then using locking magic to create
a door there resulted in an impossible warning: "engraving sanity:
illegal surface (23)" if the 'sanity_check' option was On (wizard
mode only). Engraving in an open doorway and then simply closing
the existing door produced the same effect.
Accept engravings at closed doors. Presumably hero will be using
Passes_walls to attempt that so treat closed doors same as open ones.
Update the engraving sanity check to deal with that.
Bonus fix: engraving sanity checking stopped after the first problem
instead of checking every engraving. Have it continue instead.
Not fixed: vault wall repair and temporary corridor removal does
not delete engravings and can trigger the illegal surface warning if
player engraves before the repairs. I didn't test shop wall repair
but it doesn't have any engr references so probably has the same bug.
The revised #overview (displaying info in a menu instead of text
window), works as intended for tty and curses, during play and also
during end of game disclosure.
For X11 it worked during play but during disclosure it issued a
call to impossible() for every line of overview output and there
was no overview shown. ("add_menu: called before start_menu",
sent to stdout where the player may not have a chance to see it.)
For Qt things were worse, working during play but with indentation
that isn't what is intended, and during disclosure it crashed in
add_menu().
This avoids the impossible and the crash, by changing how the core
treats the menu rather than by updating how FOO_add_menu() deals
with the offending previous usage.
I suspect that to fix the Qt indentation, #overview might need to
be changed to behave like #attributes: use a menu during play but
a text window during disclosure. It has a hack for text windows
to switch from the default font to a fixed-width one if any line
in the text contains four consecutive spaces. Either menu windows
aren't doing the same thing, or the two-column layout they use to
render their text is messing that up. (I haven't looked.)
Require the 'm' prefix to treat #overview as a menu with selectable
entries. During end-of-game disclosure it shows every level that was
visited, but during play it only shows levels which have annotations
(typically automatically generated ones). The menu wasn't offering
any chance to add an annotation to levels without such, so force
'm #overview' to show every visited level. Continue to avoid that
(and also avoid the clutter introduced by menu entry selector letters)
for normal #overview.
relocate surface(), ceiling(), and avoid_ceiling() to dungeon.c
adjacent to has_ceiling() etc.
astral and fire, like airlevel and waterlevel return FALSE
for has_ceiling()
if a caller does happen to call ceiling() on fire level,
return "flames above"
if a caller does happen to call ceiling() on quest level,
return a more-generic "expanse above", instead of the
word "ceiling"
add "stairs" return to surface()
remove recent update to engrave.c to special-case "stairs"
since surface() will return that now
Add "walls of lava", basically lava which blocks vision and
require a bit more than just levitation or flight to move through.
No levels use this yet, as testing isn't thorough enough.
Add 17 fake objects to objects[], one for each object class. All
specific color as gray. They're grouped at the start--actually near
the start since "strange object" is still objects[0]--rather than
being among the objects for each class. init_object() knows to start
at [MAXOCLASSES] instead of [0]; other code that loops through every
object might need adjusting.
For potions, non-stone gems, and non-novel/non-Book_of_the_Dead
spellbooks that don't have obj->dknown set, display the corresponding
generic object rather the object itself. Fixes the longstanding bug
of seeing color for not-yet-seen objects whose primary distinguishing
characteristic is their color. Walking next to a generic object
while able to see its spot will set dknown and redraw as specific.
It's slightly disconcerting to have objects change as you reach them;
I hope it's just a matter of becoming used to that. (If there is any
code still changing the hero's location manually instead of using
u_on_newpos(), it should be changed to use that routine.)
Most of the new tiles are just a big rendering of punctuation
characters. The potion, gem, and spellbook ones could be cloned from
a specific object in their class and then have the color removed. I
started out that way but wasn't happy with the result. I'm not
artisticly inclined; hopefully someone else will do better. Each of
them is preceded by a comment beginning with "#_"; the underscore
isn't required, just being used to make the comments stand out a bit.
Invalidates existing save and bones files.
Just zero out the allocated memory.
Explicitly setting struct field values isn't enough, because field alignment
means there can be several unused bytes which are written to savefile.
The consolidation of global variables from scattered source
files into decl.c and declared in decl.h was begun in 3.7.0.
Their placement in common files was done for centralized
initialization and potential re-initialization during a
"play again" scenario.
It wasn't really necessary for all of them to be housed in a
single huge structure to meet the "play again" requirement,
and the single huge structure has been a little unwieldy when
it comes to maintenance.
Following this commit, instead of one single extremely large structure
named 'g' to house all of the relocated global variables, they
are distributed into several ga through gz.
To make things easy for the developer, each variable is placed
into the struct corresponding to the starting letter of the variable.
That way, no lookup is required in order to know which struct houses
a particular variable, it is a simple match to the starting letter
for all the centralized global variables.
A global variable named 'amulets', would be found in ga.
ga.amulets
^ ^
A global varable named 'move', would be found in gm.
gm.moves
^ ^
A global variable named 'val_for_n_or_more' would be found in gv.
gv.val_for_n_or_more
^ ^
A global variable named 'youmonst' would be found in gy.
gy.youmonst
^ ^
Although gcc specifies support for declaring a function as
noreturn after the function name and parameters, other compilers
do so via an attribute at the start of the declaration. Add some
macro support for the attribute-at-the-beginning method:
o MS Visual Studio compiler
o Upcoming C23 standard (untested at this point)
Condense the setup of PCHAR/PCHAR2 and OBJCLASS/OBJCLASS2 (last one
renamed from OBJCLASS7) so that it's easier to see the variations
at once on an ordinary size terminal/window. Revise some of the
indentation and other spacing, also to try to enhance readability
a little.
Unrelated: remove a trailing space that crept in with a recent pull
request.
Permit levelport by name to "delphi". That is what the Oracle calls the
level (or at least her room) in-game, so it seems like a natural guess
for the name of the level.
Instead of using index() macro defined to strchr, use C99 strchr.
Instead of using rindex() macro defined to strrchr, use C99 strrchr.
If you want to try building on a platform that doesn't offer those
two functions, these are available:
define NOT_C99 /* to make some non-C99 code available */
define NEED_INDEX /* to define a macro for index() */
define NEED_RINDX /* to define a macro for rindex() */
Formatting became strange when '#ifdef WIZARD' became unconditional
several years ago, then the bitmask testing code itself became a bit
strange when amnesia got changed a couple of years ago.
(unconnected to the current dungeon branch, that is)
Level teleporting allows you to type in the name of a level instead of
its number. This normally only works for levels within your current
dungeon branch (main dungeon <-> Gehennom levelports are the exception):
entering "medusa" as a destination won't work while the hero is in the
Gnomish Mines, but it will work fine to get to the Medusa level from
elsewhere in the main dungeon.
Teleporting to a particular branch entrance didn't apply the same
restriction. The teleport would still happen even if the destination
branch was unreachable from the current branch, and in such a case the
game would just try to get the hero to the depth of the branch entrance,
within the current branch. For example, entering "quest" as a
destination within the Gnomish Mines would bring the hero to Mines' End,
since that's the closest depth-wise it's possible to get to the quest
portal level. Apply the same rules to branch entrances as exist for
named levels, excluding destinations that are unreachable from the
current branch.
One of the drivers of this change was that screen coordinates require a
type that can hold values greater than 127. Parameters to the window
port routines require a large type in order to be able to have values
a fair bit larger than COLNO and ROWNO passed to them, particularly for
their use to the right of the map window.
This splits the uses of xchar into 3 different situations, and adjusts
their type and size:
xchar
|
-----------------------
| | |
coordxy xint16 xint8
coordxy: Actual x or y coordinates for various things (moved to 16-bits).
xint16: Same data size as coordxy, but for non-coordinate use (16-bits).
xint8: There are only a few use cases initially, where it was very
plain to see that the variable could remain as 8-bits, rather
than be bumped to 16-bits. There are probably more such cases
that could be changed after additional review.
Note: This first changed all xchar variables to coordxy. Some were
reviewed and got changed to xint16 or xint8 when it became apparent that
their usage was not for coordinates.
This increments EDITLEVEL in patchlevel.h
(user-side decisions really, but as it stands right now
user-side decisions/options are made and processed by the core)
add a parameter to add_menu so color can be passed
This is enough to prevent abuse by denying access to functions and
denial of service (RAM and instruction step limits), but not enough
to allow restricted use of things that require finer control (e.g.
filesystem access).
If something goes wrong, the whole thing can be turned off, for
now, in config.h (see NHL_SANDBOX).
None of the current functionality requires changes to build systems;
some of the possible future functionality may require some #defines
- TBD. There is lots of dead code (#ifdef notyet) for bits of that
additional functionality; we can rip it out if we don't want those
additions or we can complete (parts of) it depending on our needs.
All current uses of Lua are connected to sandboxes and guarded with
nhl_pcall (sandbox and lua_pcall wrapper); options and limits can
be set at the callsites in the passed nhl_sandbox_info. Some of
the error handling may be wrong - panic() vs. impossible() vs
silence.
Memory and instruction step limits should be tuned prior to release;
there's no point tuning them now.
Instead of returning 0 or 1, we'll now use ECMD_OK or ECMD_TURN.
These have the same meaning as the hardcoded numbers; ECMD_TURN
means the command uses a turn.
In future, could add eg. a flag denoting "user cancelled command"
or "command failed", and should clear eg. the cmdq.
Mostly this was simply replacing return values with the defines
in the extended commands, so hopefully I didn't break anything.
Reported and diagnosed by entrez:
recalc_mapseen() has been looking at all slots in u.urooms[] rather
than stopping at the first \0. Since the whole array doesn't get
zeroed out when the value changes, stale room indices might follow
that first \0 and any rooms those referred to would erroneously get
flagged as having been visited by overview updates. Wouldn't matter
for the level where the stale indices got set, since you'd have to
have been in those rooms for it to happen, but would matter once you
moved on to other levels.
Having the opposite side of the stairs up from level 1 be unknown is
weird because the hero conceptually just came down those stairs at
the start of the game. But it's tricky because remote destination
varies depending on whether the Amulet is being carried. This gives
an accurate description of where the stairs lead (if you step on
them with the 'mention_decor' option On, or use the ':' command when
already on them).
|"There is a staircase up out of the dungeon here."
or
|"There is a branch staircase up to the Elemental Planes here."
It gives away a little information when carrying the Amulet, but not
much and anyone who gets that far deserves a break.
New routine known_branch_stairs() was performing two different things
and was unnecessarly complicated because of that. Split off newer
routine stairs_description() to handle one of those.
First cut at displaying branch stairs/ladder up/down as ordinary
stairs/ladder up/down if the destination hasn't been visited yet.
Stepping on stairs with 'mention_decor' enabled, or using ':' when
already on them, will report regular stairs' destination level.
Probably not very useful since it's just N+1 for downstairs or N-1
for upstairs when currently on level N.
It's based on whether the destination level has been visited, not
on whether the stairs have been traversed, so reaching a level via
trap or level teleporation can make the level's stairs known when
their destination really shouldn't be discovered yet.
From newsgroup discussion where slash'em changes have revealed a
latent nethack bug: prevent placing level teleporters in single-
level branches. The Knox level doesn't have any level teleporters
(or random traps) but wizard mode wishing could create them there.
They wouldn't do anything because the only possible destination
would be the same level. Pushing a boulder onto one used to trigger
an infinite loop (and still does in slash'em, which has other
single-level branches besides Ft.Ludios) trying to relocate it.
Boulder pushing was changed 15 years ago to prevent the infinite
loop and to avoid giving "the boulder disappears" message when a
level teleporter failed, but rolling boulder traversal lacked that
same change--it wasn't vulnerable to looping but could give an
inaccurate message claiming that the boulder disappeared when it
actually didn't. Fixing this is a bit late; rolling boulder trap
creation was recently changed to not choose a path that rolls over
teleportation or level tele traps any more.
Report states that the dungeon overview menu doesn't honor the
'menu_headings' option. However, dungeon overview is not a menu.
Despite that, switch its hardcoded use of bold and inverse to use
the option value instead. It doesn't really need two different
highlights and this allows user to control which video attribute
gets used. If someone wants different highlighting for overview
than for menus, they're out of luck.
Fixes#481
Implement a better fix for commit 2f4f7d22d ("Fix align type
mixup wth align mask") which was reverted in commit 4e35e8b5a
("Revert "Fix align type mixup wth align mask"").
In the present code, the field align in both struct altar and
struct monster is not an aligntyp, but an align mask with extra flags.
Change the type to match its actual use and improve the naming.
Consolidate duplicated code into a single routine.
Change the return type of induced_align() to be unsigned to match
amask usage.
Change the special level align mask values to be separate from
the normal align mask values.
Whitelist all the verified existing triggers:
makedefs.c: In function ‘name_file’
attrib.c: one compiler balks at a ? b : c for fmtstring
cmd.c: In function ‘extcmd_via_menu’
cmd.c: In function ‘wiz_levltyp_legend’
do.c: In function ‘goto_level’
do_name.c: In function ‘coord_desc’
dungeon.c: In function ‘overview_stats’
eat.c: one compiler balks at a ? b : c for fmtstring
end.c: one compiler balks at a ? b : c for fmtstring
engrave.c: In function ‘engr_stats’
hack:c one compiler balks at a ? b : c for fmtstring
hacklib.c: one compiler balks at a ? b : c for fmtstring
insight.c: one compiler balks at a ? b : c for fmtstring
invent.c: In function ‘let_to_name’
light.c: In function ‘light_stats’
mhitm.c: In function ‘missmm’
options.c: In function ‘handler_symset’
options.c: In function ‘basic_menu_colors’
options.c: In function ‘optfn_o_autopickup_exceptions’
options.c: In function ‘optfn_o_menu_colors’
options.c: In function ‘optfn_o_message_types’
options.c: In function ‘optfn_o_status_cond’
options.c: In function ‘optfn_o_status_hilites’
options.c: In function ‘doset’
options.c: In function ‘doset_add_menu’
options.c: In function ‘show_menu_controls’
options.c: In function ‘handle_add_list_remove’
pager.c: In function ‘do_supplemental_info’
pager.c: In function ‘dohelp’
region.c: In function ‘region_stats’
rumors.c: sscanf usage
sounds.c: In function ‘domonnoise’
spell.c: In function ‘dospellmenu’
timeout.c: In function ‘timer_stats’
topten.c: In function ‘outentry’, fscanf, sscanf, fprintf usage
windows.c: In function ‘genl_status_update’
zap.c: one compiler balks at a ? b : c for fmtstring
win/curses/cursstat.c: In function ‘curses_status_update’
win/tty/wintty.c: In function ‘tty_status_update’
win/win32/mswproc.c: In function ‘mswin_status_update’
clear some -Wformat-overflow warnings being experienced with
i586-pc-msdosdjgpp-gcc (GCC) 10.2.0 cross-compiler
--
Warnings log:
botl.c: In function 'status_hilite_menu_add':
botl.c:3661:38: warning: ' or ' directive writing 4 bytes into a region of size between 1 and 80 [-Wformat-overflow=]
3661 | Sprintf(obuf, "%s or %s",
| ^~~~
In file included from ../include/config.h:631,
from ../include/hack.h:10,
from botl.c:6:
../include/global.h:274:24: note: 'sprintf' output between 5 and 163 bytes into a destination of size 80
274 | #define Sprintf (void) sprintf
botl.c:3661:21: note: in expansion of macro 'Sprintf'
3661 | Sprintf(obuf, "%s or %s",
| ^~~~~~~
do_name.c: In function 'getpos_menu':
do_name.c:594:37: warning: 'sprintf' may write a terminating nul past the end of the destination [-Wformat-overflow=]
594 | Sprintf(fullbuf, "%s%s%s", firstmatch,
| ^
In file included from ../include/config.h:631,
from ../include/hack.h:10,
from do_name.c:6:
../include/global.h:274:24: note: 'sprintf' output 1 or more bytes (assuming 257) into a destination of size 256
274 | #define Sprintf (void) sprintf
do_name.c:594:13: note: in expansion of macro 'Sprintf'
594 | Sprintf(fullbuf, "%s%s%s", firstmatch,
| ^~~~~~~
dungeon.c: In function 'print_dungeon':
dungeon.c:2172:27: warning: '%s' directive writing up to 1407 bytes into a region of size 256 [-Wformat-overflow=]
2172 | Sprintf(buf, "%s: %s %d", dptr->dname, descr, dptr->depth_start);
| ^~
In file included from ../include/config.h:631,
from ../include/hack.h:10,
from dungeon.c:6:
../include/global.h:274:24: note: 'sprintf' output between 10 and 1427 bytes into a destination of size 256
274 | #define Sprintf (void) sprintf
dungeon.c:2172:13: note: in expansion of macro 'Sprintf'
2172 | Sprintf(buf, "%s: %s %d", dptr->dname, descr, dptr->depth_start);
| ^~~~~~~
dungeon.c:2169:27: warning: '%s' directive writing up to 1407 bytes into a region of size 256 [-Wformat-overflow=]
2169 | Sprintf(buf, "%s: %s %d to %d", dptr->dname, makeplural(descr),
| ^~
dungeon.c:2169:26: note: directive argument in the range [-2147483647, 2147483646]
2169 | Sprintf(buf, "%s: %s %d to %d", dptr->dname, makeplural(descr),
| ^~~~~~~~~~~~~~~~~
In file included from ../include/config.h:631,
from ../include/hack.h:10,
from dungeon.c:6:
../include/global.h:274:24: note: 'sprintf' output 10 or more bytes (assuming 1427) into a destination of size 256
274 | #define Sprintf (void) sprintf
dungeon.c:2169:13: note: in expansion of macro 'Sprintf'
2169 | Sprintf(buf, "%s: %s %d to %d", dptr->dname, makeplural(descr),
| ^~~~~~~
dungeon.c: In function 'print_mapseen':
dungeon.c:3185:33: warning: '%s' directive writing up to 255 bytes into a region of size 249 [-Wformat-overflow=]
3185 | Sprintf(outbuf, " (play %s to open or close drawbridge)", tmp);
| ^~ ~~~
In file included from ../include/config.h:631,
from ../include/hack.h:10,
from dungeon.c:6:
../include/global.h:274:24: note: 'sprintf' output between 37 and 292 bytes into a destination of size 256
274 | #define Sprintf (void) sprintf
dungeon.c:3185:9: note: in expansion of macro 'Sprintf'
3185 | Sprintf(outbuf, " (play %s to open or close drawbridge)", tmp);
| ^~~~~~~
dungeon.c:3350:35: warning: '%s' directive writing up to 255 bytes into a region of size 240 [-Wformat-overflow=]
3350 | Sprintf(buf, "%sThe castle%s.", PREFIX, tunesuffix(mptr, tmpbuf));
| ^~
In file included from ../include/config.h:631,
from ../include/hack.h:10,
from dungeon.c:6:
../include/global.h:274:24: note: 'sprintf' output between 18 and 273 bytes into a destination of size 256
274 | #define Sprintf (void) sprintf
dungeon.c:3350:9: note: in expansion of macro 'Sprintf'
3350 | Sprintf(buf, "%sThe castle%s.", PREFIX, tunesuffix(mptr, tmpbuf));
| ^~~~~~~
explode.c:541:69: warning: '%s' directive writing up to 255 bytes into a region of size 236 [-Wformat-overflow=]
541 | Sprintf(g.killer.name, "caught %sself in %s own %s", uhim(),
| ^~
In file included from ../include/config.h:631,
from ../include/hack.h:10,
from explode.c:5:
../include/global.h:274:24: note: 'sprintf' output 21 or more bytes (assuming 276) into a destination of size 256
274 | #define Sprintf (void) sprintf
explode.c:541:21: note: in expansion of macro 'Sprintf'
541 | Sprintf(g.killer.name, "caught %sself in %s own %s", uhim(),
| ^~~~~~~
hacklib.c: In function 'yyyymmddhhmmss':
hacklib.c:1034:28: warning: '%02d' directive writing between 2 and 11 bytes into a region of size between 4 and 11 [-Wformat-overflow=]
1034 | Sprintf(datestr, "%04ld%02d%02d%02d%02d%02d", datenum, lt->tm_mon + 1,
| ^~~~
hacklib.c:1034:22: note: directive argument in the range [-2147483647, 2147483647]
1034 | Sprintf(datestr, "%04ld%02d%02d%02d%02d%02d", datenum, lt->tm_mon + 1,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ../include/config.h:631,
from ../include/hack.h:10,
from hacklib.c:7:
../include/global.h:274:24: note: 'sprintf' output between 15 and 67 bytes into a destination of size 15
274 | #define Sprintf (void) sprintf
hacklib.c:1034:5: note: in expansion of macro 'Sprintf'
1034 | Sprintf(datestr, "%04ld%02d%02d%02d%02d%02d", datenum, lt->tm_mon + 1,
| ^~~~~~~