One of the huge strengths of NetHack is that there's a lot of
mechanical content in it; you can continue learning about the game
more or less continuously, and when you die, it's usually possible
to figure out what you did wrong and avoid that for future games.
There are two parts to this: a) a death should only happen if the
player didn't play perfectly, b) the nature of the problem should
be clear.
FoodPois status meets a) just fine, but not b); food poisoning
mechanics tend (based on my research) to actively give the wrong
impression to new players. (Normally, something along the lines of
"corpses are sometimes randomly dangerous", which is insidious in
the sense that it'll cause players to leave them as a last resort
and thus never discover what they're doing wrong.) The easiest way
to fix this is to explain what the rule is explicitly.
Additionally, this should hopefully go some way towards changing
the incorrect perception many people who haven't played (any/much)
NetHack have that the game is arbitrarily cruel... It's important
to get the "everything happens for a reason" attitude across from
an early stage, by tying it to an event like this that's commonly
seen by new plyers.
Experienced players should not be affected much if at all by this
change, because they hardly ever get food poisoning anyway.
(Note: there are actually two main mechanics related to food
poisoning, one widely applicable, one much more subtle. The message
here is focused mainly on the common case, but doesn't actually
contradict the rare case, and attentive players may be able to
deduce both mechanics from the one message. Most likely, players
will hit the common case, see the message, learn the common case,
and some time later hit the rare case, get the message again, and
read it more carefully this time.)
Latent bug: if a non-role-specific artifact which could be invoked
for charging existed, it would never work as if blessed because the
test for no-role was wrong. Caused when 3.3.0 changed the Role_if()
predicate and artilist[] array to use monster index numbers (PM_foo)
instead of role letters; the non-role value changed from '\0' to
NON_PM (-1) but the test for non-role didn't.
No fixes36.1 entry; there aren't any artifacts which were affected.
My compiler was understandably concerned about a potential buffer
overflow here. I don't think the string could get long enough to
cause that to happen, but it's hard to be certain. It's much safer
to limit the length of the string so that it fits in the buffer, as
done here, and if there really wasn't a problem the change will
cause no harm at all. (If there was, the string will be truncated
rather than corrupting memory. This code is in showing the
config-file version of a status highlight, something where
truncated text will probably be obvious to the user.)
This change was coded by FIQ, who suggested it by email.
The main change here is related to monster anti-oscillation code.
When a monster believes it's stuck in an AI loop, it looks for an
alternative strategy. That applies to pets too. However, if the
pet is currently near the player, we can typically assume that it's
there because it wants to be there, and an oscillation is not
because it's stuck but because it's already in the best possible
place. This commit causes pets to be "allowed" to stay near the
player, rather than running the wrong way down a corridor because
it's the only way to do something different than what they're
currently doing.
If the pet is far from the player, we use the old behaviour unless
the pet is leashed or the player tried to call it with a whistle
or the like, in order to avoid the risk of a genuine AI loop trying
to get back to the player. (Whistling happens rarely enough that it
won't cause AI loops of its own - the player isn't going to whistle
every turn - and it makes flavour sense that a pet might interpret
it as "you're going in the wrong direction!".)
There's far too much gold just laying around on the floor.
Didn't previous adventurers grab most of it?
This should incentivize gold detection and digging out vaults,
selling stuff to the shopkeeps, and making it harder to donate
for protection.
Most radical reduction on the first few levels, for dlevel 1,
average amount of gold was 80, is now 10, for dlevel 2, 95->15
Does not change the amount of gold deposited by mineralize,
in the vaults, or contained in chests and large boxes.
Due to the new player selection dialog I did, it was possible
to rename your character - but this didn't rename the lock files
and tried to load a save from the wrong name.
This is a bit of a hack, but seems to work and didn't seem to
cause problems for the tty.
Report was for tty, but X11 exhibited the same behavior. With the
perm_invent option enabled, when the permanent inventory window is
displayed, it would be empty if not carrying anything. For tty, that
meant a naked "(end) " selection prompt. Put a separator line of "Not
carrying anything" into the menu so that it won't be completely empty.
The selection prompt is still present but it is attached to something.
(The behavior is different from !perm_invent, where you get that same
text via pline without any menu at all.)
New code in nttty.c had a dependency on gdi32.lib. Previously
that was only being linked in for the gui build when using the
Makefile.
Move the reference into the base libraries if both tty and gui
depend on it now.
Player tried to #name a potion on the floor and got prompted to call a
stream of fluid (sink feedback) instead of a potion. A mimic posing
as an object is represented by a partially initialized object when
examining its map location. #name for floor object uses the same data
as look_at.
obj->fromsink overloads obj->corpsenm which is set to NON_PM (-1) even
when creating a non-init'd object. 'fromsink' was only being forced to
0 when creating an init'd object (unlike leash which has its overload
of corpsenm set properly regardless of caller's request to init). So
docall() treated a mimicked potion as a sink stream.
The fix is straightforward but has pointed out another bug which is
harder to fix. Examining a floor object next to you sets that obj's
dknown flag as if you had seen it up close (a new feature in 3.6.0).
But a mimicked item is discarded as soon as it's been looked at, so
looking again from a non-adjacent spot will give different feedback
since the previously set dknown will be unset when replaced by a new
fake object. So you can use '/' and ';' to recognize mimics without
provoking them into motion. Best fix: mimicking an object should use
a fully initialized one which is tracked via monst->mextra, but that
will break save file compatibility. Possible hack: change monst->
mappearance into a mask which uses N bits for object type (instead of
full 'int') and one of the other bits to track obj->dknown. Examining
an adjacent object probably ought to set bknown for priests, so bknown
and blessed/uncursed/cursed would need to be tracked too.
Doors in des-files were always generated vertically.
This wasn't visible unless you had separate symbols for
closed vertical and horizontal doors, or used tiles.
The expression '*cp-- = cp[-count]' is not valid C. There's no sequence
point between the two references to 'cp', and the decrement side-effect
could occur before or after cp[-count] is resolved.
The functions were also using ANSI-style argument definitions. The rest
is just reformatting.
It seems to me that the strbuf structure ought to have an allocation
size field in addition to the current length field. Otherwise a string
which gets shortened will forget about the extra length available for
later expansion, potentially resulting in unnecessary reallocation.
Added support to detect when the current console font has glyphs
that are too wide and will cause rendering errors in the console.
If detected, we warn the user and change the code page to 437
and the font to Consolas. At exit, if we had changed the font
and code page then we will restore to the original font and code page.
Defined strbuf_t and related routines to support dynamically sized
strings. Modified strip_newline() to strip the last newline in a string
instead of the first.
Simplified splash window code using new strbuf_t.
Prior to exiting game, re-enable getreturn and call wait_synch() in
case there is buffered raw prints that must be displayed to user.
The extended command added to test handling for adjacent mouse clicks
had a description which was too long. In the list from '#?', white
space for column alignment got squeezed out to make it fit (at least
for tty, where it ended up looking awful).
The new description isn't a complete sentence any more, but I don't
think anyone will care.
The code that checked for and complained about having more than one
'prize' object on the mines' end or sokoban end level uses static
counters and would complain if you used #wizmakemap to recreate the
level. (Or if a game got far enough that either of those levels was
created and then started over--I'm not sure what state support for
that has reached.) So re-init those counters each time any special
level gets created; that's sufficient for what they track.
I also changed several variables in sp_lev.c from global to file
scope since they aren't used anywhere else.
The followup message about the fix for #5056 was trapped by the spam
filter so didn't reach us for a while.
xlogfile has an extra field to track various achievements made during
the game it logs, two of which are fully exploring the gnomish mines
and fully exploring sokoban. Those are accomplished by finding the
special 'prize' item on the final level of their branch: luckstone
for mines and bag of holding or amulet of reflecition for sokoban.
3.6.0 had a bug where any item of the target type found anywhere in
the dungeon resulted in achieving the relevant goal. A post-3.6.1 fix
for that required that the item be found on the end level of the branch
and attempted to require that it an item explicitly placed there by the
special level loader, but the latter aspect had a bug which meant that
random items of the appropriate type placed on final level would count
as the prize. Chance of extra luckstones on mines' end is fairly high,
so potential for false completion of the achievement was also high.
The second complaint was that since the achievement was only recorded
if the special prize item was found on final level, then if a monster
took it to another level then the achievement became impossible. (Not
true, the player could take it back, drop it, and pick it up again, but
that is admittedly a pretty silly hoop to jump through.) On the other
hand, if a monster removed the item before the hero found it, then a
case could be made that the hero hadn't really fully explored the
level. However, this fix records the achievement no matter where the
hero picks up the item. The final level must be entered--otherwise no
monster could possibly acquire and transport the item--but it isn't
guaranteed to have been fully explored. Big deal....
The prize could also be acquired in bones data. Before the second
portion of this fix, that wouldn't have mattered. But now it does, so
clear the prize indicator when saving bones unless it happens to be the
same level where that item is created (impossible for sokoban, where no
bones are left; not sure offhand about mines' end). The former prize
stone or bag or amulet becomes an ordinary one of its type.
This can all be done in a much cleaner fashion once we give up on the
current save file compatability. Putting obj->o_id values into new
context.mines_prize and context.soko_prize, plus a hack to mkobj() to
not reuse those two values if the o_id counter ever wraps back to 0,
would cover most of the details. Adding an achievement tracking flag
to lev_comp's object handling for use by the special level loader
would cover most of the rest.
The recent fix to prevent #adjust from letting the player move things
into slot '-' (if compactify() reduced any sequence of consecutive
letters to x-y, introducing dash into the string of characters that
could be chosen from) was triggering a complaint about mixing &&
and || without parentheses. Fixing that was trivial, but I ended up
making a much more substantial change.
If the '#' overflow slot is in use, you can move something into it
even when you no longer have all 52 regular slots in use. (When it
isn't already in use, you can't access it. Previously you could swap
from '#' to any letter but not vice versa.) If you manage to get
gold in multiple slots or in some slot other than '$', you can move
or merge it into the '$' slot. And when that situation isn't present
(if even possible--I had to force it with a debugger to test), then
gold will no longer be listed among the inventory letters to adjust.
(That became an issue when GOLDINV came into use, but either nobody
ever noticed or at least never reported. "Adjust what? [$abd]",
then pick '$' and be told you can't adjust gold. Prior to GOLDINV,
'$' wasn't included in the list of candidates.)
Poly'd into a giant with a full inventory that already contains at
least one boulder, moving onto a boulder (that can't be pushed due
to a wall or other obstacle) yielded
You try to move the boulder, but in vain.
However, you can easily pick it up.
You are carrying too much stuff to pick up another boulder.
You see here a boulder.
The second and third statements contradict each other. Make the
code that dishes out the second message smarter. If autopickup is
set for it and you will pick up the boulder:
However, you easily pick it up.
If autopickup is not set for it but would have worked if it was:
However, you could easily pick it up.
If your inventory is full and you have a boulder (or are in Sokoban)
However, you easily push it aside.
That last one is instead of "however, you can squeeze yourself into
a small opening" that you'd get if not a giant and not carrying much.
The #adjust command allowed, due to compactified buf, putting
items into the '-' inventory letter, which is usually reserved
for "empty hands", zeroobj. This caused problems down the line
when user was allowed to pick that letter for dropping.
Reading a scroll while blind is permitted if you know its label, but
message is "as you pronounce the words, the scroll vanishes" unless
you are poly'd into a form which can't make sounds, in which case you
"cogitate" rather than "pronouce". Switch to the cogitate variant if
you are suffering from strangulation.
Casting spells didn't even have the distinction; you could cast them
without regard to speech capability. Check for that. Unlike with
scrolls, now you can't cast if you can't speak (or grunt or bark or
whatever) instead of having a variant description of the action, so
this is a bigger change.
A wishlist/TODO item: when "female" is highlighted, change "caveman"
to "cavewoman" and "priest" to "priestess". If it gets toggled to
"male", change them back.
Rearrange the tests for edibility of non-food so that touching an
artifact won't happen unless the object could be eaten.
Add a bit of bulletproofing for rust monsters trying to eat a
rustproofed item and spitting it out. Wishing for rustproof iron
ring, cursing it, wearing it, and attempting to eat it as a rust
monster would remove the rustproofing and spit it onto the floor,
ignoring the cursed state as far as taking it off goes. That was
an issue in 3.4.3 and probably in 3.6.0, but in current code the
'rustproof' part of the wish would be ignored for an item which
isn't subject to erosion damage, so hero-as-rust monster will
successfully eat the ring instead of spitting it out.