Submitted for 3.7.0; all but one also apply to 3.6.3.
I rewrote the curses terminal-too-small message instead of just
fixing the spelling of "minumum".
Slippery fingers would transfer from bare hands to gloved hands if
you put gloves on. The reverse, transfering from gloves to bare
hands when taking gloves off, was already being prevented for
directly taking them off, but still allowed the slipperiness to
transfer when gloves were lost. This prevents putting on gloves
when fingers are slippery and attempts to handle cases where gloves
get unworn by ways other than 'T' (or 'R') or 'A'.
There's no slippery attribute for objects (way too much work for too
little value); slippery gloves is just the combination of wearing
gloves and having slippery fingers (which now has to have happened
while already wearing those gloves). This changes inventory to use
"(being worn; slippery)" when applicable and much of the patch deals
with funnelling Glib changes through new make_glib() to try to make
sure that persistent inventory adds or removes "; slippery" right
away when changes happen.
If gloves are taken off involuntarily (shapechange to a form that
can't wear them, destruction via scroll of destroy armor or monster
spell of same or via overenchantment, theft), slippery fingers ends
right away instead of the usual few turns later.
When Stoned, Slimed, Strangled, Sick (TermIll or FoodPois or both)
counts down to 0 without being cured, keep it listed as an active
condition while killing off the hero. It will show in the status
section when disclosing final attributes and in both that section
and map's status lines when producing a dumplog.
Add the contributed code that checks for attempting to start a
duplicate timer. It's based on a comment which must have been there
at least 25 years and doesn't solve any known problems, but it is
conceptually similar to the large amount of sanity checking which has
gone into 3.6.x.
It didn't work as is because it was comparing two unions with '=='.
I don't know offhand whether C++ supports that but C doesn't (through
C11 at least; don't know about C17). The union ('anything') is simple
enough that two instances can be compared without jumping through hoops.
I've also added another check for timer 'kind' (level, object, monster,
or global).
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.
Fixes#196
If you didn't die from turning into green slime but then died because
green slimes had been genocided, the message given assumed that you
had just seen "OK, you don't die" from answering No to "Really die?".
Its wording didn't make sense if the reason you didn't die was an
amulet of life-saving. Give a different message for that case.
Also, if you survive turning into slime (via either method) and either
green slimes are still around or you answer No to "Really die?" when
they've been genocided, give a message after "You survived that attempt
on your life" pointing out that you have done so in green slime form.
Useful since prior to 3.6.2 you would have reverted to original form--
despite the Slimed countdown saying you had turned into green slime.
When a hero dies due to turning into green slime, actually polymorph
him into a green slime monster before killing him off. That way he'll
show as a green 'P' on the map instead of white '@' during final
disclosure. Also, armor that gets destroyed by polymorphing into that
form will be absent from resulting bones file.
Report suggested that if hero is turning into green slime, genociding
green slime should cure it. I went another direction: if life-saved
while dying due to turning into green slime, you survive polymorphed
into green slime form. If green slimes have been genocided (probably
after becoming infected with slime or hero wouldn't have faced any
slimes to cause infection, but that could be from eating a glob of
green slime created prior to genocide, or from #wizintrinsic), you'll
immediately die again, this time from genocide.
Use the make_foo() intrinsic set/reset routines instead of trying
to manipulate the intrinsics directly. Previous patch left Dex
down by 1 if stoning caused wounded legs to be fixed, and left
delayed killer allocated if stoning cured sliming or vice versa.
I tried 'pick all' in the #wizintrinsics' menu and after 30 turns,
died with "poisoned by a poisoned, while vomiting". Food poisioning
and/or terminal illness beat the other fatal conditions to the coup
de gras. However, the final stage of vomiting sets Sick to 0 cure
food poisoning and ends up clobbering the killer reason if Sick is
due to terminal illness. It's feasible for that to happen without
using #wizintrinsic, so this fixes that, and also a few other
combinations that seemed contradictory:
1) limbs turn to stone during Stoned countdown now cures wounded legs;
2) turn to stone (a couple of turns later) cures vomiting and sliming;
3) turning to slime during Slimed countdown cures stoning.
When #wizinstrinsic was expanded to be able to set any timed attribute,
some that need more than just a timeout counter were left inconsistent.
1) Timed Flying wasn't blocked by levitation, and existing flight
wasn't becoming blocked by timed levitation. Also, eventual flight
timeout wasn't updating the status line, so false 'Fly' condition
remained shown until a status update happened for some other reason.
2) Setting timer for Warn_of_mon didn't set up any type of monster to
warn about so wouldn't do anything. This sets that to grid bug
unless already set due to polymorph form or artifact that warns.
The end.c portion is just a bit of formatting.
Reported internally, if a prayer resulted in 'fix all troubles' and
one of those was TROUBLE_STUCK_IN_WALL but safe_teleds() couldn't find
any place to relocate the hero to, nothing was done and STUCK_IN_WALL
would be found again as the next trouble to fix. Since safe_teleds()
eventually resorts to trying every single spot on the map, there was
no other result possible than failing to find an available spot again,
nothing would be done, and next trouble would be STUCK_IN_WALL, ad
naseum.
I started out with a fix that looked for secret corridors to expose
and doors to open, to make more space available, then try to move a
monster off the level, then try digging out rock and/or walls and
smashing boulders. None of those guarantee success and I got bogged
down by the digging case. This was going to be a last resort if all
of those still failed to make somewhere to move the hero, but for now,
at least, I'm skipping all that other stuff and going directly to the
last resort: give the hero Passes_walls ability for a short time, and
let him or her find own way out of trouble. The next trouble to fix
won't be STUCK_IN_WALL because Passes_walls makes that a non-issue.
I'm not thrilled with the new messages involved but want to get this
behind me.
From a beta-tester running 3.6.0:
|You faint from lack of food.
|You suddenly vomit!
The latter has already been changed to "You vomit" (it's given at the
end of a multiple-message vomiting countdown so wasn't "sudden") but is
still odd if your stomach is so empty that you're subject to fainting.
Give an alternate message in that case:
|Your stomach heaves convulsively!
Vomiting while unconscious (when that's due to something other than
fainting from hunger) should pose a risk of choking to death, but I'm
going to pretend that this hasn't occurred to me....
Adjust the Candelabrum of Invocation's weight when it has candles
attached. This has been a known issue ever since the candelabrum and
candles were introduced.
When the candelabrum burns out, update persistent inventory window to
show that it no longer has candles.
The countdown for delayed vomiting gave message "you suddenly vomit!"
at T-2, allowing you to move some more, then vomit at T-0 with no
message, finally at T+2 get "you can move again", making it seem as
if the program was letting you move during a time it thought that you
couldn't move. Also, there was nothing "sudden" about it since it was
near the end of a 15 or so turn sequence where a few other messages
are given leading up to that.
Change it to
T-2: You are about to vomit. // different wording
T-0: You vomit! // new message
so that "you can move again" more clearly refers to the actual event.
Polymorph control gives the player a chance to accept or reject a form
change due to lycanthropy, but if it occurs during combat or movement
the player might type 'y' before realizing that the prompt is pending.
Provide a paranoid_confirmation setting for 'Were-change' to allow a
player to require "yes" instead of 'y' for that.
The existing setting 'wand' is renamed to 'wand-break' and now requires
at least two letters in the config file options instead of just 1. The
spelling of its synonym is changed from 'breakwand' to 'break-wand';
it can be shorted to as few as 2 letters (same as before) but if more
than 5 are present, the new dash is required.
Both 'wand-break' and 'Were-change' are placed before 'pray' in the 'O'
menu for paranoid_confirmation so that all the "yes" vs 'y' settings
are grouped together.
Bonus fixes:
Reverting from were-critter form to human (due to timeout) did not give
a player with polymorph control the option of remaining in creature
form; now it does.
The 'O' command's menu would not show "wand" (now "wand-break") in the
current value of paranoid_confirmation. (A post 3.6.0 issue, so no
fixes entry included.)
The revised Guidebook.mn has been tested; Guidebook.tex has not.
Remove the assumption of property index values from the list of
property names. Move the properties that can't have timed values
in normal play to after those which can. (Mainly only matters for
the #wizintrinsic command.)
Bug fix: #wizintrinsic variable 'any' wasn't initialized properly
if 'a_int' is smaller than a long or a pointer. The separator line
I've added was ending up as a menu choice.
Extend the wizard mode #timeout command: show timeouts for all 67
intrinsics rather than just a handful. Most won't appear because
they don't have any way to receive a timed value. Except for...
Extend the wizard mode #wizintrinsic command: allow setting a
brief (30 turn) timeout for any/every intrinsic, not just for
deafness. It ought to prompt for duration, but that's more effort
than I'm willing to expend. This might turn up lots of quirks that
the code isn't prepared to handle (like setting life-saving to
non-zero will break the assumption that it comes from worn amulet).
Perhaps some will warrant fixing, others just a shrug.
There are still some timed events that aren't listed by #timeout:
remaining duration to stay polymorphed in current form, number of
turns until it's safe to pray, luck decay, number of turns until
next attribute exercise/abuse check, probably others that I'm
overlooking.
Bug fix: while testing, I observed
Your limbs have turned to stone.
You have turned to stone.
You can hear again.
You are a statue.
when deafness and petrification were timing out at the same time.
This modifies the stoning and sliming countdowns to extend deafness
duration a little if it's about to time out at the tail end of the
stoning or sliming sequence, so that "you can hear again" won't
happen until after life-saving. There are probably other variations
of simultaneous or near simultaneous timeout that interact oddly.
m_monnam() overrides hallucination, which is appropriate in some
situations but not others. This fixes one instance where it was
being misused: discovering a hidden monster when another monster
attacks it was calling either m_monnam() or a_monnam(); one ignores
hallucination and the other doesn't, so accurate or inaccurate
monster type depended on the condition tested.
Figurine activation and egg hatching are using m_monnam(), which
seems suspect, but I left them as is.
timeout.c: the luck timeout expression caused a bit of thought trying
to decide whether it was doing what was intended (it was). Add some
parentheses so that thought is no longer required. ;-)
worn.c: fix up a macro where automated reformatting mistook a
bitwise-and operation for a cast applied to the address of something.