Added the silver mace to be the base weapon type of Demonbane. It is appropriate that an artifact weapon designed to slay Demons would be made of (or plated with) silver. This helps to offset the damage reduction when Demonbane was changed from a longsword to a mace and makes it more specialized against silver-haters.
Set the probability to 2, equal to that of a silver spear.
Increased the weight of the silver mace by 120% -- equal to the weight increase from a normal spear to a silver spear. (Assuming the weapon is silver plated rather than made entirely out of silver.)
Increased the base cost to 60, a similar increase as spear to silver spear, to be an even number between silver spear and silver saber.
Monsters will prefer silver maces over regular maces.
Otherwise, identical in function to a normal mace.
If user has changed the stone glyph to something other than a space
(or uses a tileset), Sokoban levels showed the unreachable stone outside
the map area. Prevent marking those areas as seen, so the stone
glyphs aren't shown.
Author: PatR <rankin@nethack.org>
Date: Mon Apr 7 13:58:28 2025 -0700
fix issue #1404 - re-tamed feral pet starves
Issue reported by k21971: changes in 'struct edog' initialization
resulted in re-taming of a feral former pet producing a tame monst
that immediately dies of starvation.
I didn't look at the earlier behavior, just forced hunger to be
initialized separately from other edog fields.
Fixes#1404
Fix up a few comments in the monster throwing code. And change a
couple 'if (!Blind)' checks to use 'if (canseemon(magr))' instead
of that the player won't be told about a returning aklys hitting an
invisible monster ("it") on the arm.
Issue reported by k21971: a gnome throwing a wielded aklys at the
hero was killed when failing to catch its return. Bookkeeping for
dead monsters got messed up, then a crash occurred.
This fixes things. Instead of a comment stating that the thrower
might be dead, kill it off.
This has been sitting around for a long time. It prevents at least
one fuzzer exit.
Before adding this, I did trigger one yn_function() 'impossible'
while doing ordinary testing but wasn't able to reproduce that, so
am still not sure what is going on.
I hope this doesn't break anything. There seemed to be one or two
misplaced 'end' statements, but after some massaging I'm not sure
about this anymore.
There were lots of wide lines; those are easy. The Water-surrounded
vault had very inconsistent indentation so was harder to untangle.
This was discovered when a game of xNetHack crashed with stack smashing
detected during dumplog creation after an ascension. I traced the
problem to a wish with a very long string the player had made much
earlier in the game ("greased very blessed holy rustproof unlit historic
thoroughly +5 very cloak of protection named it would be a shame if
something happened to me wearing this cloak"), which is further recorded
in an even longer form in the chronicle as 'wished for "X", got "Y"'.
That string does get truncated, but since the gamelog strings are
dynamically allocated, they can be longer than BUFSZ.
When show_gamelog was subsequently called, it didn't use any bounds
checking, which allowed its stack-allocated buffer to overflow. Changing
the offending sprintf to snprintf and limiting it to the buffer size
appears to fix this issue. It will truncate the string at BUFSZ-1
characters and therefore will be expressed in the dumplog as an
incomplete string, but 1) that was happening anyway because the gamelog
string already doesn't capture the entire "wished for X, got Y" message
on such a wish, and 2) this should only ever happen for very long
wishes.
Most things that can be dug or chopped can only have that done by one
of the two types of digging/chopping tools: pick-axe or axe. Since
closed door can be broken open via either type, mention the type of
implement in the final "you break through the door" message by adding
"with your <uwep>."
After updating the --dumpweights code in hack.c to insert "pair of"
for gloves and boots and "set of" for dragon scales, I've switched
it to use simple_typename() instead.
Turns out that that routine also lacked handling for 'pair|set of'.
And it was generating "coin of gold piece". Fix those.
Roughly half of the gems are "<gem>" and the others "<gem> stone",
so the --dumpweights output is different by more than just pair/set.
Experience-level and experience-points, if enabled, could be
highlighted via 'up' or 'changed' rules in initial display after
restore. I tried 'down' rule too but didn't produce with that.
I don't understand what was going on but was able to reproduce it
and then fix it via the trial and error method....
Those places that use get_table_str_opt() to get an optional string
value can instead use a function that returns a string.
This can be used for example in quest data lua table, or some table
fields in the lua api bindings, or the dungeon definition.
For example, in quest.lua
text = "You again sense %l pleading for help.",
could be replaced with
text = function() return "You again sense %l pleading for help."; end,
which of course allows using lua to build the string.
There were couple reports of doors being generated in a corner
of a room. This happened for randomly generated irregular rooms,
because the code that was deciding if a corridor starting or
ending location was good did not handle irregular rooms at all.
This changes the corridor code so it can now generate start and
end points properly for any valid position in irregular rooms,
instead of only on the edges. This means the corridor sometimes
meanders a bit more than before, because it tries to find
the end point away from the edge of the room rectangle.
Also added is sanity checking for the randomly generated rooms
and corridors level, testing for door placement and room connectivity.
And another fix for a rare special case where dig_corridor
created a zero-tile long corridor; the entrance door was placed,
but there was nothing behind it.
Fixes github #1269 and #1385
Fix warnings
objnam.c: In function ‘wizterrainwish’:
objnam.c:3536:54: warning: variable ‘didblock’ set but not used [-Wunused-but-set-variable]
3536 | boolean madeterrain = FALSE, badterrain = FALSE, didblock, is_dbridge;
| ^~~~~~~~
Disarming a chest trap was setting obj->tknown = 0 even though the
hero just discovered that it isn't trapped.
Triggering a chest trap behaved similarly. Since there are no
repeating chest traps, hero should know that the chest whose trap
just went off is no longer trapped.
chest_trap() didn't document its return value but was clearly meant
to return True if the chest was destroyed. It didn't handle that
correctly when the chest was being carried. However, none of the
callers actually use the return value. [This fix tracks whether the
chest gets deleted; a better fix would be to destroy an exploding
chest even when it is being carried.]