The pull request included some changes that were neither accidental nor
unintentional, so only a subset of the changes from pull request #869
submitted by klorpa were manually applied.
behaviour -> behavior
speach -> speech
knowlege -> knowledge
incrments -> increments
stethscope -> stethoscope
staiway -> stairway
arifact -> artifact
extracing -> extracting
The uses of "iff" were left alone.
Close#869
Issue #722 posted by copperwater and commented on by Entrez: both
shk_your() and the() inserted "the" in front of a unique monster's
corpse, yielding "the Lord Surtur's corpse glows iridescently" and
"the Lord Surtur's corpse drops to the floor."
Teach both of those routines to skip "the" when used for monsters
with personal names. It now omits "the" for "Medusa's corpse" but
still gives "the Oracle's corpse".
shk_your() operates on an object and can deal explicitly with corpses
of named monsters. the() operates on text and has to guess whether
it is being used in a similar situation. Right now the guess is just
"is there an apostrophe present?" and might need further refinement.
Fixes#722
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.
If #wizrumorscheck isn't able to open or read the rumors file,
continue on to check engravings, epitaphs, and bogusmons instead of
immediately returning.
Simplify the handling of capitalized hallucinatory monster names
which should be described as "the Bogon" rather than just "Bogon".
Instead of inserting an ESC character to indicate "this entry
should be freed when done", keep track of how many of the entries
come from mons[] (which always come first and shouldn't be freed)
and just free the rest. So one instance of inserting an ESC and a
couple of skipping it when present and one of testing for it when
freeing are removed.
Also, the check for bogusmons classification code was testing
whether the name started with a letter (or with '@', an obscure
special case for letter()) rather than for the actual type codes
used in dat/bogusmons.txt. If '@' becomes used as a classification
code (so far it isn't one) any entries using it could have been
misclassified and would be misspelled because it would stay as the
first character. And existing entry "/r/tard" was also subject to
misclassification, but since it doesn't start with a capital letter
either with or without leading '/' that had no noticeable effect.
If the leading slash was followed by a capital letter there would
have been a different sort of misspelling with that slash missing
in the copy kept in CapMons[]. I knew all that when I first used
letter() but have decided that it is better to extract 'bogon_types'
from bogusmon() in order for the handling to be more consistent.
The code to provide even distribution for rumors was being successfully
used for engravings, epitaphs, and hallucinatory monster names but not
actually for rumors themselves. Move it into its own routine and have
both the three miscellaneous things and rumors use that.
My testing has verified that asking for a true rumor can produce the
first and last true rumors and not either of the first or last false
rumors, and vice versa when asking for a false rumor. Asking for
unspecified true/false can produce all four of those. Aside from that
verification of correctness (I hope...), I haven't checked that the
distribution when selecting is actually even.
Solve the uneven distribution situation that has been present for
picking random rumors for a long time and for random engravings,
epitaphs, and hallucinatory monster names since 3.6.0. This relies
on the previous partial solution where short lines have been padded
to a longer length. When that length is N and random seek lands in
a long line of length L, retry if the position is in the first L-N
characters. Put differently, it if takes more than N characters to
reach the next newline, reject that random seek and try again. This
effectively makes long lines behave as if they had the same length
of N as the short lines have been padded to and when all lines are
the same length, all entries have the same chance to be chosen.
Two semi-related patches within the span of less than three days and
I overlooked the overlap. When the() deals with a capitalized string,
it calls CapitalMon() to check whether the string matches a monster
and if so whether usage should be "the Monster" (Oracle or Olog-hai
for example) or just "Monster" (Medusa). The first time that gets
called, it collects all capitalized non-the Monsters from both mons[]
and the bogusmon file. The latter just got changed to pad short
lines, and that works fine for selecting hallucinatory monsters at
random via get_rnd_text(), but non-the Monster collection processes
the bogusmon file directly and wasn't updated to strip the padding.
While testing 'the("Capitalized Hallucinatory Monster")' I noticed that
some hallucinatory monsters showed up more often than others. When
the random engravings, epitaphs, and bogus monsters were converted from
hard-coded arrays to data files accessed by random seek (3.6.0), they
became subject to the same distribution irregularites that rumors suffer
from. The chance that an entry will be chosen depends upon the chance
that a random seek will hit somewhere in the line which precedes it, so
entries that follow long lines are more likely to be chosen and entries
that follow short lines are less likely. We improved that for rumors
by having makedefs pad the shortest lines. Distribution still isn't
uniform but is much better than it was (and could be further improved
with a longer padding length at the cost of making data files bigger so
possibly slower to access; both overall size and access speed mattered
back when floppy discs were supported but are probably irrelevant now).
Start doing the same thing for the newer files: pad the shortest lines
to increase the chance that seek will find them. The tradeoff is that
the data files become bigger. Rumors, engravings, and epitaphs lines
are all at least 60 characters now; bogus monsters are at least 20.
These are the data file sizes I see (in bytes: old, new; padding for
rumors was already in use so its size hasn't changed):
bogusmon 4449 7211
engrave 1326 2894
epitaph 14159 24075
rumors 49173 49173
The only place that padding is noticeable in-game is #wizrumorcheck.
Function the() wasn't supposed to be used for monsters because many
of the ones with capitalized names confuse it, but over time multiple
instances of the(mon_nam()) have crept into the code. Instead of
ripping those out, modify the() to handle that situation better.
Pull request #636 by entrez dealt with this with one extra line of
code, but could end up scanning all the names in mons[] repeatedly
if the("Capitalized string") gets called a lot. This uses a similar
one line fix but calls a whole new routine that scans through mons[]
once collecting all the relevant special case names. As a bonus,
it does the same for hallucinatory monster names which name_to_mon()
couldn't handle.
Fixes#626
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’
Starting out replacing ambiguous pronoun "it" since it might seem
to be referring to "--More--" rather than to "...", then ended up
rewriting whole paragraph.
Fixes#369.
Fixes#370.
The default entries inserted by makedefs -s (starting in 3.6.6,
to guard against having an empty data file which led to divide by
zero crash when nethack picked a random entry) lacked a terminating
newline so the first entry from the file (for the usual case when
that data file wasn't empty) got implicitly concatenated to it.
If the first entry got chosen during play, the initial portion
corresponding to the default entry was decrypted properly but the
concatenated portion corresponding to file's first line didn't.
So gibberish was appended to default engraving or epitaph or bogus
monster; also, the input file's first line would never appear.
The newline fix in makedefs is different from pull request #370
but accomplishes the same thing.
The bulk of the patch is an enhancement to #wizrumorcheck to show
first (default inserted by makedefs), second (first in input file)
and last engravings, epitaphs, and bogusmons in addition to rumors.
The command name has become a little misleading but the limited
functionality doesn't call for separate commands.
If either rumors.tru or rumors.fal was empty when makedefs made
'rumors', init_rumors() will set true_rumor_size to -1 to indicate
that rumors aren't available. It also closes the input file, and
then #wizrumorcheck closed that again, triggering a crash in the
dlb code.
Fortune cookies and oracles work ok (just not very interesting)
when rumors aren't available. Only the check command had trouble
with that.
The traceback points directly to the problem: divide by 0 happens
if the 'bogusmon' file only contains the "do not edit" line, which
would happen if 'bogusmon.txt' is empty. makedefs probably ought to
complain about that.
There is now one hardcoded bogus monster to fall back to: 'bogon'.
Random tombstone epitaphs report divide by 0 if their text source is
empty, but it is done by rn2() rather than rn2_for_display_rng() so
is just a warning for pre-release code. It would crash for release
version though.
I tried placing an empty engravings file and expected similar results
but didn't see any response. Not sure what that means.
After the fix, empty epitaph file yields blank result so graves that
want a random epitaph won't have any epitaph.
Fixes#302
The traceback points directly to the problem: divide by 0 happens
if the 'bogusmon' file only contains the "do not edit" line, which
would happen if 'bogusmon.txt' is empty. makedefs probably ought to
complain about that.
There is now one hardcoded bogus monster to fall back to: 'bogon'.
Random tombstone epitaphs report divide by 0 if their text source is
empty, but it is done by rn2() rather than rn2_for_display_rng() so
is just a warning for pre-release code. It would crash for release
version though.
I tried placing an empty engravings file and expected similar results
but didn't see any response. Not sure what that means.
After the fix, empty epitaph file yields blank result so graves that
want a random epitaph won't have any epitaph.
Fixes#302
Introduce eight achievements that can be attained by more players.
Entered Gnomish Mines - self explanatory
Entered Mine Town - the town portion, not just the level
Entered a shop - any tended shop on any level
Entered a temple - likewise for temple
Consulted the Oracle - bought at least one major or minor oracle
Read a Discworld Novel - read at least one passage
Entered Sokoban - like mines
Entered the Big Room - not always possible since not always present
The novel and bigroom ones aren't always achieveable since novels are
only guaranteed if a book or scroll shop gets created and bigroom is
only guaranteed in wizard mode. No one ever claimed that every
possible achievement can be attained in a single game. (If one for
entering the Fort Ludios level--or perhaps entering the Fort itself--
eventually gets add, that won't be possible in every game either.)
The mine town one probably needs some tweaking. Two of the town's
seven variants have no town boundary (despite a rectangular area of
pre-defined map) and at present simply arriving on either of those
levels is enough to be credited with the entered-town achievement.
Bump EDITLEVEL because u.uachieved[] has increased in size. This
time it has been expanded to the maximum that xlogfile's bitmask of
achievements can handle, enough for up to 9 more achievements without
another EDITLEVEL increment.
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".
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.
This is based on the multiple-RNGs code fron NetHack4, but using
only the parts relevant to the display RNG (and with substantial
changes, both because of post-3.4.3 changes, and because Nethack4's
display code is based on Slash'EM's rather than NetHack's).
If bogusmon, engrave, epitaph, oralces, or rumors won't open, assume
it's because the file is missing so don't have impossible() tack on
"saving and restoring might fix this" when telling the player.
(Missing rumors or oracles previously only used pline() rather than
impossible() so this hadn't been an issue for them. Now it would be.)
Last few && or || followed by end-of-line comments, plus tab replacement
and 'return' parentheses. Not as many of those; some of these files had
already had that done.
Also, tweaked non-cursed scroll of charging read while confused to be a
tiny bit more effective.
To do: find and fix block comments that immediately follow a line with
an end-of-line comment and got misindented to line up with that comment.
I'll push a formatting guide at some point. There may still be
outstanding changes, but please feel free to resolve those as you arrive
a them.
To the best of my knowledge, there is no changes to the actual code
content, but the formatter does have the occasional bug. If you run into
an issue, please fix it!
From the newsgroup, while rehashing our bug page entries someone
mentioned that buying a minor consultation from the oracle exercizes
wisdom twice. First when getrumor() chooses one, then again when
outrumor(BY_ORACLE) delivers it.
This also simplifies some #if GOLDOBJ conditional code.
For text data processed by makedefs at install time, change all
printf and scanf calls that use %lx format to deal with unsigned long
variables, replacing the makedefs hack of a few days ago. It's not as
clean as I would have liked (quite a few casts), because the values
involved are derived from ftell and/or passed to fseek, which deal in
signed longs. But it clears up a few format check warnings by gcc in
rumors.c and pager.c in addition to the previous one in makedefs.c and
uses the right data type even in the places where no warning was issued.