I am still working on release 0.3 and right now I am tinkering with a brand new problem: how to create and chain timed events inside a game turn.
As always, the problem is harder than it sounds.
Imagine the following situation: your character is hit by a rush attack that pushes it onto a sheet of ice, making him slide even further.
These are the kind of mechanics that are at the core of Wizards of Unica. These kinds of “emergent” behaviors are what gives depth to the game on top of other mechanics like spell combinations.
Let’s analyze in detail what needs to happen in the aforementioned scenario, all during the same turn:
- An attacker entity moves one step closer to a target, becoming adjacent;
- After becoming adjacent the attacker hits the target;
- The target sustains damage and is pushed back;
- After the target has finished moving the game recognize that it is standing on a slippery surface and calculates that it is going to shift another step;
- The target is moved and the round ends.
To pull this off I need to enqueue all the steps so that they are executed with the right timing. Without this Time Queue the player would see everything happening simultaneously: the attacker would shift while playing the attack animation and the target would shift at the same time by two cells while taking damage. Everything would feel disconnected and the feedback to the player would be confusing and disorienting.
The trick for a turn-based game is that you need to manage two different timelines. The first one is Initiative, which keeps track of the turns and of what entity is going to go next. The second one is the actual Real-Time queue, which executes actions one after another.
I am still evaluating the best design for these two and, in specific, how to best model their interactions. Right now the Real-Time timeline is the dominant one and it is modeled as a list of Actions, with each Action characterized by a function and a Time To Live (zero by default, meaning that the function is run instantaneously, like damaging a target or creating a particle effect).
During each update loop the first action in the Real-Time queue is executed/updated, until the TTL expires and the Action is removed from the top of the list. If there are no Actions left in the Real-Time queue, the Initiative queue moves forward until some entity acts, thus pushing new actions inside the Real-Time queue and restarting the cycle.
Everything is still in flux for now, and I am far from being confident that this is the best approach, even if it seems pretty straight forward.
What do you think? Have you any experience with this kind of situations that you care to share?
Thanks for reading.