Adds a new boolean option, accessiblemsg. If on, some game messages
are prefixed with direction or location information, for example:
(west): The newt bites!
(northwest): You find a hidden door.
I added the info to the most common messages, but several are
still missing it.
Some functions are passed an obj or monst chain,
and the callers typically don't check them
against 0, so mark them explicitly as NO_NONNULLS
(NO_NONNULLS expands to nothing, but it flags that
some null arg analysis has been done)
Update several places where lazy lastseentyp[] might be an issue.
I think it isn't updated in a timely fashion when newsym() shows
a spot covered by an object or trap, but didn't manage to find any
cases where that caused a problem. This is more in the nature of
a precaution.
Fixup some of the inconsistently formatted code that has been
introduced recently or been building up for a while. Done manually.
I wasn't systematic except for looking for lines ending in '&' or '|'
(which wouldn't find such things if they're followed by a comment)
so there might be lots more. I changed a bunch of C++-style //...
comments to old style C /*...*/ so that they'll match the rest of
the core's code rather than because they shouldn't be used.
Prior to this commit, there was no good way to deal with swarms of
weak, good-AC enemies using magic; trying to play a wizard as a
pure spellcaster would make bees and ants very difficult to deal
with (because they would usually dodge force bolts).
This commit adds a new spell designed to be very good against
swarms of weak enemies: "chain lightning", which does 2d6 lightning
damage to every monster around you. It has an initially short range,
but can chain from monster to monster to cover potentially large
distances (as long as none of the monsters en route are shock
resistant or tame/peaceful; the spell can't chain past shock
resistant monsters and avoids peacefuls). Monsters within one
space of the visible lightning bolts are affected. Unlike other
lightning effects, this one does only 2d6 damage, not enough to
blind affected monsters.
This commit breaks existing save and bones files (thus the
EDITLEVEL increase).
The change to Sokoban difficulty calculation introduced a balance
issue: previously the Sokoban prize was obtainable before item-
destruction attacks became common and would protect from them,
whereas now they commonly appear in Sokoban itself, before the
prize is accessible. This makes scrolls and potions much less
usable as escape items, and potions of healing less usable for
HP recovery, because they either get stashed or destroyed (and
in either case aren't usable by the character in time-critical
situations).
This commit attempts to alleviate the problem by adding a way to
protect items from cold and fire in the early game. It's a
little unreliable, and requires equipping an item that has bad
stats and generally isn't otherwise used, but which is generally
easy to find early on.
Not included in this commit, but probably necessary for a
"finished" version of the feature: some way of letting players
know the new mechanic. It shows up in enlightenment, but there
should probably be at least rumors (and maybe even a major Oracle
consultation) hinting at the mechanic. We might also want to
change the unIDed description of the dwarvish cloak as a hint,
although that would require, e.g., new database entries.
The same checks were being repeated for every damage type; this
sends them through two centralised functions (one for checking
whether an extrinsic blocks a specific instance of item destruction
and one for the enlightenment message), so that new mechanisms of
item destruction prevention will need to change only one point in
the code.
For the accessories section of '*' and perminv_mode=InUse, show the
ring on main hand first (after amulet) and the one on off hand after
(before blindfold). Main hand ring is more significant due to the
potential of a one-handed weapon becoming cursed, preventing it from
being removed.
cg.zeroobj was originally added (under its previous unprefixed name)
for providing a one-line way to zero out the fields of a struct obj.
struct obj tempobj;
tempobj = cg.zeroobj;
initfn(struct obj *otmp)
{
if (otmp)
*otmp = cg.zeroobj;
}
More recently, the address of cg.zeroobj began to be used as a return
flag to indicate some things, but the 'const struct obj zeroobj' wasn't
an ideal fit for the purpose and required a number of casts, including
casting away const.
Provide a better fitting variable (gi.invalid_obj) and eliminate a
number of casts.
Classify nearby ice as "solid" (no melt timer), "sturdy" (more than
1000 turns left), "steady" (101 to 1000 turns left), "unsteady" (51
to 100 turns left), "thin" (15 to 50 turns left), or "slushy" (1 to
14 turns left, matching walking on ice with the Warning attribute).
[I'm not thrilled with "steady" and particularly "unsteady".]
I was originally going to do this just for probing downward, but ended
up also doing it for look-here and getpos's autodescribe. It nearly
got out of hand and touched more files than anticipated.
'mention_decor' ought to treat moving from ice firmer than thin to
thin or slushy, from thin to slushy, from slushy to any other, and
from thin to firmer as if moving onto different terrain but I haven't
attempted to tackle that.
The melt timer could work more like a candle's burn timer, triggering
at intermediate stages and resetting itself, so that ice which changes
to a weaker state under the hero could be reported to the player. But
this doesn't implement that.
Recent changes to wand of probing cause it to map unseen terrain when
zapped into the dark (or while blind) and also to reveal tin contents
if the beam hits such. Extend that to discover secret doors, secret
corridors, and traps whether the spot can be seen or not, and also to
reveal egg contents. Previously, secret corridors were converted into
regular corridors when they couldn't be seen but left as is if their
spot could be seen; now they're found and converted either way.
Issue the kaboom sound effect if zapping a magical trap with wand or
spell of cancellation causes it to go "Kaboom!"
A couple of chunks of code has been moved out of zap_updown() and
bhit() into new routine zap_map().
Wand of make invisible doesn't make you permanently invisible,
just for a short duration. Potion of invisibility makes you
invisible for much longer period, or if blessed, has a small
chance of giving permanent invisibility.
This makes the wand actually useful, and improves the spell
too.
Wielding one or more rocks while other rocks were quivered and casting
stone-to-flesh at self wasn't subject to the "null obj after quiver
merge" panic but it did result in a combined stack of meatballs that
was neither wielded nor quivered. Keep inventory items that turn into
meat and are wielded/alt-wep/quivered separate and still wielded/&c.
Instead of a 5% chance for crystal plate mail or crystal helmet to
break each time it's subjected to breakage, switch to a 10% chance
but the damage is treated as erosion rather than break/don't-break.
'crystal foo' will need to go through four stages of damage before
breaking: cracked crystal foo, very cracked crystal foo, thoroughly
cracked crystal foo, then gone. Crackproof handling is included,
described as tempered crystal foo.
It mostly still applies to throwing and kicking the item. Having
some hits trigger damage might be worthwhile but isn't implemented.
Object creation within lua code probably needs to be updated, and
when the Mitre of Holiness is created in the priest/priestess quest
it should start out as tempered (erodeproof). Perhaps it ought to
be erodeproof regardless of where/how it's created.
Issue reported by vultur-cadens: changing helm of brilliance to
crystal made it stop being classified as "hard helmet" so it gave
less protection against things falling onto the hero's head.
Change the is_metallic() tests used on helmets to new hard_helmet().
Unlike when thrown, crystal helmets don't break when objects fall
on them.
Fixes#1060
bhit() updates the value of bhitpos as a zap or missile progresses
and it also maintains a copy in local x,y. But it was using the
latter inconsistently and also used x,y to fetch drawbridge location,
which might retain the same value or might change one of the two
values by +1 or -1. Use different local variables for drawbridege
handling and use x,y instead of bhitpos throughout bhit().
Also, zapping the spot in front of a closed bridge with striking or
force bolt would destroy the bridge even though the zap was just
passing over moat. Don't destroy the bridge in that situation.
Zapping striking/force bolt across the span of an open bridege still
destroys the bridge.
'sanity_check' complains if it finds a boulder in water or lava.
Polymorphig a statue usually produces another statue but might
produce a boulder. If done it water, keeping the boulder intact
would trigger the sanity warning. Break it into rocks if object
polymorph produces a boulder at water or lava location.
Zapping a wand of teleportation at a location with object and
a monster hiding under it, but with the level full of monsters,
the monster would stay hidden even when the object was moved.
Delete engravings made in a breach of a shop's wall or of a vault's
wall or in the guard's temporary corridor when the wall is repaired
or the corridor removed. If 'sanity_check' was On, those would
trigger impossible warning "engraving sanity: illegal surface (x)"
where x was the terrain type code for solid rock or relevant walls.
Adding del_engr_at() calls to the shop code was straightforward.
The vault code is very complicated and I'm not sure that all the
calls I added were actually necessary.
Burying an olog-hai corpse with a boulder resulted in a panic when
its time to revive occurred. I was able to reproduce this once but
failed with "you feel less hassled" several times (using same save
file for multiple tests) so I'm not quite sure what was happening.
A buried corpse was allowed to revive if it was for a zombie. This
fix extends that to auto-revivers (trolls and Riders). The corpse
keeps its revive_mon timer rather than changing that to zombify_mon.
If/when revival of a buried troll or Rider happens while in view, it
will "claw itself out of the ground" like zombies do.
... unless explicitly specified to generate at a specific point or
within a specific area. But if they are permitted to generate anywhere
on the level, and it contains water, they always end up in the water. I
noticed this when trying to explicitly specify ghouls to generate
anywhere on a level with a minimal amount of water.
This was due to the definition of "amphibious" being conflated with
"breathless", such that all breathless monsters counted as amphibious.
There are plenty of breathless monsters in the game that decidedly don't
normally inhabit water, such as undead, but they would pass the
amphibious() check in pm_to_humidity and thus the game decides that they
must generate in wet terrain if there is any available.
This fix takes the approach of changing amphibious() so that it no
longer checks the M1_BREATHLESS flag and only considers M1_AMPHIBIOUS,
then updating the places where amphibious() and Amphibious are used
accordingly. I also added a new macro cant_drown() which wraps up
swimming, amphibiousness, and breathlessness because these three things
are frequently checked together in the context of whether something
should drown.
Places where amphibious() or Amphibious did NOT have an extra
breathless() or Breathless check added on, and thus where behavior has
been changed:
- The pm_to_humidity function (to fix the bug).
- Player vs water in goodpos; it didn't seem like being polymorphed into
a breathless non-amphibious monster should make it fair game to
randomly teleport into water even though it's technically safe.
- Awarding extra experience when killing an eel. (So the hero will get
the extra experience if they are polymorphed into a breathless
non-amphibious monster and don't have magical breathing. Very much an
edge case.)
This aims to fix the issue in which there are way more wands of speed
monster in the game than the player has any use for. Usually, one is
enough for the whole game (unless a player has a lot of pets they want
to speed up) but since they're an item monsters can generate with, it's
typical to see eight to twelve of them, useless for anything besides
polypiling into other wands since they just grant an intrinsic that's
usually obtained early in the game.
By making the wand effect (on the player) temporary very fast speed, it
becomes desirable to keep the wand around again. By adding a lightweight
source of very fast speed, it also helps diversify character builds away
from always relying on speed boots. And importantly, it becomes an item
the player can use situationally early in the game to enhance their odds
in a fight or run away from danger.
Meanwhile, the speed-intrinsic-granting has been moved to the potion,
and the potion is still superior to the wand in the duration of very
fast speed it grants (100 + d10 turns for an uncursed potion or 160 +
d10 turns for a blessed one, versus 50 + d25 turns for the wand).
Since monsters don't have a concept of very fast speed, this doesn't
change the wand or potion effects on them. They will still gain
permanent intrinsic speed by either method.
Issue reported by AndrioCelos: if hero was killed by a wand zapped
by a monster, the cause of death was "killed by <wand damage>
imagined by <the monster>" instead of intended "killed by <wand
damage> zapped by <the monster>". Report mentioned that the monster
was unseen but that wasn't relevant.
This worked when monster wand zaps were briefly changed to use
-9..-0 but got broken when that was reverted to using -39..-30 (due
to -0 being the same as 0 so making zapper of wands of magic missile
ambiguous).
Fixes#1011
Every other case I'm aware of where a visible monster that the hero can
see turns invisible has some associated message, but not when zapping it
with the wand of make invisible and lacking see invisible. This adds a
message "[monster] vanishes!" in this case, which is the same as the
most common message one gets for zapping a monster with teleportation,
by design to keep the wand's identity ambiguous.
Technically, prior to this commit, there was a leak of information if
one zapped a wand that made a monster disappear: the identity of the
want could be determined immediately by the presence or absence of a
vanishing message. Practically, though, it was easy to check if there
was a new invisible monster in the same spot or not.
Also, zapping teleportation at a monster and having it land somewhere
visible to the hero now has attention explicitly drawn to it with a
message (in prior versions it didn't, so it could potentially be a
different monster appearing by some other means), so I additionally made
the wand of teleportation automatically identify itself when the hero
sees the monster appear as a result of their zap.
Reverse part of commit 4021a63bcf
"wand/spell/breath killer reason" so that wand of magic missile
zapped by a monster isn't ambiguous about who is responsible.
Extend "killed by the touch of death inflicted by <monster>" to buzz().
"Killed by a bolt of cold" becomes "killed by a bolt of cold zapped by
<monster>" or "killed by a blast of cold" becomes "killed by a blast
of cold exhaled by <monster>" and so forth.
More work than expected; the zap code isn't passed enough context.
BZ_M_WAND() was producing the wrong value for wands zapped by monsters.