Wednesday 6 February 2008

Mumble mumble mechanics mumble

Timing

Timing is a tricksy beast, but here are my thoughts so far. Each action has an associated cost which represents the time required to execute that action. Some actions may have an additional 'lead in' cost, and these may or may not also be flagged as interruptible.

In execution, this is fairly simple apart from all the horrible edge cases. Acting creatures (including the player character) have an energy value. Conceptually, every creature gains energy every game tick. A given creature gets to act when its energy value hits zero. If it performs a simple action (moving, for example, which may cost 100E) the cost gets deducted from its energy, pushing it into negative values. If it instead performs a complex action with a lead-in time (as another example, a complex and powerful melee attack may cost 150E and have a 50E lead-in) the following sequence occurs:
  1. The lead-in action occurs, consuming that portion of the energy cost. This will generally also telegraph some information about the action to nearby creatures ("The goblin winds back her weapon and screams a battle cry!"). In our example, the goblin goes from 0E to -50E
  2. The lead-in action may also add a status effect, such as interruptible, off balance or something. Examining the creature in this state should reveal the nature of things. ("The goblin is preparing to attack. She looks unstable on her feet.")
  3. The creature goes back into the list of critters awaiting updates, but has a the main part of their action attached as a latent action. The goblin ticks up from -50E to 0E
  4. When the creature gets to act again, and provided none of the preconditions for the main (latent) action have been invalidated, the next part of the action executes and consumes the remaining energy. As with any action, there may be an attached transient status effect e.g. winded or fatigued. ("The goblin strikes for massive damage and falls back, exhausted by her efforts"). Our goblin falls to -150E.
  5. If the preconditions have been invalidated, the creature gets to choose another action as normal and the lead-in is wasted. ("Seeing the brave hero flee around the corner in a cloud of terrified flatulence, the goblin lowers her weapon and steps forward menacingly")
The tricky bit is making sure the precondition evaluation is sane and covers all the edge cases. In the example above, the target could have moved out of range or become invisible, the goblin herself may have been moved, disarmed, knocked down, paralyzed or a host of other things during the lead-in. There is the question of when to allow the creature to act when such preconditions are invalidated, especially if status effects that allow easy interruption of the action are implemented.

We can separate these invalidating conditions into recoverable and unrecoverable sets.
  • Recoverable conditions include such things as the target moving out of range, sight or line-of-effect. These may be rectified before we have to act. For example a retreating character may move out of range of our goblin, but her squadmate may knock the character back into range immediately.
  • Unrecoverable conditions include being disarmed or knocked down. Even with favorable outside influences, the action has been interrupted and cannot really be completed.

So we could then say that events which cause unrecoverable conditions might also let the acting creature immediately pick a new course of action, effectively resetting their energy level to zero. But these lead-in actions should represent a risk, plus it seems unrealistic to let creatures react instantaneously to changes, so for the sake of simplicity I think the better approach is to only let a creature act after the lead in, irrespective of the type of condition which caused the original action to become invalid.


So, what does all this blether buy me?

For starters, it suggests that actions should potentially have status effects attached. It also suggests that care should be taken with status effects that are tied to action durations, and that a unified way of operating on lists of entities awaiting update would be nice.

It allows for preparatory actions. A kiai that provides a short term boost to some physical stats could be modeled as a lead-in of moderate duration, followed by an instantaneous buffing action. As the final part of the action has zero duration, the creature gets to act immediately.

It allows forewarning the player (and AI), equipping them with information about an incipient action and hopefully giving them sufficient time to consider preventative measures. Some roguelikes come with a slew of actions that are only conditionally useful - no-one wanders around with their ears stoppered with wax all the time - but because of the random nature of many RL elements it can be hard to use them effectively. If you enter a room to find a sleeping BansheeBunny, great. If it wakes just as you round the corner, the dread beast will be howling your brain to jelly before you so much as reach for the dribbly candle.

It implies that I can add some tactical options. Instead of being able to rest/wait for a fixed duration as is typical in roguelikes, creatures can opt to wait until just after (the exact time will probably depend on a derived reaction speed stat) another visible creature acts. If they then choose an action with a lead-in, you're in a position to act in good time. If they instead use a standard action, you've only lost the minimal amount of time.

Lastly, it allows me to compose actions together. If using an object from your backpack takes longer and is more subject to interruption than using one from your belt/pockets/bandolier, it makes sense from a code perspective to create the lead-in bit and simply reuse the 'activate inventory item' code that's already been written, debugged and so forth.


Terminology

These 'actions with lead-in' will be termed compound actions. They'll probably help delineate classes of actions, but I anticipate it'll be somewhat fuzzy and they could crop up anywhere.


Implementation

In practice, rather than update the energy for all creatures every turn, we only need to sort our list of active creatures by energy and increment them all by the minimum (absolute) value, so every tick at least one creature gets to act. Tie breaks could be resolved in several ways, but initially will probably just be sorted by dexterity/speed or something and executed in order.

Because ticks are irregular, any effect with a duration must have an entry in the 'creature' list. A buffing spell will have a presence in the list, initially with energy = -Duration, and remove itself from the target creature when it gets to act. Such transient effects never go back into the list of course, and must be removed should the effect be terminated early (dispel etc.). Inconstant effects are a bit of a pain in the arse, requiring either many mini-actions to fiddle variables or an integration scheme that gives the accumulated result between creature updates.


Haste, Rings of Speed and their ilk

Haste spells and McGuffins of Speed are a staple of the genre, and generally have slightly odd effects. In this case, anything which affects your speed simply multiplies the cost of all relevant actions by a real number. As I'd like stats and skills to influence speed of related actions, this means classifying actions somewhat.

No comments: