Reported directly to dev-team: vapor cloud dissipation didn't always
update vision properly.
Region removal affecting visibility needs to make two passes, the
first unblocking all no longer blocked spots, then the second deciding
whether spots are visible.
Attempting to do that in one pass was doing
| unblock <x1,y1>
| if cansee <x1,y1> whatever
| unblock <x2,y2>
| if cansee <x2,y2> whatever
and the cansee <x1,y1> test wasn't accurate if <x2,y2> blocked it and
hadn't been unblocked yet.
Testing with steam didn't seem to trigger the problem but with poison
vapor trail from green dragon breath did. The order of evaporation
mattered too; sometimes the single pass unblocking plus vision-testing
worked ok by coincidence.
Eliminate the 'goto' for human corpse added a day or two ago.
If a dead gnome is generated with a candle, light it if/when the spot
is dark. (Testing never managed to verify that this works as intended.)
mkcorpstat() never returns Null.
AFAICT, we only used the first char of the "command_line" string.
Just turn it into int to hold the key the main input loop parse() got.
Shouldn't have any functional difference.
Re-use the array allocated for iterating over all monsters during
monster movement much of the time. It was being allocated from
scratch for each round of monster movement, then freed after they
moved, then repeated the next round.
Update some potential weight issues. Eggs won't hatch when in
containers so they weren't affected but add some bulletproofing.
Corpse revival from inside containers was already ok too, so
effectively there's no change except for making container_weight() be
global instead of local to mkobj.c.
When a low-level trap is created with a dead pseudo-adventurer,
usually make a player monster when the human case gets picked.
They have default level and no inventory. They should probably be
given montraits that force low level but this doesn't do that.
The recent acurr() changes introduced a bug that caused Str less
than 25 to be limited to 18/07. 25 was treated correctly as a
special case but 18/01 through 18/100 and 19 through 24 were not.
The cap of 25 imposed on the other characteristics is the same as
encoded Str 18/07.
Restore its ability to reject a string longer than will fit within
size_t that was lost by moving away from strnlen(). Determine the
length inline rather than using strlen().
Move it from hacklib.c to alloc.c so that utility programs have easy
access, and remove the copy of it from dlb_main.c.
Fix a logic bug in str_start_is(). If a string was considered to
be too long, it exited the loop when n was 0 but also performed
post-decrement. So after the loop, n would be -1 and the 'if (n==0)'
test would fail. panic() would occur if the initial string matched
and happened to be LARGEST_INT-1 characters long.
doengrave was the largest function by far, so split it up into smaller
ones. Use a structure to hold the dozens of variables needed all over it.
Should have no difference in behaviour, but I haven't tested everything.
Previously reported via github pull request #1188 as an out of bounds
access to u.uhpinc[], followed by issue #1189 when it was closed, the
backtrace accompanying new assertion failure provided more information
that led to figuring out the problem.
Only mattered for the debug fuzzer; wouldn't happen in regular play.
When the hero dies during fuzzing, the fuzzer sometimes restores lost
levels via blessed potion of restore ability. If that happened to a
hero who died by being life-drained while at level 1 then losexp()'s
assumption that life-saved hero was still level 1 got violated. If
levels had been lost all the way down from a peak of 30, restoration
to u.ulevel==30 resulted in invalid array indexing into u.uhpinc[],
then failure of 'assert(u.ulevel >= 0 && u.ulevel < MAXULEV)' which
was added to avoid that.
Pull request #1188 and issue #1189 are already closed, but they hadn't
actually been solved yet.
Fixes#1188Fixes#1189
There were few places where the object weight was not updated:
- container when the contents were broken by impact
- starting to eat but getting stopped by rotten food
- using lua, container when putting an object inside it
- when a single egg of a larger stack hatched
Juiblex could expel the hero over water, they'd get relocated to
a safe location, but Juiblex was still thinking they were at
the location it expelled them.
+/-N for charged rings with known enchantment was clobbering the
BUC formatting that had occurred earlier. #K4088 thought it was a
problem with the implicit_cursed option; followup #K4089 from same
user correctly pointed out that the problem was present for any BUC
state.
This is the same line of code that inadvertently omitted the space
between +/-N and "ring of <type>". That was fixed by commit
1a2b2a8cae a couple of days ago.
While in doname(), fix a potential issue calling corpse_xname().
That assigns a new value to gx.xnamep, clobbering the value that
doname() relied on when it was first called (but doesn't look at
again, so doesn't matter now but could conceivably in the future).
Have alloc() treat a request for 0 bytes as if it was one for
'sizeof (long)' bytes so that the issue of malloc(0) maybe yielding
NULL becomes moot.
I think instances of attempting to allocate 0 bytes should probably
still be checked for and made to avoid calling alloc(0). Assuming
that alloc() will clean up requests for nothing feels sloppy.
While hunting for a memory leak in object allocation--which I haven't
found yet--I discovered one in monster movement. iter_mons_safe()
allocates an array of (monst *) pointers for the monsters on the
current level, loops over that array to call a function for each
one, then frees the array. But if the game ends while that called
function is running, execution never returns to iter_mons_safe() so
it wasn't able to free the memory.
Since that can happen at most once per game, it wasn't a signifcant
leak. This fixes it anyway.
There was a second issue: make sure that iter_mons_safe() doesn't
call alloc(0) to make the temporary array for zero monsters when
there aren't any on the level. That might not be able to happen for
monster movement but the routine is written to be more general than
just movement. alloc(0) could confuse the MONITOR_HEAP code. In
C89/C90 I think malloc(0) is allowed to return NULL (don't recall
for sure; maybe that was just known pre-standard behavior for some
implementations). Null return would trigger a panic even without
MONITOR_HEAP. Don't know about C99 and later.
Changing the quantity to 2 (50:50 chance) when creating a potion of
healing (also 50:50 chance for each attempt) to place inside a supply
chest wasn't updating the potion stack's weight, resulting in the odd
encumbrance behavior that was reported last December.
Taking the stack out of the container doesn't fix the weight but
drinking one of the potions splits the stack of 2 into two stacks
of 1 and does update the weight for both. That gives the hero higher
encumbrance when the formerly weightless one has its proper weight.
Finishing drinking the potion uses it up, removing second potion's
weight again. When below an encumbrance threshold by the weight of
one potion or less, player will see encumbrance increase and then
decrease, with healing message given before both due to sequencing.
Supply chests weren't having their own weight updated when they were
populated, so would behave as if empty if hero carried them around.
Removing something, breaking something by kicking the chest, or adding
something would update its weight to match its contents.
I also noticed a refutation (or should that be rebuttable?) to my own
remarks in this:
| commit cd91d0630b
| Author: PatR <rankin@nethack.org>
| Date: Sat Dec 30 17:10:39 2023 -0800
|
| github issue #1180 - humans and murder
|
| Issue reported by Umbire: reviving a human corpse into a human
| monster and then killing it entails murder penalty even when it is
| hostile.
|
| This is probably a non-issue. Human monsters tend to not leave
| human corpses, they leave shopkeeper corpses or sergeant corpses
[...]
Dead fake hero corpses placed at trap locations on early levels are
leaving plain human|dwarf|elf|gnome|orc corpses rather than fake
player monster ones (which are always human but resurrect as player
monsters rather than as plain humans), so there are more plain human
corpses now than there were in 3.6.x or early to-be-3.7. I've added
a comment about the situation.
Some minor cleanup of artifacts from the splitting up of trap effects
into the various trapeffect_foo functions: consolidate redundant
variables in trapeffect_pit (tt vs ttype), and simplify the definition
of 'inescapable' traps a couple functions (since the functions are now
specialized to a particular trap, it's unnecessary to check ttype
there in determining whether it's an inescapable Sokoban pit or hole).
If your inventory is full and you aren't already carrying a loadstone,
you can pick one up into the overflow slot. But if you are already
carrying one and the one you're trying to pick up won't merge with it
(only criterium that matters would be BUC state, I think), you can't
pick it up and get a message saying so. If loadstone isn't known
yet, the message always referred to it as "gray stone" rather than
"stone called <whatever-you-called-it>".
Replace the single strnlen() use with strlen() so as not to require
something from posix.1 (2008) when we supposedly only require c99.
If someone manages to produce a string that's longer than will fit
within size_t, strlen()'s length count will wrap and Strlen_() might
not notice. Since size_t has to be at least 32 bits, that doesn't
seem like something to worry about.
If checking for size_t overflow is considered essential, we should
just switch to counting the length via an in-line loop that imposes
a similar limit to strnlen().
When ready to return, check for overlooked overflow (shouldn't happen)
and panic, or report the first excessively long but not overflown
description to paniclog, similar to xname.
Make ConcUpdate() more robust by not needing bp_eos to be previously
set. Less efficient but I think that boat has left the barn? :=}
Fix a comment typo.
Issue reported by Umbire: suggestion to always destroy adjacent webs
via 'F'<dir> if wielding Sting or Fire Brand.
Sting already did that; this adds Fire Brand.
This also augments the #untrap command when wielding either of those,
or any other blade. And rephrases successful untrap message
"You remove {the or your} {bear trap or webbing} from Fido." to
"You extract Fido from {the or your} {bear trap or web}." since the
trap remains intact.
Forcefight and #untrap against webs ought to be reconciled to remove
[some of] their differences and/or share code. But not by me...
Closes#1201
Avoid two new warnings in xname_flags() about strncpy() not supplying
a terminating '\0'. That's exactly why strncpy() was being used.
The gcc manual lists -Wno-stringop-truncation to suppress the warning
but not -Wstringup-truncation to voluntarily enable it, so the pragma
stuff in warnings.h probably won't work for this. Just switch from
strncpy() to memcpy() instead even though it seems like obfuscation.