World Operator & Game Engine Context
When to Use This Skill
Activate this skill when working on:
- The WorldOperator (
frontend/src/editor/utils/world-operator.ts)
- Stage helpers (
frontend/src/editor/utils/stage-helpers.ts)
- Core domain types (
frontend/src/types.ts)
- Rule evaluation, conditions, or actions
- Actor/character behavior
- World state mutations or history/undo
- The recording flow (programming by demonstration)
- Animation frames or debug data
Key Files
| File |
Purpose |
frontend/src/types.ts |
All domain model types |
frontend/src/editor/utils/world-operator.ts |
Simulation engine |
frontend/src/editor/utils/stage-helpers.ts |
Position, transform, and comparison helpers |
frontend/src/editor/utils/frame-accumulator.ts |
Animation frame tracking |
frontend/src/editor/utils/world-constants.ts |
FLOWBEHAVIORS and CONTAINERTYPES |
Core Concepts
Domain Model Hierarchy
Character (template)
├── rules: RuleTreeItem[] # Behavior tree
├── spritesheet.appearances # Sprites
└── variables # Variable definitions
Actor (instance of Character)
├── characterId # Reference to template
├── position: {x, y} # Grid coordinates
├── appearance # Current sprite ID
├── variableValues # Instance variable overrides
└── transform # Rotation/flip
Stage (2D grid)
├── actors: {[id]: Actor}
├── width, height
└── wrapX, wrapY # Edge wrapping
World (complete state)
├── stages: {[id]: Stage}
├── globals # Global variables
├── input: {keys, clicks} # Current frame input
├── evaluatedRuleDetails # Debug: detailed rule evaluation results
├── evaluatedTickFrames # Debug: animation frames
└── history: HistoryItem[] # Undo stack (max 20)
Rule Structure
A Rule defines a before→after pattern transformation:
Rule = {
mainActorId: string // "Owner" actor, always at (0,0) in rule space
actors: {[id]: Actor} // "Before" pattern - positions relative to mainActor
conditions: RuleCondition[] // Additional constraints
actions: RuleAction[] // Transformations to apply
extent: RuleExtent // Bounding box {xmin, xmax, ymin, ymax, ignored}
}
Rule Tree (Control Flow)
Rules are organized hierarchically:
- RuleTreeEventItem (
group-event): Filters by input
- event: "key" with code - Specific key press - event: "click" - Actor was clicked - event: "idle" - Always fires
- RuleTreeFlowItem (
group-flow): Controls iteration
- behavior: "first" - Stop after first match - behavior: "all" - Execute all matches, then hand control back to the parent container so the rules after this group still get a turn ("Do All & Continue") - behavior: "random" - Shuffle, then first match - behavior: "loop" - Repeat N times
Conditions and Actions
RuleCondition: Comparison between two RuleValues
{ left: RuleValue, comparator: VariableComparator, right: RuleValue, enabled: boolean }
RuleValue variants:
{ constant: string } - Literal value
{ actorId, variableId } - Actor's variable/appearance/transform
{ globalId } - Global variable
RuleAction types:
move - Change position (delta or offset)
appearance - Change sprite
transform - Rotate/flip
variable - Modify actor variable (add/set/subtract)
delete - Remove actor
create - Spawn new actor
global - Modify global variable
WorldOperator API
WorldOperator(previousWorld, characters, characterZOrder) → {
tick(), // Advance simulation one step
untick(), // Revert to previous state
resetForRule() // Set up for rule preview
}
tick() Flow
- Snapshot previous state to history
- Clone globals and actors for mutation
- Update special globals (keypress, click)
- Evaluate each actor's rules via
ActorOperator, top-most character
first (sortActorIdsByTickOrder: descending characterZOrder, then actor id), so execution order follows the layering the user sees on the stage. Each actor is visited exactly once: an actor blocked by a neighbour that has not moved yet stays put until the next tick, and when two actors want the same square the one layered on top gets it
- Return new immutable world state
Pattern Matching (checkRuleScenario)
For each grid cell in rule.extent:
- Find stage actors at that position (wrapping-aware)
- Find rule actors covering that position
- Unless "ignored", actor counts must match
- Stage actors must match rule actors (same character + conditions)
- Verify all referenced actors found
- Validate action offsets are valid positions
Returns {ruleActorId → stageActor} mapping, or false.
Debug Data
evaluatedRuleDetails
{ [actorId]: { [ruleId]: EvaluatedRuleDetails } }
Tracks detailed evaluation results for each rule per actor. Includes:
- Overall pass/fail status
- Per-square matching results (with failure reasons)
- Per-condition evaluation results (with resolved values)
- Actor mappings (which stage actor matched which rule actor)
Used by inspector to show rule state circles and condition status dots. See granular-rule-tracking.md for full details.
evaluatedTickFrames
Frame[] where Frame = { actors: {[id]: FrameActor}, id: number }
FrameActor = Actor & { deleted?, actionIdx?, animationStyle? }
Animation frames within a tick. Stage container animates through these.
Important Patterns
- Immutable Updates: Uses
updeep (imported as u) for state updates
- Relative Positioning: Rule positions are relative to mainActor at (0,0)
- Deep Clone: Always clone before mutating (
deepClone from utils)
- Wrapping: Use
wrappedPosition() for stage edge handling
- History: Only saved when at least one rule fires
See Also
architecture-reference.md - Detailed architecture diagrams, data flow, and code examples
granular-rule-tracking.md - Documentation for the granular rule evaluation tracking system (square/condition-level feedback)