# lib-core.interaction Proximity-trigger + action-dispatch. Register triggers with `(target_x, target_y, range, action_name, callback)`; per-frame `update(actor_x, actor_y)` checks distance + `was_action_pressed` and fires the nearest matching trigger per `action_name`. **Version:** 0.1.0 **Lib-ID:** lib-core.interaction **Requires:** lib-core.input v>=0.4.0 **Tags:** interaction, trigger, proximity, dispatch ## Topology ```mermaid graph LR this["lib-core.interaction"] lib_core_input["lib-core.input"] this --> lib_core_input ``` ## API ### `interaction.register(target_x, target_y, range, action_name, callback)` **Syntax:** `interaction.register(target_x: number, target_y: number, range: number, action_name: string, callback: fun(info: table)) -> id` **Example:** ```lua local id = interaction.register(144, 144, 40, "interact", function(info) engine.print("you pressed interact at distance " .. info.distance) end) ``` **Description:** Registers a proximity-trigger. Range is scalar pixel-radius. When the action's press-edge fires inside range, the callback is called with `info = { actor_x, actor_y, target_x, target_y, distance, trigger_id, action_name }`. Returns an opaque id used for `unregister`. Loud `error(...)` on misuse (non-number coords, non-positive range, non-string action, non-function callback). ### `interaction.unregister(id)` **Syntax:** `interaction.unregister(id: id) -> void` **Description:** Removes a trigger. Silent no-op if `id` is unknown. ### `interaction.update(actor_x, actor_y)` **Syntax:** `interaction.update(actor_x: number, actor_y: number) -> void` **Example:** ```lua function update(ctx, dt) interaction.update(player_center_x, player_center_y) end ``` **Description:** Per-frame proximity-check + dispatch. Per `action_name`, only the NEAREST in-range trigger fires that frame; different `action_name`s are independent (all may fire same frame). ### `interaction.list()` **Syntax:** `interaction.list() -> {id: id, target_x: number, target_y: number, range: number, action_name: string}[]` **Description:** Returns an array of trigger-records. Debug + UI inspection. ### `interaction.trigger_count()` **Syntax:** `interaction.trigger_count() -> integer` **Description:** Returns the number of currently registered triggers. ## Conventions - Targets are points (not AABBs in v0.1.0). `range` is scalar pixel-radius. - Distance: `engine.spatial.distance(actor, target) <= range`. - Multi-match resolution: per `action_name`, only NEAREST in-range trigger fires per update. - Was-pressed semantics: `input.was_action_pressed` (single-frame edge). - Silent-accept for `action_name` that hasn't been bound with `lib-core.input` — trigger simply never fires. - Callback errors bubble up (no pcall-wrap in v0.1.0). ## Consumer pattern ```lua local interaction = require("lib-core.interaction") local input = require("lib-core.input") input.bind("interact", { "e" }) local sign = { x = 144, y = 144, text = "Hier steht: ..." } interaction.register(sign.x, sign.y, 40, "interact", function(info) engine.print(sign.text) end) function update(ctx, dt) interaction.update(player_center_x, player_center_y) end ``` ## CHANGELOG ### v0.1.0 (P.0) - Initial release: register/unregister + per-frame proximity + nearest-per-action dispatch. ## References - Spec v0.1.0 (P.0): `meta/docs/superpowers/specs/2026-05-10-p0-lib-interaction-design.md` - ADR-0001 (engine knows verbs, libs bring nouns) - ADR-0031 (pixel-convention: Y-down-positive) - ADR-0038 (API-Doc-Convention)