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
Delete engravings made in a breach of a shop's wall or of a vault's
wall or in the guard's temporary corridor when the wall is repaired
or the corridor removed. If 'sanity_check' was On, those would
trigger impossible warning "engraving sanity: illegal surface (x)"
where x was the terrain type code for solid rock or relevant walls.
Adding del_engr_at() calls to the shop code was straightforward.
The vault code is very complicated and I'm not sure that all the
calls I added were actually necessary.
sound_verbal(char *text, int32_t gender, int32_t tone, int32_t vol,
int32_t moreinfo);
-- NetHack will call this function when it wants to pass text of
spoken language by a character or creature within the game.
-- text is a transcript of what has been spoken.
-- gender indicates MALE or FEMALE sounding voice.
-- tone indicates the tone of the voice.
-- vol is the volume (1% - 100%) for the sound.
-- moreinfo is used to provide additional information to the soundlib.
-- there may be some accessibility uses for this function.
It may be useful for accessibility purposes too.
A preliminary implementation has been attempted for macsound to test
the interface on macOS. No tinkering of the voices has been done.
Use of the test implementation requires the following at build time with make.
WANT_SPEECH=1
That needs to be included on the make command line to enable the test code,
otherwise just the interface update is compiled in.
I don't know for certain when AVSpeechSynthesizer went into macOS, but older versions
likely don't support it, and would just leave off the WANT_SPEECH=1.
If built with WANT_SPEECH=1, the 'voices' NetHack option needs to be enabled.
It was a bit strange, when I first started up the test, to hear Asidonhopo,
the shopkeeper, talking to me as I entered his shop and interacted with him.
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
^ ^
GCCs older than 3.1 understand __attribute__(printf(...)), but only
with functions; it doesn't work with function pointers. This change
uses PRINTF_F_PTR to remove the attribute from two function pointers.
This change establishes GCC 3.0 as the minimum version to build
NetHack. Older versions have trouble with the variadic macros and
variable declarations in mid-block.
Reported seven and a half years ago: if you are in a vault but not
carrying any gold and the guard arrives, you're told "Follow me."
Then if you pick up gold while the guard is still in the wall breach
rather than out in the corridor, you would be told "I repeat, drop
that gold and follow me!" "Repeat" refers to the follow part but
sounds as if it refers to the drop-gold part which isn't actually
being repeated. Keep track of whether the guard has issued a drop
gold demand and use that to vary the wording of subsequent "I repeat"
message.
Modifies monst->mextra->egd so save and bones files are invalidated.
If the first monster on the migrating_mons list couldn't arrive and
was put back on the list to try again later, 'later' would happen
immediately and the program looped forever trying and failing to
bring that monster to the level.
Defer repeat attempts at migration until losedogs() has been through
the whole migrating_mons list. mon_arrive() now populates a new
list called failed_arrivals and losedog() moves its contents, if any,
to migrating_mons prior to returning.
Extend findgd() to bring a migrating guard back 'early' if there is
one and an active guard is wanted but none is present on the level.
It the level is full then the found guard is likely to be sent into
limbo (scheduled to migrate back), so one can end up moving back and
forth. Unlikely to occur during normal play.
Also, when a guard is needed and there's a dead one parked at <0,0>,
revive it. The dead guard has temporary corridor info in its
mon->mextra->egd that a new one won't have. (This might introduce
unexpected changes in vault behavior but if so, the old behavior was
probably buggy.)
When the hero is in a vault, don't find a guard unless u.uinvault is
ready to bring it into play. It was finding one every turn and then
ignoring the result for 29 turns out of 30.
Change guard appearance timing: the first appearance is still after
30 turns, but if it goes away, bring it back after 15 more turns
rather than another 30 and repeat as needed.
The bookkeeping for number of dead or removed monsters got out of sync
if a shopkeeper, temple priest, or vault guard on the migrating_mons
list who was scheduled to return to the current level got passed to
mongone() when #wizmakemap destroyed the current level in order to
replace it.
When getting rid of such monsters, first put them on fmon. Once 'gone'
they'll still be on that list and dmonsfree() will include them in the
tally of dead or removed monsters and hopefully the count will match
the number it thinks were pending.
This works for a vault guard; I didn't try for shopkeeper or priest.
Ditch pet(s) so that they won't kill stuff and open up map spots while
you're waiting for guard activity; enter vault away from the wall
nearest to 'civilization'; fill the level with lichens or other junk;
wait for guard to arrive; don't drop gold. After a short while the
guard will try to teleport next to you but with the level full will
end up in limbo instead (migrating, scheduled to come back to current
level). Then perform #wizmakemap. Prior to this patch, there will be
an impossible about N removed not matching N+1 pending; after it, no
such impossible.
When a vault guard was created it was producing a "guard appears"
message, then the vault code immediately produced a "vault's guard
enters" message. Suppress the creation message.
While testing that, I noticed that if the hero was blind and lacked
telepathy, "someone enters" started the guard interrogation sequence
but if hero answered and dropped gold, the way out wasn't discernable.
Put a "remembered, unseen monster" glyph at the guard's spot in the
breeched vault wall. The player will need to do <search><move> over
and over to actually follow the guard but at least will know where to
start doing that.
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
Teleporting a monster only updated the map. Give a message
so blind players can get the same information.
Making a monster invisible gives the same message, if you
cannot detect invisible.
Several other places where monsters teleported themselves
now also give the same message.
I thought there were more places that checked for "it" and substituted
"someone" or "something". Perhaps there are and I'm just not finding
them now. Anyway, this extends x_monnam() and adds some_mon_nam() and
Some_Monnam() to do that during monster name formatting instead of
having various bits of code try fix it up after the fact. The fixups
could be fooled by monsters given the name "it" or "It"; x_monnam()
won't be.
Noticed when testing the "wall_angle: unknown" fix, if there is a
boulder rather than a door in the breach in a vault's walls at the
spot where the guard arrives, the guard would walk onto it, tell the
hero to drop any gold and follow, then move back. The boulder would
remain in the hero's way and couldn't be pushed because the guard was
in the boulder's way. Have the guard smash any such boulder(s) into
rocks when arriving (with no explanation for how that is accomplished,
just a message about seeing or hearing boulder(s) shatter). Later
when repairing the walls, delete any rocks or boulders at all vault
wall locations (even when no gap-to-wall repair is needed).
Fix the vault repair issue that could lead to "wall_angle: unknown"
warning. Unlike shop repair, the original wall info isn't available
so this recreates it. The extra 'flags' field added yesterday could
be eliminated but this leaves it in place.
Fixes#606
triggering an impossible warning about "wall_angle: unknown" due
to the known conflict between door state and wall info which both
overlay the flags field for map locations.
Reported and diagnosed by vultur-cadens: if a shop's wall was dug
open, followed by use of locking magic to plug the gap with a door,
and then unlocking that door, the D_CLOSED door flag was left as
invalid wall_info when shop damage was repaired. Map re-display
complained. Leaving the door locked or opening it after unlocking
did not result in any complaint because the values for those door
states do not conflict with wall angle values.
The problem was reproducible and is now fixed by adding an extra
field to the shop damage structure. A similar change has been
made to the vault guard's 'fake corridor' structure but I have no
test case for that so don't know whether it makes any difference.
At least it doesn't seem to have broken anything.
Existing save and bones files are invalidated by the fixes.
Fixes#606
add MALE, FEMALE, and gender-neutral names for individual monster species
to the mons array. The gender-neutral name (NEUTRAL) is mandatory, the
MALE and FEMALE versions are not.
replace code uses of the mname field of permonst with one of the three
potentially-available gender-specific names.
consolidate some separate mons entries that differed only by species into a
single mons entry (caveman, cavewoman and priest,priestess etc.)
consolidate several "* lord" and "* queen/* king" monst entries into
their single species, and allow both genders on some where it makes some
sense (there is probably more work and cleanup to come out of this at some
point, and the chosen gender-neutral name variations are not cast in stone
if someone has better suggestions).
related function or macro additions:
pmname(pm, gender) to get the gender variation of the permonst name. It
guards against monsters that haven't got anything except NEUTRAL naming
and falls back to the NEUTRAL version if FEMALE and MALE versions are
missing.
Ugender to obtain the current hero gender.
Mgender(mtmp) to obtain the gender of a monster
While the code can safely refer directly to pmnames[NEUTRAL] safely in the
code because it always exists, the other two (pmnames[MALE] and
pmnames[FEMALE] may not exist so use:
pmname(ptr, gidx)
where -ptr is a permonst *
-gidx is an index into the pmnames array field of the
permonst struct
pmname() checks for a valid index and checks for null-pointers for
pmnames[MALE] and pmnames[FEMALE], and will fall back to pmnames[NEUTRAL] if
the pointer requested if the requested variation is unavailable, or if the
gidx is out-of-range.
Allow code to specify makemon flags to request female or male (via MM_MALE
and MM_FEMALE flags respectively)to makedefs, since the species alone doesn't
distinguish male/female anymore. Specifying MM_MALE or MM_FEMALE won't
override the pm M2_MALE and M2_FEMALE flags on a mons[] entry.
male and female tiles have been added to win/share/monsters.txt.
The majority are duplicated placeholders except for those that were
separate mons entries before. Perhaps someone will contribute artwork in the
future to make the male and female variations visually distinguishable.
tilemapping via has the MALE tile indexes in the glyph2tile[]
array produced at build time. If a window port has information that the
FEMALE tile is required, it just has to increment the index returned
from the glyph2tile[] array by 1.
statues already preserved gender of the monster through STATUE_FEMALE
and STATUE_MALE, so ensure that pmnames takes that into consideration.
I expect some refinement will be required after broad play-testing puts it to
the test.
consolidate caveman,cavewoman and priest,priestess monst.c entries etc
This commit will require a bump of editlevel in patchlevel.h because it alters
the index numbers of the monsters due to the consolidation of some. Those
index numbers are saved in some other structures, even though the mons[] array
itself is not part of the savefile.
Window Port Interface Change
Also add a parameter to print_glyph to convey additional information beyond
the glyph to the window ports. Every single window port was calling back to
mapglyph for the information anyway, so just included it in the interface and
produce the information right in the display core.
The mapglyph() function uses will be eliminated, although there are still some
in the code yet to be dealt with.
win32, tty, x11, Qt, msdos window ports have all had adjustments done to
utilize the new parameter instead of calling mapglyph, but some of those
window ports have not been thoroughly tested since the changes.
Interface change additional info:
print_glyph(window, x, y, glyph, bkglyph, *glyphmod)
-- Print the glyph at (x,y) on the given window. Glyphs are
integers at the interface, mapped to whatever the window-
port wants (symbol, font, color, attributes, ...there's
a 1-1 map between glyphs and distinct things on the map).
-- bkglyph is a background glyph for potential use by some
graphical or tiled environments to allow the depiction
to fall against a background consistent with the grid
around x,y. If bkglyph is NO_GLYPH, then the parameter
should be ignored (do nothing with it).
-- glyphmod provides extended information about the glyph
that window ports can use to enhance the display in
various ways.
unsigned int glyphmod[NUM_GLYPHMOD]
where:
glyphmod[GM_TTYCHAR] is the text characters associated
with the original NetHack display.
glyphmod[GM_FLAGS] are the special flags that denote
additional information that window
ports can use.
glyphmod[GM_COLOR] is the text character
color associated with the original
NetHack display.
Support for including the glyphmod info in the display glyph buffer
alongside the glyph itself was added and is the default operation.
That can be turned off by defining UNBUFFERED_GLYPHMOD at compile time.
With UNBUFFERED_GLYPHMOD operation, a call will be placed to map_glyphmod()
immediately prior to every print_glyph() call.
When SCORE_ON_BOTL is enabled, you could tell how much gold is
inside a container with unknown contents by having 'showsore' On
and watching how much the score changed on the status line when
picking the container up.
For some reason the vault guard fake corridor code checked
if the hero was outside the corridor before removing monsters.
But the vault end of the corridor usually gets sealed off
even while you're still in the corridor. This left monsters
stuck in solid rock.
When GOLDOBJ was activated unconditionally, several texts started referencing
"money" instead of "gold".
As we don't have the intention to introduce a complex coin system with
different denominations, change it back and also some other places that
reference "money".
... when the guard is angry, and he's in the vault or in his corridor,
you're not in the vault nor in his corridor, and the level is full
so the guard cannot relocate.
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.
Do late message suppression in a different fashion. Also, there are
more messages than shk taking hero's possessions and guard taking
hero's gold that need to be suppressed if regular message delivery
is no longer possible: "do not pass Go", "you arise from the grave
as a foo", "the corridor disappears", "you are encased in the rock".
Those last two are from vault handling but take place in a convoluted
manner: paygd -> mongone -> grddead -> clear_fcorr.
Closing nethack's window sets 'program_state.stopprint' to inhibit
disclosure interaction, but shopkeeper claiming hero's stuff or vault
guard claiming hero's gold didn't honor that and just issued normal
pline messages. For win32, they got delivered in a popup even though
nethack's window had gone away.
Make those two end-of-game situations honor 'program_state.stopprint'.
[Fix not tested on win32...]
Fixes#101
If you tell the vault guard your name, drop carried gold, wait one
turn, then pick up the gold again, the guard will move a step away
during the wait. If you teleport away, the guard will seal vault
walls and then park himself on the one-square (so far) temporary
corridor adjacent to the vault wall. Periodically he'll say "Move
along!" and the hero will hear it, regardless of location on the
level. Unless you dig a corridor to rescue him, or one or more of
the vault's walls get breached again, he will never move.
The report emphasized that you could use this to steal the vault's
gold, but it relies on being able to teleport beyond the gaurd's
reach and if you can do that, you might as well do so before the
guard comes. The stranded guard, and him saying "Move along!" when
no longer leading hero out of the vault, are more significant bugs.
Bonus fix: if the game ends and the guard seals up the vault while
the hero is in a spot that gets fixed up (vault wall or temporary
corridor) don't give the "You are encased in the rock" message if
game end was due to death rather than quit.