Gates: locks & conditions

A gate decides whether an interaction is allowed to run. MovaInteract has two gate mechanisms that share one address grammar: a group-level gate (Gates, on the Group's More block) and a tag-level inline gate (?).

debug overlay tags gates (placeholder mockup)

Save the real screenshot as: shot-debug-overlay-tags-gates.png

debug overlay tags gates (real screenshot)

Group-level: Gates

Every Group's More block carries one list, TArray<FName> Gates, checked when the group is asked to start:

  • Every entry must pass, ex: Open, @Lock:!Off.

  • A trailing :!State negates that one entry: allow and block share this one list, there is no separate "must be true" vs "must be false" list.

Same address grammar as everywhere else (see Tag Grammar Reference) so an entry reads Open (self state), @Lock:Off (internal component), or %Vault:Sealed (external actor). This is the coarse gate: it decides whether the whole group is even allowed to begin.

An entry that fails blocks the whole group and fires OnActionRejected with the reason "gate <Condition> not met", so UI and debug tooling both get a specific, per-token reason instead of a plain "no".

Tag-level: the inline ? gate

A ? token on an interaction tag gates a single target, evaluated before that target runs, not the whole group:

!Open @Door ?@Lock:Off
└─┬─┘ └──┬─┘ └───┬────┘
 flow  target   gate: run "Open" on @Door only if @Lock is Off

Use the group's Gates list when the condition should block the interaction from being offered/started at all. Use ? when different targets in the same tag need different conditions (a choice that shows a padlock icon vs one that runs freely).

Other reasons a group can be rejected

ExecuteActionGroup checks three things in order, and OnActionRejected reports which one fired:

Check

Reason text

Meaning

CanExecuteGroup (Blueprint-overridable)

"blocked"

A game-side hook said no, ex: needs a key the player does not have.

Gates

"gate <Condition> not met"

One of the group's own Gates entries failed.

RepeatPolicy

(logged, see Groups & Cycles)

The group is already at rest in a state its RepeatPolicy blocks re-triggering from.

Cross-object gates

Because the condition grammar is the same address syntax used for targets, a gate reads state on another actor without any extra wiring:

@Lock:Locked            my own Lock component
%Vault:Sealed           the external =Vault actor, any component or its IC
%Vault@Bolt:Locked      that Vault's Bolt component specifically

A door checks a keycard reader elsewhere in the level. A vault checks three separate bolts on three separate actors before it opens. No Blueprint casts, no event dispatchers: only an address the resolver walks.

RepeatPolicy and cycle rollback

When a Cycle-triggered group's gate blocks it, the cycle's step index is unaffected, so the next press retries the same group instead of advancing: a locked door's Toggle keeps offering Open instead of skipping ahead to Close because the blocked attempt still "used" a press. See Groups & Cycles for RepeatPolicy in full.

Worked examples

Keycard door: one Gates entry, blocked until the card reader is in its Accepted state:

Group "Open":
  Gates: [ @Reader:Accepted ]

Walking up and pressing without a valid card refuses the group; OnActionRejected fires with "gate @Reader:Accepted not met", which a HUD or debug overlay can show as a denied cue.

Multi-lock vault: three independent bolts, all required:

Group "OpenVault":
  Gates:
    - %BoltA:Unlocked
    - %BoltB:Unlocked
    - %BoltC:Unlocked

Each bolt is its own actor with its own IC, unlocked independently (a separate key, a separate puzzle). The vault door itself holds no lock logic: it only reads three external states.

See also