Monster invisibility can create some interesting and memorable situations,
but i…s somewhat divisive among experienced players. One recurring
complaint is that even in situations where there is no ambiguity about a
monster's identity or location, the interface still makes it more
difficult to deal with them - autofight stops working, many spells require
force-casting (since the game thinks no enemies are in range), and
sometimes the game doesn't do a very good job of making it clear to the
player that there even *are* invisible monsters around if a situation is
crowded enough. Perma-invis monsters can also present learnability issues
for new players, since there is no way to examine them (or even know what
type of monster to look up).
This is an attempt to make the UI work more smoothly around invisible
monsters, and keep better track of known information. In doing so, it
changes a few rules around what counts as 'known information' regarding
invisible monsters. It is my hope that these changes allow preserving some
of the interesting situations invisibility can create while reducing
unwanted friction.
First of all, the old 'outline of a sensed invisible monster' system is
completely replaced. Instead, monsters whose positions are unambiguously
known to the player are drawn using their normal monster tile (somewhat
transparently, and with an additional icon) and most parts of the UI
regarding examining and targeting them should now treat them similarly to
a visible monster.
To be clear, these monsters are *still invisible*; the player cannot
obtain any information about their health or current status effects, still
suffer combat penalties against them, and cannot target them with effects
that strictly require a visible target. This is purely a UI convenience
for the many situations where the player could already deduce this
information.
These 'known invisible' monsters can be sensed via most methods which
previously created monster outlines (ie: by being in shallow water or
clouds, or firing spell projectiles), but also several others ways, such
as 'bumping into them'. In other words, walking into a ghost moth will
reveal specifically that there is a ghost moth on that tile and display it
as such. This means that an invisible monster's type is now considered
public information in any scenario where a monster has been sensed. (Doing
so greatly simplifies the interface regarding them, allows people to xv
things like unseen horrors, and I think comes with little cost since the
number of situations where an invisible monster could realistically be
more than one type of thing is very small.)
On top of this, invisible monsters whose position was *recently* known,
but have since moved, are drawn with a variation on this tile to indicate
they are no longer there (but likely still nearby). This should help the
player keep track of what threats are still around, even if they cannot
currently see them. As such, 'whether a known-invisible monster has moved'
is also now considered public information. Assume that the player is using
hints like the sound of footsteps or changes in airflow to infer, in a
general sense, that movement has occurred.
Finally, in cases where a monster has performed some action that informs
the player of their presence *without* revealing their location in any way
(most commonly when a perma-invis monster attacks the player in melee),
the game will still add them to the monster list and treat them for a
short while as if they were nearby, for purposes of delays and
autoexplore. (Fully sensing a monster just from being hit by it would
significantly defang the interesting positional play that can come from
ambushes by ghost moths and unseen horrors, so you still have to actually
interact with them successfully *yourself*).
This new system of treating invisible things which are *probably* nearby
as threats (for autoexplore/delays) fully replaces the old autopickup
autotoggle. It works more reliably, in my experience, than the old system
did (which, for example, failed to interrupt travel while being attacked
by invisible things, so long as they *missed*, and would turn back on at
the first invisible thing killed, even when multiple were known to be
around.) and also doesn't let you forget to turn it back on (and
periodically cause newcomers to be confused as to why they stopped picking
up things).
A few caveats here:
When a 'remembered invisible' monster is nearby,
spell_no_hostile_in_range() will treat them as potentially always being in
range (so as to avoid the needless friction of needing to force-cast in
scenarios where there is almost certainly a hostile nearby). This *does
not* properly account for the monster's resists, entirely because the code
involved made this very awkward. I hope this will be a very minor issue in
practice.
The blocking of autoexplore uses a LoS check to the position the player
was standing in when the monster was last sensed. In my experience, this
is 'correct' the vast majority of the time (in that it blocks autoexplore
when an experienced player would realistically consider it unsafe, and
doesn't otherwise), but there are some very specific situations where the
player can know they're safe nonetheless (an obvious example involves
using Imprison to put walls in the way of the general area where an
invisible monster was known to be). In order to prevent the player from
simply being unable to autoexplore here, I've added a new command to
temporarily 'forget' about nearby invisible threat (CMD_IGNORE_INVISIBLE,
bound to Ctrl+T by default). This is a bit clunky, but I was unable to
devise a heuristic that didn't have *some* fail cases, and I believe the
player should almost never have to use this command.
No *all* actions an invisible monster can perform cause the player to
sense them. While beam code covers many spells, there are many, many
spells that do not use it. For instance, an invisible monster can summon
creatures in LoS of the player without this giving away their presence
(even though the player can tell that something *must* be nearby).
However, I feel strongly that full coverage is *not a pragmatic goal*, nor
worth the code complexity. Turning invisible while in LoS already senses a
monster, so they would need to first turn invisible while out of LoS
(itself a rarity), and even then, the summons themselves will interrupt
delays (and it's unlikely the monster will not be sensed in some other way
while the player was fighting them). Antimagic Gaze in particular *does*
alert (since it's used by a common permainvis monster), but I feel that
covering the vast majority of common cases is better than suggesting a
standard whereby every way in which the player could infer a monster's
presence was handled by the UI. That way lies madness.
---------
In terms of code structure, you.aware_of() is a new method that includes
both you.can_see() *and* the presence of known-invisible monsters. For
most UI and targeting purposes, aware_of() is now the method that should
be used.
Since known-invisible and visible monsters now look rather more similar to
each other than before, this batch of commits will also attempt to enforce
some general principles about what does or doesn't work on invisible
monsters:
-All beams, explosions, and projectiles can affect and be aimed at
invisible monsters
-Spells which hit an entire area (or all of LoS) can affect invisible
monsters
-Spells which automatically target a *random subset* of monsters in LoS
will *not* target invisible monsters
-This was true for several of these already, but will be applied more
universally in a subsequent commit
-Non-damage spells which directly target a single enemy cannot affect
invisible monsters (unless part of the point is that they can, such as
Yara's)
-This was already true of most of these, but will be applied to a few more
in a subsequent commit
In addition, several player downsides that depended on the presence of
only visible monsters (to avoid leaking information) are now active while
known-invisible monsters are nearby (eg: 'No regen near monsters'). No
more removing your SInv ring to conveniently forget about things.
Finally, the old 1-in-7 chance to passively detect any nearby invisible
creature no longer exists. (I feel like there are plenty of ways to get
information on them that this isn't needed; in many ways, it was working
as a poorer version of the known-invis system, except that it also worked
on sleeping monsters at random sometimes.)
(This is a very large commit that touches enough different things that I
am not confident I've explained everything it does in even this much text,
but hopefully am close.)