The 'O' handling for bouldersym was updating the display value for
boulder even if the value had been rejected, and if it still had the
default of '\0', the map would end up with <NUL> characters. (When
examined via '//' or ';', those matched dummy monster class #0 and
led to the impossible "Alphabet soup: 'an("")'" that was suppressed
yesterday.)
Attempting to set bouldersym to ^@ or \0 would also be rejected as
duplicating a monster symbol. That is now accepted and used to reset
the boulder symbol to default. However, other control characters are
also accepted--not due to this patch, they already are, and from a
config file in addition to via 'O'--so bouldersym can still disrupt
the map. But that's no different from putting control characters
into a symbol set or setting them from config file via S_foo:^C.
A recently added impossible to check for an(Null) and an("") was
triggered by the fuzzer: Alphabet soup: 'an("")'. I reproduced it a
couple of times and tracked it do_screen_description(for '/' command)
matching the symbol from mapglyph to monster class #0, a placeholder
with symbol value '\0'. So mapglyph() returned a symbol of '\0', but
not necessary from showsyms[0 + SYM_OFF_M].
The pager lookup code's monster loop shouldn't have been attempting
to match against class #0, and since this fix I haven't been able to
reproduce the situation again. But I also didn't trigger it with a
bunch of temporary checks in mapglyph() so don't know what is really
going on under the hood.
People have been wondering how to change the tiles on the X11
version, and the old default of NetHack-specific binary tile data
isn't directly editable with image editing tools.
Also show in the #version info if xpm and graphic rip are enabled.
The revised mkclass() [actually new mkclass_aligned()] has an extra
check which didn't used to be there, and attempting to create a
monster of class 'I' with ^G triggered impossible "mkclass found no
class 35 monsters" which the fuzzer escalates to panic.
Fixes#170
Monsters never throw athames or scalpels but some fake player monsters
on the Astral Plane are given those. Since they're stackable the
quantity usually gets boosted but there's no point in having more than
one if they won't be thrown.
This could have been fixed by letting monsters throw those two items,
but I prevented the quantity from being boosted instead.
When merging one stack into another and they have different obj->o_id
price adjustments, keep the o_id of whichever one commands the higher
shop price.
Player came across a stack of 2 gray stones in a shop and kicked one.
That one ended up with a different (in his case, lower) price once it
was separate. This behavior only applies to non-glass gems which add
a price variation derived from internal ID (obj->o_id) number. Make
splitting stacks always yield the same price per item in the new stack
as was being charged in the old stack by choosing a similar o_id. Do
it for all splits (that can vary price by ID, so just non-glass gems),
not just ones performed inside shops.
He picked up the lower priced one and dropped it back on the original
higher priced one; the combined stack took on the lower price. That
will no longer happen if they come from splitting a stack, but this
fix doesn't address merging with different prices when they start out
as separate stacks. (Unpaid items won't merge in inventory if prices
are different, but shop-owned items will merge on floor.)
It was possible to have the guaranteed luckstone at Mines' End become
merged with a random one and lose its specialness for achievement
tracking. Mark it 'nomerge' when created and clear that if/when the
achievement is recorded.
Fix another inconsistency with containers in shops: prices shown when
looking inside. Apply had them (because shop goods in containers are
flagged as 'unpaid' when hero carries the container), and loot did not
(because they aren't flagged that way).
get_cost_of_item() was giving different information from shop #chat
when dealing with containers owned by hero containing objects owned
by the shop. And when it was legitimately reporting a price of 0,
doname_with_price() wasn't reporting 'no charge' for items inside a
shop that were owned by hero or that shopkeeper didn't care about.
Extend the shop price reveal to far-look, but only when hero and item
being examined are inside the same shop.
Symset:Blank sets all the map symbols (except STRANGE_OBJECT) to
<space>. The status lines for !STATUS_HILITES force status to use '$'
instead of ' ' for the prefix before ":1234" for gold, but the status
lines for STATUS_HILITES did not. tty ended up with ":1234" for gold.
win32 and curses both ignore the prefix and construct their own, but
since win32 uses the map symbol for that it must also be ending up
with ":1234" (I assume; I haven't seen it). curses is forcing '$' for
the prefix, even on the rogue level.
This attempts to fix win32 without be able to test the result. I've
left curses alone.
Watching the fuzzer, I saw hero's strength plummet to 3 again and not
rise above 5 after that. It turns out to be due to life-saving, which
was fixing severe hunger but was not restoring the point of strength
that's lost when you go from hungry to weak.
I'm not sure whether this was caused by 3.6.1's commit
024e9e1225 or already behaved that way.
Another fuzzer bit: the monk I was watching was bitten by a wererat
early on and was still inflicted with lycanthropy when he reached
level 19. (I've no idea how his level got to be so high; it jumped
from 14 to 19 while I wasn't paying attention.) Extend the earlier
hack for drinking a blessed potion of restore ability to recover lost
characteristcs to sometimes drink a potion of holy water instead.
The rationale is that since the player character resists conflict,
fake players should too.
[I'm not sure that I buy that. Player character is always the one
*causing* conflict and it doesn't affect self. But this is simple
as long as no other resistance checks are against attack-by-ring.]
Dropping an existing fragile item while levitating will usually
break it. Getting a new wished-for fragile item and dropping it
because of fumbling or overfull inventory never would.
Some callers of hold_another_object() held on to its return value,
others discarded that. That return value was unsafe if the item
was dropped and fell down a hole (or broke [after this change]).
Return Null if we can't be sure of the value, and make sure all
callers are prepared to deal with Null.
Fixes#155
When drowning, you need to be unencumbered in order to crawl out of
water. When not drowing, you don't, but put a limit on how much can
be carried. If polymorphed into a swimming creature, allow stressed
or less; otherwise (magical breathing), burdened or less. (Doesn't
apply on the Plane of Water since there's no climb from water to land
involved.)
'Detect' is used for observing a vampire shape change without being
able to see the vampire. The problem here is that it changed from
bat form to fog cloud form in order to pass under a closed door,
and the message was being delivered when it was already at the door
location instead of before the move from a visible spot to that door.
I'm not happy with this fix, but any other alternative I considered
seemed to be worse. Having the shape change use up the monster's
move is probably a better way to go. Then on its next move it will
be in the right form to make a normal flow-under-door move.
Code appears to intend that riding for 100 turns be treated like a
successful weapon hit as far as skill training goes, but it was
actually requiring 101 turns each time. It's conceivable that that
was intentional, but unlikely.
Reported seven years ago, when ice melts underneath a monster, it
hovers there until its next move, then falls in and drowns. Dunk it
immediately, and give hero credit/blame if it happens during the
hero's turn (so presumably the melting was caused by the hero).
Also, let monster with teleport capability who gets dunked teleport
away from the water before getting wet, the way hero does.
Another one from 6.5 years ago, identifying a type of gem should give
a new price for any unpaid gems of that type and adjust shopping bill
accordingly. Report was for rubbing with touchstone and learning
worthless glass with price not changing until the learned 'gem' was
dropped. Fix works for that and also other forms of identification
(and for amnesia, raising prices of forgotten gems); no dropping is
required for the price to change.
Theoretically could apply to any type of item, but prices of gems are
by far the most sensitive to whether or not they're identified.
When testing the change to the Eyes of the Overworld wording and asking
for information about inventory item
k - a pair of lenses named The Eyes of the Overworld
I got "I don't have any information on those things". Not because that
item wasn't identified, but because the lookup was for "pair of lenses"
(finding nothing) and then for "The Eyes of the Overworld" (and not
finding it due to "The" which is stripped from the first attempt but
wasn't from the second nor present in the data.base key).
Another one from nearly 7 years ago. Hero kicked embedded gold out
of a wall while following the guard away from the vault and got
"The guard calms down and picks up the gold."
and player thought it was odd because the guard was peaceful. It is
odd, but guards have an agitation state (0..7) when peaceful and it
is always non-zero when this event occurs. Suppress the "calms down"
part unless the agitation is close to making the guard turn hostile.
[Agitation is set to 5 after that event, so it isn't very calming.]
Also, the guard was picking up gold from underneath the hero while
two steps away. Move him adjacent (although it doesn't knock other
monsters out of the way if there's no room) prior to the message,
then back again after. That's how if works for gold that's not at
the guard's location and not at the hero's location, although that
case does knock another monster out of the way if one is on the gold.
Fixes#169
Monsters should not be afraid of stepping on the vibrating square
since it's only a trap for display purposes. [Perhaps they should
deliberately avoid it if the hero hasn't seen it yet, but I didn't
implement that.]
"You see a strange vibration beneath <mon's> <parts>." was strange
when <parts> was a wolf's "rear paws" or horse's "rear hooves"--was
the vibration magically skipping the front ones? And it sounded
naughty when it was a snake's "rear regions". If the creature has no
limbs or is floating or flying, just say "beneath <mon>"; otherwise,
if the part is "rear <something>", omit "rear ".
The message was weird in another way. Caller removes the monster
from it's old location and places it on the new one, calls newsym()
for the old location to show lack of monster, but then calls mintrap()
before newsym() for monster's new location (the trap's location). If
pline messages cause buffered map output to be flushed, the monster
will be missing during the time the messages are delivered. I fixed
that for vibrating square [seetrap()->newsym() before pline() rather
than after] but it should probably be fixed in the caller instead.
Another 6.5 year old report. This one from Steven Melenchuk told
how to reproduce C343-23 which is still open on our 'known bugs'
page. (I've no idea whether the original bug report came through
the contact page, and if so, what its assigned number was.)
I didn't try to solve this one, I just confirmed that it could be
reproduced and took the fix from grunthack at github. He didn't
menion a fix at the time but implemented one before abandoing his
variant. (Others kept it going afterwards; fix was during his time.)
The overflow occurred when the guard couldn't figure out where to
move to next and just repeatedly 'moved' to his current location
until the maximum number of fake corridor spots was used up. The
fix detects not knowing where to go next and explicitly choosing a
new destination.
Original problem could be reproduced by teleporting into the vault,
digging out a wall and two spaces of stone in a straight line, then
going back into the vault to wait for a guard. When he shows up:
answer, drop gold, follow. If the guard's path walks through both
dug spaces, he will stop waiting for the hero. But hero is in
between the guard and the gap in the vault wall and can't advance;
guard has reached a persistent corridor so doesn't know where to go
next. Have hero wait for 125-ish more turns and then game panicks.
The code was 3.4.3 vintage so needed thorough reformatting, but not
any actual changes (unless I've overlooked something).
Fix a few typos, reword a couple of entries describing the bug rather
than its fix to use past tense, move a couple of entries to be adjacent
to closely related entries (there's a lot of scope for more of that...)
and remove a bunch of trailing spaces.
[fixes36.2 starts with a header line but the tags on it aren't being
substituted.]
Another bug from seven years ago, sent directly to devteam so no #H
number. Report stated that throwing recoil could move a levitating
hero diagonally through a shop's doorway to exit it. If the thrown
item was unpaid, it remained unpaid after landing on shop's floor
and was an unlisted item on shop's bill. Moving diagonally out the
door seems to have been fixed, but the same effect still occurred
if you were far enough from the door to have the shopkeeper vacate
his door-blocking spot and throwing recoil took hero to that spot.
The thrown unpaid item remained unpaid, and walking out the door was
treated as shop robbery.
This one is only seven years old. Dropping an unpaid item inside an
engulfer leaves it unpaid and still on bill. If engulfer is killed,
it ends up unpaid when back on the shop's floor.
Treat dropping an unpaid item into engulfer's inventory as stealing
that item. You have to pay for it to leave the shop, and like any
other dying monster's inventory, the shopkeeper will take ownership
if it lands on the shop floor when the engulfer is killed.
The 'theft' doesn't anger the shopkeeper and the cost shows up on 'Ix'
as part of "usage fees/other charges" rather than as an itemized used
up item.
That #H number isn't a typo. This finally fixes--at least improves--
something reported eight years ago. The monster types chosen by
mkclass() could be way off in some circumstances. Cited example was
repeated same-race sacrifice by chaotic hero on dungeon level 20; it
produced about twice as many incubi as succubi even though they're
the same as far as difficulty goes. (No changes in the intervening
years had any discernable effect; that was still reproducible.)
The report also mentioned that ndemon() threw away the result from
mkclass() and retried quite often and suggested that mkclass() be
taught to filter by alignment when caller cared about that.
This seems to even things out, although it also made harder monsters
chosen more often. A test program generated these numbers when
picking a chaotic demon 10000 times (level 1 hero on dungeon level 20,
so not realistic; actually probably level 0 hero since the program
didn't initialize struct u.) Third column is the number of times the
monster type was chosen with the old mkclass(), fourth is same for
the new one.
mkclass() calls 27315 10000
286 succubus 2800 3309
288 incubus 5552 3262
291 marilith 973 780
292 vrock 477 1617
293 hezrou 150 626
294 bone devil 46 247
295 ice devil 2 107
296 nalfeshnee 0 23
297 pit fiend 0 15
298 sandestin 0 4
299 balrog 0 10
Note that vrock has generation frequency 2 and marilith only 1, so
getting twice as many vrocks as mariliths should be expected.
I temporarily changed ndemon() to ask for lawful demons instead of
chaotic ones and got this.
mkclass() calls 15762 10000
287 horned devil 3197 3375
289 erinys 4991 3339
290 barbed devil 1812 3286
I also ran it for dragons with any alignment (so the outcome was
never thrown away; 10000 calls were needed for 10000 picks) instead
of demons of specific alignment and am suspicious of the outcome.
mkclass() calls 10000 10000
140 baby yellow dragon 1124 0
141 gray dragon 1096 1096
142 silver dragon 1073 1099
143 red dragon 1061 1126
144 white dragon 1077 1128
145 orange dragon 1141 1118
146 black dragon 1154 1049
147 blue dragon 1137 1123
148 green dragon 1137 1154
149 yellow dragon 0 1107
There may be a flaw in the test program. Or else old mkclass() was
not very good at picking dragons.
We still don't know whether this will be of any help against
disconnected processes that hog the CPU instead of exiting, but I
don't think it imposes significant overhead on ones which aren't
disconnected. Install it before it suffers from more bit rot.
Stop pretending that long and int are the same size when picking status
highlight rule for gold or time or experience-points.
Also, K&R compilation might lack <limits.h>, so let XXXconf.h define the
necessary macro(s) (currently just LONG_MAX) so that it can be skipped.
More on clearing pickup_types so that autopickup reverts to picking up
evertyhing: for menustyle:Full and Partial, add a menu entry for 'all
classes' as an alternative to unselecting every class already set.
Also, Full and Partial had no way to include venom. Now it's a choice
when in wizard mode. There's still no way--other than switching to
Traditional or Combination--during normal play (where venom objects can
exist if they were wished for in wizard mode and then left in bones).
"Placing monster over another?" warning was triggered for vault guard
by an earlier change which made m_detach() stop removing monsters at
<0,*> from level.monsters[][]. So one guard would replace another at
<0,0> for however many guards were created, and memory for all but
the last one would be lost.
This involved a lot of flailing about and the patch includes various
things would could have been discarded. One or two extended monster
sanity checks are included, plus a couple of debugpline()'s for
tracking guard movement.
When clairvoyance revealed an underwater object and the player wasn't
being given a chance to browse for some other reason, the map didn't
get refreshed and the object stayed displayed with the water hidden
under it. Force a chance to browse when revealing underwater objects
despite that being intrusive when clairvoyance kicks in while walking
around.
Jumping or teleporting while levitating in xorn form wouldn't toggle
blocking of levitation when moving from open spots to wall/stone and
unblocking of same when moving the opposite way.
This handles those cases but there are no doubt others. The only
other one I checked was when failed #untrap moves hero onto trap.
That case works correctly--at least after this fix is in place.
Noticed while working on it: change of terrain didn't always update
the status line. When levitation became blocked, it still said Lev
and when unblocked, didn't say that. Next status update got status
condition back in sync.
Some code added for 3.6.1 tries to find the longest matching prefix
when comparing a user-supplied with previously set up fruit names.
It does so by temporarily replacing space with NUL then passing that
to makesingular(). After already having named a fruit (resulting in
something to try to compare with), attempting to assign a name
beginning with two or more spaces would yield an impossible "singular
of null?" warning.
After the warning, the name minus its leading spaces got successfully
assigned. I'm not sure why a single leading space didn't trigger it
too, nor where the leading (and trailing, if any) spaces are going
away when the name is assigned.
Fix by removing all leading and trailing spaces from a new fruit name,
and combine consecutive internal spaces to one, before any other
manipulations. (This can result in names that used to work as-is now
being simplified a bit--when consecutive internal spaces have been
given--but that shouldn't be a problem.)
Also, don't complain about "missing parameter for 'fruit:'" if user
hits <return> when prompted for fruit name by 'O'. An empty fruit
name at that stage is just a no-op.
After consultation with the original committer, this
is being pulled out, possibly revisited later. There was
originally meant to be a follow-up piece to this that he
never had a chance to integrate for various reasons.
add MM_NOGRP makemon() flag as a means of suppressing groups of monsters in
a couple places that warrant it when a specific monster type isn't
specified on the call to makemon()
In the 'special options' section at the end of 'O's menu, change the
spelling for the menucolors entry to "menu colors" so that it isn't
spelled exactly the same as the 'menucolors' boolean option. Only
affects what the player sees when reading that menu.
If player uses 'O' to add any menu colors and 'menucolors' boolean is
Off at the time, give a reminder to toggle it to On in order to have
those menu colorings become active. (Adding hilite_status entries has
a similar reminder for 'statushilites' if done while that is 0.)
With options along the line of
OPTIONS=statushilites:4
HILITE_STATUS=gold/always/yellow
gold started out unhighlighted (unhighlit?). I didn't try to figure
out why, just changed things to force a full status update when gold
requires internal changes (different \G encoding or different glyph)
which is something that happens when session first enters moveloop().
Inventory traversal can be disrupted when items being traversed are
able to change inventory. I've lost track of how many times this
sort of thing has been discovered.
Report claimed that boiled potion of polymorph caused transformation
which resulted in dropped weapon and dropped or destroyed worn armor.
That was evidently a guess; potionbreathe() for that potion only
abuses constitution. The traceback showed 'you_were()' was involved.
Boiled potion of unholy water triggers human-to-beast transformation
of hero inflicted with lycanthropy, yielding similar situation.
I didn't notice anything unusual when reproducing this but inventory
was definitely vulnerable. My 'one line' fixes entries are steadily
getting to be more verbose; I may have to go back to 'fix bug'. :-}