# Tag Grammar Reference One syntax is used for **targets** (what an action animates), **conditions** (gates), and **filters**, across every field: component tags, actor tags, proximity Enter/Leave, an Action's `Target`, and a group's `Gates` list. Learn it once. ```{note} This page is the full reference. {doc}`../getting-started/quickstart` shows it in use; {doc}`model` explains what a tag ultimately triggers. ``` ## Two principles 1. **Each character has one job**, in every field. A token reads the same whether it sits in a Target or a Condition, only the field it lives in changes what it's *for*. A mangled token (a stray space, a wrong character) lands on an *invalid* token the parser and the panel's chips reject, never on a *different valid meaning*. 2. **Selection and execution are separate.** `@` `=` `%` `#` pick *which* component/actor. A trailing **`^`** picks *whose IC runs the verb*. They compose independently. ## Selection: which thing | Prefix | Points at | |---|---| | `@Name` | a **component** named `Name` on the current actor | | `@=Tag` | a **component tagged** `=Tag` on the current actor (`=` = "by tag" modifier on `@`) | | `%Tag` | an **external actor** (anywhere in the level) tagged `=Tag`: actors are always addressed by tag | | `#Special` | logic-resolved: `#Self` `#Parent` `#Root` `#Actor` `#Carry` | | *(nothing)* | this IC itself (only valid before a `:State`) | ## Bones: `/` (only after `@`) A trailing **`/Bone`** on an `@` hop targets that **bone** of a Poseable Mesh instead of the component itself: | Form | Means | |---|---| | `@DoorMesh` | the **component** `DoorMesh`, moved as a whole | | `@DoorMesh/hinge_01` | the **bone** `hinge_01` on `DoorMesh`: the mesh itself stays put | - `/` is a bone delimiter **only** under `@`. A plain `DoorMesh/hinge_01` (no `@`) is not a valid address and is rejected as unparseable. - The target must be a `UPoseableMeshComponent`. A skeletal mesh is driven by its AnimInstance and fights a bone pose: see {doc}`bones`. - The bone is driven in **component space**; anything skinned beneath it follows. - Deltas layer onto the bone's captured `@Zero` pose, not the world origin. ## Execution: whose IC runs it (the `^` suffix) A trailing **`^`** on a hop means "hand the rest of the address to *that* element's own IC" (recursively). With no `^`, the IC that ran it resolves the whole path and drives the resolved components **directly**, with its own keyframes. | Form | Means | |---|---| | `%Lift` | our IC drives `Lift`'s component(s) directly | | `%Lift^` | hand off to **`Lift`'s own IC**: it runs *its* interaction | | `@Door` | our IC drives my `Door` component | | `@Door^` | my `Door` is a child-actor with its **own IC** → hand off to it | `^` is **execution-only**: it appears on targets, never in a condition (a condition only *reads* state, it never delegates). ## Chaining: where `^` sits Walk the path with `@`/`=`. Drop a `%Actor` in to jump to an external actor. Place `^` after whichever hop's IC should take over. Everything to the **right** of a `^` becomes the sub-target that IC resolves next. | Token | Reads as | |---|---| | `%Lift@Panel` | external `Lift`, its `Panel` component: **our** IC drives it | | `%Lift@Panel^` | ...hand off to **`Panel`'s** own IC | | `%Lift^@Panel` | hand off to **`Lift`'s** IC, which then targets its `Panel` | | `%Lift^@Platform^@Motor` | `Lift`'s IC → its `Platform`'s IC → that `Platform`'s `Motor` | | `@Platform^` | (editing `Lift`) my own child `Platform` → **its** IC | ## State: `:` Append `:State` to any address to test / filter by resting state: | Form | Means | |---|---| | `:State` | **this IC** is in `State` (no address = self) | | `@Lock:Off` | my `Lock` component is in `Off` | | `%Lift:Locked` | the external actor `=Lift` is in `Locked` (any component, or its IC) | | `%Lift@Platform:Docked` | `Lift`'s `Platform` component is in `Docked` | | `:!State` / `@Lock:!Off` | **not** in that state (`!` in the state slot = negate: unambiguous there; `!` is "flow" only at a token's very start) | ### Phase: has it arrived, or is it still moving? A state is only recorded once the animation **reaches** its keyframe. Mid-move a target holds no state at all, so a plain test reads a sliding drawer as still closed and lets a colliding move start. Mark the phase to say what counts: | Form | Means | |---|---| | `@Drawer:Open` | moving toward `Open`, **or** already there (the default) | | `@Drawer:=Open` | **fully** `Open`, the animation has finished | | `@Drawer:~Open` | **on its way** to `Open`, not there yet | The unmarked form is the forgiving one: a collision gate usually means "is it open *or about to be*", and that is the easy thing to type. Markers combine with negation, the `!` coming first: ```text !OpenY @Drawer2 ?@Drawer3:!Open └──────┬───────┘ open Drawer2 only if Drawer3 is neither open nor opening ``` Use `:=` for "the move is complete", ex: only allowing a lift to be called once its doors have fully shut. ### Filter vs gate: same `:State`, two roles - **In a target → filter.** `%Lights:On` drives *only* the `=Lights` actors currently `On`. It narrows the set, it does not stop execution. - **In a `?` token → gate.** `?@Lock:Off` doesn't select anything, it decides whether the whole interaction runs. ## Gate: `?` (inline precondition, tags only) | Form | Means | |---|---| | `?
:State` | run this interaction **only if** the predicate holds | | `?
:!State` | run **only if not** | | `?
:=State` | only if it has **fully** reached that state | | `?
:~State` | only if it is **still moving** toward it | ```text !Open @Door ?@Lock:Off └─┬─┘ └──┬─┘ └───┬────┘ flow target gate: run "Open" on @Door only if @Lock is Off ``` For a group-level gate instead of a tag-level one, use the group's own `Gates` list: same address grammar, a trailing `:!State` negates that one entry. See {doc}`gates`. ## Flow & label: start of a verb token | Prefix | Flow | |---|---| | `!Verb` | **parallel**: runs on its own | | `>Verb` | **choice**: offered as an option to pick | | `+Verb` | **extension**: adds onto the previous interaction | | `Verb/Label` | `/` splits the run verb from its **display** label | The verb names a Cycle or Group, resolved by whichever IC ends up running it (the IC that ran it, or a delegated one via `^`). ## Wildcards `*` globs inside a name, ex: `@Door*` hits `DoorHandle`, `DoorHandle2`. `?` is **not** a wildcard here, it is the gate prefix. ## Worked examples ```text # Targets: selection (@ @= % #) + optional ^ (delegate) @Door my Door component (our IC) @Door^ my Door child-actor → its own IC @=Handle my component tagged =Handle @Door* my components whose name starts "Door" #Self the component the tag sits on %Lift external Lift's components, driven by our IC %Lift^ external Lift → its own IC (it runs its interaction) %Lift@Panel external Lift's Panel component (by name), our IC %Lift@=Rail external Lift's component tagged =Rail, our IC %Lift@Panel^ ...that Panel's own IC %Lift^@Panel Lift's IC, targeting its Panel %Lights:On only the =Lights actors currently On (filter) # Conditions (no ^: reads never delegate) :Open this IC is in "Open" @Lock:Off my Lock component is Off @Bay:Docked my child Bay's state (the cross-IC read is handled for you) %Vault:Sealed the external Vault is Sealed %Lock@Bolt:Locked external Lock's Bolt component is Locked # Full interaction tags !Open @Door open my Door (parallel) !Raise %Lift^ tell the external Lift to raise itself >Unlock @Door ?@Key:Held offer "Unlock" only while @Key is Held !Toggle @Fan/Spin run "Toggle", show "Spin" in the UI !Open @Door ?@Lock:!Off open only if the lock is NOT Off !OpenY @Drawer2 ?@Drawer3:!Open open only if Drawer3 is neither open nor opening !Call @Lift ?@Doors:=Shut call the lift only once its doors have FULLY shut ``` ## Where the prompt appears: `$X,Y,Z` An interaction point sits on its component's **pivot**. A pivot is a setup convenience, not somewhere a player aims: a door built from a base-pivoted cube puts its prompt on the floor. Two things fix that, and you usually need neither. **Automatic.** With no offset token, the point aims at the component's **visual center** instead of its pivot. This is on by default (`bAutoCenterPoints` on the Interactive Component), so a base-pivoted door prompts at door height with no extra setup at all. Turn it off for a project that already places its pivots where the prompt should sit. **Explicit.** A `$X,Y,Z` token places the point yourself, and always wins over auto-centering: ``` !Toggle $0,0,90 the point sits 90cm above the component origin !Toggle $,,90 the same: an omitted axis is zero !Toggle $-5,0,40 negative values are fine ``` The offset is **local to the component**, not world space, so it follows the part as it moves. On a door, a world-space offset stays behind as the door swings. It is **rotated** by the component but never **scaled** by it. The numbers are always centimeters, so `$0,0,90` is 90cm no matter how the mesh is scaled. MovaCore's cubes are 1cm and scaled up to size: a 2m door is a cube at scale 200, and a scaled offset lands 180 meters away. ### Several points on one component A `$` token also **starts a new point**, so one mesh can offer more than one place to interact without an invisible child component per spot: ``` !Open $0,0,10 !Lock $0,0,90 ``` That is one component with two independent points: a handle low down and a lock higher up, each with its own prompt and its own action. The offset binds to the action it *follows*, which is how it reads. ### The same action in several places Repeat the offset with no new verb between and the action repeats with it: ``` !Toggle $0,0,90 $90,0,0 ``` One verb, two places to reach it, ex: either end of a push bar. A tag list with no `$` behaves as before, producing a single point. ## You rarely type this The IC Control Panel offers every token from a searchable `[...]` picker ({doc}`../guides/control-panel`) and validates each one with a color-coded chip: amber with a "did you mean" suggestion when a token resolves to nothing.