Skip to content

Shift

Shift is a compiled finite-state machine (FSM / HSM). A .shift graph becomes u8 state ids in out/. Invalid transitions are rejected by CanEnter / ChangeState; hierarchy walks ancestor enter/leave chains. Not Hive (ECS).

Header: #include <clpp/libs/shift.clh> or generated Npc.clh. Runtime: CluauppLibs.Shift. Hot path: one-byte WriteId / ReadId buffers for Flare — not JSON.

  • One byte on the wire. Replicate state with WriteId; apply on clients with ReadId (skips guards — server owns truth).
  • Hierarchy. state Chase parent CombatOnEnter / OnLeave run along the ancestor chain.
  • Side data on the machine via GetData / ChangeData without stringly OnDataChanged every frame.
opt name = Npc
state Idle
state Combat
state Chase parent Combat
Idle -> Chase when SeePlayer
Chase -> Idle when Lost
#include "Npc.clh"
void OnChase() {
post("chase");
}
void init() {
ShiftMachine brain = Npc.New();
brain.Guard("SeePlayer", CanSeePlayer);
brain.OnEnter("Chase", OnChase);
brain.ChangeState("Chase");
buffer id = brain.WriteId();
}

Pair Hive for component columns; Shift for combat / AI mode.

Returns: ShiftMachine (generated Npc.New() passes compiled ids, transitions, and parents).

When: Create one machine per NPC, weapon mode, or UI flow that needs compiled states.

ShiftMachine brain = Shift.New();

Returns: nothing (no-op if destroyed, unknown state, same state, or CanEnter is false).

When: Authoritative transition on the server (or local-only UI states). Runs leave/enter callbacks along the hierarchy.

brain.ChangeState("Idle");

Returns: string — current state name.

When: Logging, UI labels, or guards in gameplay code.

string s = brain.GetState();

Returns: string — alias of GetState.

When: Same as GetState when you prefer explicit naming.

string s = brain.GetCurrentState();

Returns: string — state name before the last successful transition.

When: “Return to last mode” or animation that depends on where you came from.

string prev = brain.GetPreviousState();

Returns: int — current state id (u8 value as number).

When: Compare states cheaply or feed non-Shift buffers.

int id = brain.GetId();

Returns: nothing.

When: Register a named guard referenced by when GuardName edges in the .shift file.

brain.Guard("SeePlayer", CanSeePlayer);

Returns: nothing (if already in that state when registered, enter runs immediately).

When: Start animations, sounds, or Hive queries when a state becomes active.

brain.OnEnter("Chase", OnChase);

Returns: nothing.

When: Tear down chase VFX or reset timers when leaving a state.

brain.OnLeave("Chase", OnLeaveChase);

Returns: bool — whether ChangeState would succeed from the current state (explicit edges + guards + parent descent).

When: UI affordances, AI telegraphs, or client prediction checks before calling ChangeState.

if (brain.CanEnter("Chase")) {
brain.ChangeState("Chase");
}

Returns: buffer — 1 byte: current state id.

When: Pack into Flare packets or any replication that should stay JSON-free.

buffer id = brain.WriteId();

Returns: nothing.

When: Apply replicated state on a client or secondary machine without re-running guards (visual sync).

brain.ReadId(replicatedBuf);

Returns: auto — mutable string-keyed table on the machine.

When: Per-machine blackboard (target id, timer) that is not worth a Hive component.

auto data = brain.GetData();

Returns: nothing.

When: Set one key on the machine data table.

brain.ChangeData("Target", player);

Returns: nothing.

When: NPC despawn — clears data and callbacks; further transitions are ignored.

brain.Destroy();