diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100644 index 0000000..1ebdad0 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,13 @@ +#!/bin/sh +# Sporel API-Doc-Convention pre-commit hook. +SPOREL_EXE="${SPOREL_EXE:-$(command -v Sporel.exe 2>/dev/null || command -v sporel 2>/dev/null)}" +if [ -z "$SPOREL_EXE" ]; then + echo "INFO: Sporel.exe not on PATH. Skipping lint." >&2 + exit 0 +fi +"$SPOREL_EXE" --lint="$(pwd)" --fix +RC=$? +if git diff --cached --name-only | grep -qx 'README.md'; then + git add README.md +fi +exit $RC diff --git a/README.md b/README.md index 0c514bf..8ecfa48 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,80 @@ # lib-core.interaction -P.0 proximity-trigger + action-dispatch lib. 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`. +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`. -- Lib-ID: `lib-core.interaction` -- Version: `0.1.0` -- Spec: `meta/docs/superpowers/specs/2026-05-10-p0-lib-interaction-design.md` +**Version:** 0.1.0 +**Lib-ID:** lib-core.interaction +**Requires:** lib-core.input v>=0.4.0 +**Tags:** interaction, trigger, proximity, dispatch -Forward-compat stubs (DEPRECATED-MVP) for AABB-targets, action-effect- -pipeline integration, visual-feedback / moodlet, action-cycling, -hold-vs-press, per-action-resolution-override, callback pcall-wrap, -target-entity-binding, range-shape variants. +## 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) → id` -- `interaction.unregister(id)` — silent no-op if id unknown -- `interaction.update(actor_x, actor_y)` — proximity-check + dispatch -- `interaction.list() → array of { id, target_x, target_y, range, action_name }` -- `interaction.trigger_count() → integer` + +### `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 P.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. Different `action_name`s are independent (all may fire - the same frame). + +- 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). -- Callback receives `info = { actor_x, actor_y, target_x, target_y, - distance, trigger_id, action_name }`. Closures may ignore the arg - (`function() ... end` is legal). -- Loud Lua-errors on API misuse (non-number coords, non-positive range, - non-string action_name, non-function callback). -- Silent-accept for `action_name` that hasn't been registered with - `lib-core.input` — the trigger simply never fires (matches input-lib's - silent-false convention). -- Callback errors bubble up (no pcall-wrap in P.0). Resilience is - E.9 refactor target. +- 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", { engine.input.KEY_E }) +input.bind("interact", { "e" }) local sign = { x = 144, y = 144, text = "Hier steht: ..." } interaction.register(sign.x, sign.y, 40, "interact", function(info) @@ -52,7 +82,18 @@ interaction.register(sign.x, sign.y, 40, "interact", function(info) end) function update(ctx, dt) - -- (after player.update; reads player center) 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) diff --git a/scripts/install-hooks.sh b/scripts/install-hooks.sh new file mode 100644 index 0000000..2ff6e41 --- /dev/null +++ b/scripts/install-hooks.sh @@ -0,0 +1,3 @@ +#!/bin/sh +git config core.hooksPath .githooks +echo "Hooks activated."