# lib-core.actor Thin wrapper over `lib-core.composition` that enforces a baseline-speed contract for "actor" entities. Provides convenience helpers for position-read + position-write (inert-mediated). Doesn't track any ephemeral movement-state (vel/dir) — that stays in `player_control` or whatever drives the actor each frame. **Version:** 0.1.0 **Lib-ID:** lib-core.actor **Requires:** lib-core.composition v>=0.1.0 **Tags:** actor, entity, movement ## Topology ```mermaid graph LR this["lib-core.actor"] lib_core_composition["lib-core.composition"] this --> lib_core_composition engine["engine.*"] this --> engine ``` ## Scope (v0.1.0 — Spine) Re-Entry from P.0-Actor-Deferral (Trigger 1: Composition-Aktivierung). v0.1 is a minimal slice; intentional non-goals: - **No Body-Slot-Aggregator.** Phase D / J trigger. - **No Walk-Capability** (per ADR-0002 capabilities-as-derived). Waits for a second actor-consumer that needs capability-routing. - **No Movement-State on the entity.** vel / dir / direction-flags are ephemeral; kept in `player_control` (or whatever driver). Persisted ones come with a re-entry-trigger. - **No Action-mediated move.** v0.1 uses engine-§11 inert-write directly. Action-routing is the "right" path for game-state-weighty mutations but waits until a "move" Action is registered and event/effect routing matters. ## API ### `actor.create(spec)` **Syntax:** `actor.create({template: string, properties: table}) -> entity` **Example:** ```lua -- Caller defines a template via composition first: composition.define_template{ id = "player", properties = { position = {x = 0, y = 0}, movement_speed = 200, sprite_color = engine.render.rgb(200, 60, 80), sprite_w = 24, sprite_h = 24, }, tags = {"renderable", "player"}, } -- Then actor.create instantiates: local p = actor.create{ template = "player", properties = { position = {x = 400, y = 300}, movement_speed = 200, }, } ``` **Description:** Validates that `properties.movement_speed` is a positive number, then delegates to `composition.create`. Loud-Error if `movement_speed` is missing, non-number, or non-positive. The caller is responsible for `composition.define_template{id=...}` before invoking. Actor doesn't auto-define templates. ### `actor.position(handle)` **Syntax:** `actor.position(handle) -> x, y` **Description:** Returns the actor's current position as two numbers (x, y). Reads via `entity:get_property("position.x"/"position.y")`. Loud-Error if `handle` is nil. ### `actor.move(handle, dx, dy)` **Syntax:** `actor.move(handle, dx: number, dy: number) -> void` **Example:** ```lua -- Typical caller (player_control update-loop): local speed = actor.movement_speed(player) local move_dx, move_dy = compute_input_direction() actor.move(player, move_dx * speed * dt, move_dy * speed * dt) ``` **Description:** Adds (dx, dy) to the actor's current position via direct inert-write. Caller does dt-scaling and any speed-multiplication upstream. v0.1 uses engine §11 inert-write — Action-routing is a future re-entry trigger (see Scope above). ### `actor.movement_speed(handle)` **Syntax:** `actor.movement_speed(handle) -> number` **Description:** Convenience read of the actor's `movement_speed` property (in pixels/sec by convention; caller decides the unit). ## Conventions - **Position-Format**: composite-table at authoring-time (`properties = { position = {x, y} }`), flattened by composition to scalar properties `position.x` and `position.y`. See Phase-A Spec §5 A-Q5 revision (2026-06-09). - **movement_speed**: pixels per second. dt-scaling applied at the caller, NOT by `actor.move`. ## References - Spec: `meta/docs/superpowers/specs/2026-06-09-phase-A-inactive-entities-composition-actor-reentry-design.md` - Origin: `meta/docs/superpowers/specs/2026-05-10-p0-actor-deferral.md` (Trigger 1: Composition-Aktivierung) - Architecture: `meta/docs/architecture/composition-model.md` (Actor as Item-with-Body-Slot in Phase D; v0.1 is composition-only wrapper) - ADR-0001 (engine knows verbs, libs bring nouns) - ADR-0002 (capabilities as derived properties — Walk-Capability deferred) - ADR-0010 (read/write asymmetry — v0.1 inert-write escape hatch) - ADR-0038 (API-Doc-Convention)