Consider a Behavior called "switch-places-with-other-monster." When a monster executes this behavior, it is supposed to mean s/he trades places with an adjacent monster; typically this
action will appear qualified by Tests such as "more-wounded-than-me" and "same-species-as-me" and "blocks-route-to-player," enabling a teamwork situation where wounded monsters facing the player are replaced in tag-team style by fresher reinforcements from the rear.
Now, the monster that executes this action has performed something relatively simple: they've moved one square (presumably one square closer to the player). But this action also involved the monster with which places were switched, and as far as that monster is concerned this is a "Displaced action:" That is, an action chosen for it and executed on it by some other monster.
The effects of a Displaced action on a monster are basically that the monster executes a Behavior just as though that monster's AI had chosen that behavior itself; it uses up time, moves the monster, may generate a message, etc.
Now this is a relatively simple idea, but it's powerful. It allows teamwork amongst your monsters in ways that are very difficult or impossible to achieve otherwise, it allows shoving and maneuvering as well as hitting for damage, and generally adds a lot to the game.
But, carried on to a slightly different conclusion, it allows you to save yourself a lot of coding work by building special behaviors into special objects or monsters.
For example, remember Adom's Altars? If the player catches a monster stepping onto an altar he can sacrifice it. Presumably a monster can do the same thing to the player, but who has time to build special behavior into a hundred different state machines and extend the "observe" routine to cover the special case? It's far far better, I think, for the altar itself to be a monster, with a special displaced action that it's ready to use on any co-aligned, adjacent monster if the player-character should be so foolish as to step onto the altar.
Thus, the code for sacrificing the player-character can be built into the altar itself. Now the altar AI is very simple:
ALTAR_AI
IF player-standing-on-same-square
AND adjacent-coaligned-monster-can-sacrifice-player
adjacent-coaligned-monster-sacrifices-player
ELSE stand-still
From the player's point of view this looks like every monster in the dungeon suddenly got smart enough to know how to sacrifice him if he steps onto their altar. But it's absolutely unnecessary to give any mention of this behavior in the monster AI's themselves, except for
the Altar AI.
Many other bits of your dungeon can work the same way; special levers that operate gates, special gateways that conjure demons, doorways that somebody has to lock before the orc guards flee the room, thrones that want one goblin king to sit on them, and all kinds of other things can work with generic monsters, just by having those objects have AI's that get other monsters to operate themselves using Displaced Actions.
In order for a monster to be eligible to commit a displaced action, it has to have enough time. This is trickier than it seems. In most rogulikes, the monsters choose actions and act, one at a time. This may mean that, by the time the Altar, or doorway, or gate or throne or whatever acts, all the monsters around it have already committed their time for the next round and it has no monster it can make into its operator. So things that use Displaced Actions, in a turn-based system, need to have a pretty high "initiative" score so that they pick their operator before the operator decides to do something else that round. In roguelikes that use time or pulses, then you're going to have the situation where it becomes the Altar's turn and there are no monsters around it whose turn it also is. This means it picks its operator, but the operator isn't technically able to do anything until its turn comes around.
There are a couple of ways to handle this; my own favorite is that when the altar/door/gate/whatever chooses "stand-still" because it can't find an eligible operator, its "stand-still" behavior waits until the instant before its chosen operator's turn comes around.
(alternatively, this may be a different behavior named wait-upto-monster or similar: wait-on-monster waits until just after the monster's turn). This requires a function that can query another monster to see when its next turn is, but that's fairly easy.
Then, when it wakes up an instant before its chosen operator, it uses its Displaced Action, which also uses up the operator's next turn -- and whatever the "instant" is (one 'pulse' or 'phase' for you guys who break up the round into even numbers of 'pulses' and 'phases' or maybe a tenth of a second for continuous-time games where dungeon time and next-turn are float values), that can be added to the time needed to complete the Displaced Action so it all comes out even.
Anyway, with Displaced actions, you can fill your dungeon with all kinds of tricky traps and mechanisms, and most of your monsters will "magically" get smart enough to use them.
In order to support Displaced Actions, you will need a bunch of tests allowing action-displacers to check on the status and location of nearby monsters, and, of course, a bunch of displaced-behaviors that the action-displacer can project. You will probably need a state variable that specifies which monster of the nearby ones is under consideration. Once you have those, you can implement stateless AI's and state machines that use displaced tests and
displaced actions.
Uses range from the simple (monsters trading places) to ways to implement class abilities (priestly "turn-undead" effects) to funny (wand that forces any monster to spend its next few actions singing and dancing) to the arbitrarily complicated (lever that floods the next room, other lever that releases piranhas, other lever that flushes next room, fourth lever that uses the rushing water to power a "shredder" so as to properly compost the effluvient...). Creativity is encouraged, but try to avoid getting excessively capricous or vicious. Most of the "best"
uses of this capability are in fact pretty simple.
– Ray Dillinger
Acceptance Criteria
[ ] I've pondered this idea at length and have opened a followup issue to address my findings.
Story
Consider implementing a displace action.
Acceptance Criteria