Skip to content
/a/ FaceCue Performance Studio

Sources and Overrides

Every world input works the same way underneath. There is a source your game supplies, an override that can mask it, and an owner attached to that override. Read this once and the four lanes are all the same shape.

The Two Layers

A source is an interface you implement and hand to the brain. FaceCue polls it: whenever it needs the value, it asks.

An override is set imperatively, and it belongs to an owner. While an override is present, reads take it and never reach the source. Remove it and reads fall through to the source again.

They are layers, not alternatives. A source is the steady answer, and an override is the exception on top of it: a scripted moment that takes a character's gaze for the length of a cutscene, then gives it back to whatever was driving it before.

You can use either alone. A game that only ever sets values imperatively never implements a source, and one with a good source of its own may never set an override.

Owners

An override is claimed by an owner, and every lane can report which kind currently holds it.

Owner kind Means
None No override. Reads reach the source
Anonymous Claimed through the short form, with no owner given
Explicit Claimed by a specific object

The short form claims a shared anonymous lane. It is fine for a single system that will never contend with another, and it is the one to use while prototyping.

The owner form is what lets two systems share a character. Pass whatever is responsible for the value, usually the component making the call. Each system can then ask whether it is the one currently holding the lane instead of assuming.

Releasing

Three ways to give a lane back, and the difference matters.

Release…Override() gives back the anonymous lane.

TryRelease…Override(owner) gives it back only if you still hold it. This is the one to reach for. A system that has already been superseded cannot pull the value out from under whatever replaced it, so a late cleanup from something that lost the lane ten seconds ago does nothing instead of causing a glitch.

ForceClear…Override() takes the lane back regardless of who holds it. It is administrative, for tearing down a scene or recovering from a state you no longer trust.

When Something Is Destroyed

You do not have to clean up after an owner that has been destroyed. At the start of every frame, before anything on the character reads its inputs, the brain checks each lane and withdraws any override whose owner no longer exists. On the Look Target lane it also withdraws one whose target no longer exists. Either way the lane goes back to its source with a Vacated event, and a character does not go on looking at an object that is gone.

Between the destruction and that check, for whatever is left of the frame it happened in, the override is still there and still masking. The lane resolves to nothing for that instant instead of falling through to the source. That is deliberate: falling through would let the source's value flash onto the character for a frame and then be taken away again, so a character whose look target was destroyed would snap to whatever the source was pointing at and then let go of that too. One clean transition instead of two wrong ones.

So both things are true, and they happen in that order. A dead override masks for the rest of its frame, and FaceCue releases it on the next one.

Release Deliberately Anyway

The automatic release is the safety net, not the plan. Releasing yourself happens when you meant it to, with a Released event your other systems can act on, rather than a Vacated one on the next frame.

Change Events

Every lane publishes an event when its override changes. Each page documents its own, with the payload that lane carries, and they all share the same set of transitions.

Transition What happened
Acquired An override was claimed where there was none
Updated The value changed, same owner
Transferred A different owner took the lane
Released Given back by the holder
ForceCleared Taken back administratively
Vacated Lost automatically, because the owner or the value was destroyed

Two details are worth knowing before you write a handler.

A replacement is one event, not two. A setter that replaces stale state publishes a single Updated or Transferred, never a vacancy followed by an acquisition. You will not see a lane appear to empty and refill.

On a Vacated event, the previous owner is a destroyed object. It is kept instead of nulled so identity comparison still works, which is what lets you check whether the thing that just went away was yours. Do not dereference it. Comparing it is safe, using it is not.

Everything Lands Together

All lanes are maintained in one batch, from the brain's own update, and every lane commits before any handler runs.

If your game clears a look target and a dialogue state in the same moment, a handler woken by the first would otherwise be able to look at the second and see a lane still holding a dead owner. Committing the batch first means nothing ever observes a half-updated character.

Maintenance runs from the brain's update and never from a getter, so reading a character's state never changes it.