# Sequence Tracking and the Gesture Engine
## Sequence Tracker
The Sequence Tracker is a library I have written that allows for tracking of a sequence of steps over time. It's represented as a 'tree', with branching paths depending on the gestures that are done.
### Usage
You start off by creating your root Step. This should be done once only, such as in the `Start()` or `Awake()` unity methods.
For example:
```csharp=
public class SomeMono : MonoBehaviour {
Step root;
public void Start() {
root = Step.Start();
}
}
```
`Step.Start()` takes an optional `Action` parameter - an action that is called whenever a step is successfully completed.
This can be helpful both for letting the player know they've successfully completed a step, or for debugging (as you can e.g. log the current state whenver it changes).
```csharp=
root = Step.Start(() => Player.currentCreature.handRight.HapticTick());
```
---
#### Actions, Functions and Lambdas
An aside: if you're not familiar with `Action` or `Func` in C#, they are essentially the type of a function.
- An `Action` takes zero or more arguments and returns nothing
- `Action` is an action with no arguments and no return value
- `Action<int>` is an action with one `int` argument and no return value
- A `Func` takes zero or more arguments and can return something
- `Func<bool>` is a function with no arguments that returns a `bool`
- `Func<string, int>` is a function with one `string` argument that returns an `int`
These can be represented in a couple ways. You can define a function as normal and reference it like this:
```csharp=
public bool IsEven(int number) {
if (number % 2 == 0)
return true;
return false;
}
public void Start() {
Func<int, bool> function = IsEven;
Debug.Log(function(1)); // false
Debug.Log(function(2)); // true
}
```
Or, you can define an inline function - also known as a "Lambda Function".
```csharp=
public void Start() {
Func<int, bool> function = (number) => {
if (number % 2 == 0)
return true;
return false;
};
Debug.Log(function(1)); // false
Debug.Log(function(2)); // true
}
```
This can be shortened further:
```csharp=
public void Start() {
Func<int, bool> function = (number) => number % 2 == 0;
Debug.Log(function(1)); // false
Debug.Log(function(2)); // true
}
```
For functions or actions without arguments, the syntax looks like this:
```csharp=
public void Start() {
Action sayHello = () => Debug.Log("Hello!");
sayHello(); // says Hello!
}
```
---
Once you've made your root step, you can begin defining your sequence.
This mainly uses two functions: `.Then()` and `.Do()`.o
There are many overloads of `.Then()` to make it easy to add in multiple gestures, various parameters, or even sets of sets of gestures. But the most basic looks like this:
```csharp=
public void Start() {
root = Step.Start();
root.Then("Condition A", () => conditionA)
.Do("Action A", () => actionA);
}
```
The parameters of `.Then` are as follows:
- `name`: A human-readable name for the step.
- `condition`: A `Func<bool>` that returns true if the condition has been achieved, or false otherwise.
The parameters of `.Do` are similar:
- `name`: A human-readable name for the action.
- `action`: An `Action` that is triggered when the steps have been completed.
A practical example: let us write the tree for the following sequence involving an `Item`.
1. Item is held
2. Item is dropped
3. Item is held again
```csharp=
public void Start() {
root = Step.Start();
root.Then("Item held", () => item.mainHandler != null) // held
.Then("Item dropped", () => item.mainHandler == null) // not held
.Then("Item grabbed again", () => item.mainHandler != null) // held again
.Do(() => Debug.Log("Gesture complete!"));
}
```
Note that the name of the steps - the first parameter - is purely for debugging. You can leave it out but I strongly recommend you leave it in. Debugging aside, it helps you to remember which step is which.
#### Updating a sequence
Once you're done, you must then update the sequence tracker every frame.
```csharp=
public void Update() {
root.Update();
}
```
**If you don't do this, your sequence will do nothing!** Calling `Update()` tells the tracker to check conditions and traverse its own tree.
#### Resetting a sequence
The last important note is about `root.AtEnd()` and `root.Reset()`. Once a sequence is complete, the tracker will not automatically reset - it will just sit there doing nothing, as there are no more conditions to check. You can test whether the sequence has hit an end point with `root.AtEnd()`, and you can reset it from the start with `root.Reset()`.
#### Branching
Let's say we want to do two different things for the previous example depending on whether the creature that picked up the item the second time is a player or an NPC.
We can store the state at the point of being dropped, and then branch off from it depending on what picked it up:
```csharp=
public void Start() {
root = Step.Start();
var dropped = root
.Then("Held", () => item.mainHandler != null)
.Then("Dropped", () => item.mainHandler == null)
dropped
.Then("Held by player", () => item.mainHandler?.ragdoll.isPlayer == true)
.Do("Log player hold", () => Debug.Log("Item held by player!"));
dropped
.Then("Held by NPC", () => item.mainHandler?.ragdoll.isPlayer == false)
.Do("Log NPC hold", () => Debug.Log("Item held by an NPC!"));
}
```
#### Debugging
There are a couple tools available to debug. One common pattern I find myself using is this:
```csharp=
public void Start() {
root = Step.Start(() => Debug.Log(root.GetCurrentPath()));
}
```
Whenever a step is complete, this will log the current path like this:
```
Held > Dropped > Held by player
```
The other useful tool allows you to visualise the entire tree:
```csharp=
Debug.Log(root.DisplayTree());
```
This will dump the entire tree to the console in a format such as this:
```
- Held
- Dropped
- Held by Player
- Action: Log player hold
- Held by NPC
- Action: Log NPC hold
```
#### Other Usage
##### Condition Sets
Sometimes you want multiple steps to be done under one name, or you want to have a function that dynamically generates a set of steps.
This can be done in the following way:
```csharp=
public NamedConditionSet ComplicatedSequence() {
return Tuple.Create("Name of sequence", new Func<bool>[] {
() => someConditionA,
() => someConditionB,
() => someConditionC,
() => someConditionD
});
}
```
`NamedConditionSet` is an alias for a tuple of a name and a list of condition functions. You can alias this as such:
```csharp=
using NamedConditionSet = Tuple<string, Func<bool>[]>;
using NamedCondition = Tuple<string, Func<bool>>;
```
##### In-sequence Actions
You don't just have to put a `.Do()` at the end of a sequence! `.Do()` can be placed at any step in the process, although note that each step can only have _one_ `.Do()` attached to it.
```csharp=
root = Step.Start();
root.Then(() => ...)
.Do(() => ...)
.Then(() => ...)
.Do(() => ...)
.Then(() => ...)
.Do(() => ...);
```
##### Combining Steps
Sometimes it is nicer to have complicated steps written out over several lines. You can do this with `.And()`:
```csharp=
root = step.Start();
root.Then(() => conditionA)
.And(() => conditionB)
.And(() => conditionC);
```
This is functionally identical to the following:
```csharp=
root.Start()
.Then(() => conditionA && conditionB && conditionC);
```
##### Delays
You can use `.After(duration)` to wait a bit before checking the next step.
```csharp=
root = step.Start();
root.Then(() => conditionA)
.After(0.5f)
.Then(() => conditionB)
.Do(() => ...)
```
##### Time-related Steps
Sometimes you want to check if a condition is true for more (or less) than a particular duration.
The following two steps check to see whether a button is tapped or held, by checking whether it was released before or after 0.3 seconds.
```csharp=
root = Step.Start()
var buttonWasTapped = root
.Then(() => buttonPressed, "Button Tapped", 0.3f, mode: DurationMode.Before);
var buttonWasHeld = root
.Then(() => buttonPressed, "Button Held", 0.3f, mode: DurationMode.After);
```
This works with the `endCondition` parameter of `Then()`, which lets you define a custom check for 'condition complete'. Otherwise it defaults to the inverse of the start condition.
---
## Gesture Engine
The Gesture Engine is a library for Blade and Sorcery mods that allows you to detect hand gestures.
:::info
Note: you must have your C# version set to 'latest' to use this library.
:::
### Usage
```csharp=
public class SomeMono : MonoBehaviour {
Step root;
public void Start() {
var gesture = Gesture.Left
.Palm(Direction.Inwards) // palm pointing inwards
.Moving(Direction.Down) // hand moving downards
.Point(Direction.Forwards) // index direction pointing forwards
.Fist; // hand is making a fist
}
public void Update() {
if (gesture.Test()) {
Debug.Log("Gesture activated!");
}
}
}
```
You can also use it in concert with the Sequence Tracker. A `Gesture` or `GestureStep` implicitly converts itself to a format compatible with the Sequence Tracker, including adding in a generated human-readable name for the gesture.
```csharp=
public class SomeMono : MonoBehaviour {
Step root;
public void Start() {
root = Step.Start();
root.Then(Gesture.Both // both hands
.Palm(Direction.Up) // palm upwards
.Point(Direction.Outwards) // point outwards
.At(Position.Face) // positioned near the face
.Offset(Direction.Up, 0.3f)); // offset that position upwards by 0.3m
.Do(() => Debug.Log("Praise the sun!"));
}
public void Update() {
root.Update();
}
}
```
A full list of all the checks you can add to your gesture can be found in the code, which is self-documenting.
The Gesture Engine is set up to allow easy handedness options. Changing `Gesture.handedness` from `Side.Right` to `Side.Left` puts it into left-handed mode, which means that `Gesture.Left` and `Gesture.Right` will be switched.