docs: migrate README to API-Doc-Convention (S2-cascade.libs)
Restructured per ADR-0038: Abstract + bold-list Badges + Topology H2 + H3-API-Subsections (one per public function) + Conventions/Consumer pattern/CHANGELOG/References. Topology auto-populated via Sporel.exe --lint --fix. Pre-commit hook installed via --install-hooks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
13
.githooks/pre-commit
Normal file
13
.githooks/pre-commit
Normal file
@@ -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
|
||||
196
README.md
196
README.md
@@ -1,40 +1,190 @@
|
||||
# lib-core.command — v0.1.0
|
||||
# lib-core.command
|
||||
|
||||
Move-Commands für registrierte Units. RTS-Style Click-to-Move (RMB → bewegt selected Units).
|
||||
Move-commands for registered units. RTS-style click-to-move: press a bound action (e.g., RMB) and selected units move to the world-coord under the cursor. Lib-driven auto-wire via action-binding + target-provider; per-frame movement via direction-normalize + step-vs-distance + arrive-snap.
|
||||
|
||||
## Quick Setup
|
||||
**Version:** 0.1.0
|
||||
**Lib-ID:** lib-core.command
|
||||
**Requires:** lib-core.input v>=0.4.0, lib-core.camera v>=0.3.0
|
||||
**Tags:** command, movement, rts, click-to-move
|
||||
|
||||
## Topology
|
||||
|
||||
<!-- topology:start (auto-generated; do not edit) -->
|
||||
```mermaid
|
||||
graph LR
|
||||
this["lib-core.command"]
|
||||
lib_core_input["lib-core.input"]
|
||||
this --> lib_core_input
|
||||
lib_core_camera["lib-core.camera"]
|
||||
this --> lib_core_camera
|
||||
engine["engine.*"]
|
||||
this --> engine
|
||||
```
|
||||
<!-- topology:end -->
|
||||
|
||||
## API
|
||||
|
||||
### `command.register(get_pos_fn, set_pos_fn, speed)`
|
||||
**Syntax:** `command.register(get_pos_fn: fun() -> {x: number, y: number}, set_pos_fn: fun(x: number, y: number), speed: number) -> handle`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
local command = require("lib-core.command")
|
||||
local input = require("lib-core.input")
|
||||
local selection = require("lib-core.selection") -- target-provider source
|
||||
|
||||
input.bind("rmb", { "mouse_right" })
|
||||
|
||||
command.bind_move_action("rmb")
|
||||
command.bind_target_provider(function() return selection.list() end)
|
||||
|
||||
-- Register each movable unit (read+write callbacks + speed):
|
||||
local handle = command.register(
|
||||
function() return { x = unit.x, y = unit.y } end,
|
||||
function(nx, ny) unit.x = nx; unit.y = ny end,
|
||||
100 -- px/s
|
||||
)
|
||||
```
|
||||
|
||||
-- per-frame:
|
||||
**Description:** Registers a movable unit. Caller supplies read-callback (returns `{x, y}`) + write-callback (applies new pos) + speed in pixels per second. Returns an opaque handle used for all subsequent calls.
|
||||
|
||||
### `command.unregister(handle)`
|
||||
**Syntax:** `command.unregister(handle: handle) -> void`
|
||||
|
||||
**Description:** Removes a unit from the registry. No-op if the handle is unknown. Cancels any active command on that handle.
|
||||
|
||||
### `command.count_registered()`
|
||||
**Syntax:** `command.count_registered() -> number`
|
||||
|
||||
**Description:** Returns the count of currently registered units. Debug-friendly.
|
||||
|
||||
### `command.set_speed(handle, n)`
|
||||
**Syntax:** `command.set_speed(handle: handle, n: number) -> void`
|
||||
|
||||
**Description:** Updates a unit's move-speed (px/s). Takes effect on the next `update(dt)` tick.
|
||||
|
||||
### `command.speed(handle)`
|
||||
**Syntax:** `command.speed(handle: handle) -> number`
|
||||
|
||||
**Description:** Returns current speed for the handle.
|
||||
|
||||
### `command.move_to(handles, target_x, target_y)`
|
||||
**Syntax:** `command.move_to(handles: handle[], target_x: number, target_y: number) -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
command.move_to({ h1, h2, h3 }, 512, 384)
|
||||
```
|
||||
|
||||
**Description:** Programmatic move-issue for a batch of handles. All listed units start moving toward `(target_x, target_y)` (world-coords). Replaces any active command per handle.
|
||||
|
||||
### `command.cancel(handle)`
|
||||
**Syntax:** `command.cancel(handle: handle) -> void`
|
||||
|
||||
**Description:** Cancels the active command for one handle. No-op if no command is active.
|
||||
|
||||
### `command.cancel_all()`
|
||||
**Syntax:** `command.cancel_all() -> void`
|
||||
|
||||
**Description:** Cancels active commands for all registered handles.
|
||||
|
||||
### `command.current(handle)`
|
||||
**Syntax:** `command.current(handle: handle) -> {type: string, target_x: number, target_y: number} | nil`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
local cmd = command.current(h)
|
||||
if cmd then print(cmd.type, cmd.target_x, cmd.target_y) end
|
||||
```
|
||||
|
||||
**Description:** Returns the active command for a handle, or `nil` if idle. v0.1.0 always has `type == "move"`.
|
||||
|
||||
### `command.is_moving(handle)`
|
||||
**Syntax:** `command.is_moving(handle: handle) -> bool`
|
||||
|
||||
**Description:** Convenience predicate. Equivalent to `command.current(h) ~= nil`.
|
||||
|
||||
### `command.bind_move_action(action_name)`
|
||||
**Syntax:** `command.bind_move_action(action_name: string) -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
input.bind("rmb", { "mouse_right" })
|
||||
command.bind_move_action("rmb")
|
||||
```
|
||||
|
||||
**Description:** Binds an input-action whose press-edge issues a move-command to all targets returned by the registered target-provider. Cursor world-coord is computed via `lib-core.camera.screen_to_world`.
|
||||
|
||||
### `command.bind_target_provider(fn)`
|
||||
**Syntax:** `command.bind_target_provider(fn: fun() -> handle[]) -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
local selection = require("lib-core.selection")
|
||||
command.bind_target_provider(function() return selection.list() end)
|
||||
```
|
||||
|
||||
**Description:** Registers a callback that returns the current target-handle-list whenever the move-action fires. Typical wiring: forward `lib-core.selection.list()`.
|
||||
|
||||
### `command.set_arrive_threshold(n)`
|
||||
**Syntax:** `command.set_arrive_threshold(n: number) -> void`
|
||||
|
||||
**Description:** Sets the arrival-distance in pixels. Once a unit is within `n` px of its target, the command completes (snap-to-target).
|
||||
|
||||
### `command.arrive_threshold()`
|
||||
**Syntax:** `command.arrive_threshold() -> number`
|
||||
|
||||
**Description:** Returns the current arrive-threshold.
|
||||
|
||||
### `command.set_enabled(b)`
|
||||
**Syntax:** `command.set_enabled(b: bool) -> void`
|
||||
|
||||
**Description:** Master enable/disable. When disabled, `update(dt)` is a no-op and the bound action is ignored.
|
||||
|
||||
### `command.enabled()`
|
||||
**Syntax:** `command.enabled() -> bool`
|
||||
|
||||
**Description:** Returns master-enabled state.
|
||||
|
||||
### `command.update(dt)`
|
||||
**Syntax:** `command.update(dt: number) -> void`
|
||||
|
||||
**Example:**
|
||||
```lua
|
||||
function update(ctx, dt)
|
||||
command.update(dt)
|
||||
end
|
||||
```
|
||||
|
||||
## API
|
||||
**Description:** Per-frame execution: for each active command, computes direction-vector + normalized step, applies `set_pos_fn(...)`. When step >= remaining distance, snaps to target and clears the command.
|
||||
|
||||
- Registry: `register(get_pos_fn, set_pos_fn, speed) → handle`, `unregister(h)`, `count_registered()`
|
||||
- Speed: `set_speed(h, n)` / `speed(h) → n`
|
||||
- Issue (programmatic): `move_to(handles, x, y)`, `cancel(h)`, `cancel_all()`
|
||||
- State-Query: `current(h) → {type, target_x, target_y}|nil`, `is_moving(h) → bool`
|
||||
- Bindings: `bind_move_action(name)`, `bind_target_provider(fn)`
|
||||
- Settings: `set_arrive_threshold(n)` / `arrive_threshold()`, `set_enabled(b)` / `enabled()`
|
||||
- Per-Frame: `update(dt)`
|
||||
## Conventions
|
||||
|
||||
See `meta/docs/superpowers/specs/2026-05-14-p3-7-lib-command-design.md` for full design.
|
||||
- World-coords + pixel-units throughout (per ADR-0031).
|
||||
- Move-only in v0.1.0; context-sensitive dispatch (attack/use/etc.) deferred.
|
||||
- Issuing a new move on an active handle replaces the old target (no queueing).
|
||||
- `register` / `unregister` are O(1) amortized; handles are opaque integers.
|
||||
|
||||
## Consumer pattern
|
||||
|
||||
```lua
|
||||
local input = require("lib-core.input")
|
||||
local selection = require("lib-core.selection")
|
||||
local command = require("lib-core.command")
|
||||
|
||||
input.bind("rmb", { "mouse_right" })
|
||||
command.bind_move_action("rmb")
|
||||
command.bind_target_provider(function() return selection.list() end)
|
||||
|
||||
local handle = command.register(
|
||||
function() return { x = unit.x, y = unit.y } end,
|
||||
function(nx, ny) unit.x = nx; unit.y = ny end,
|
||||
100
|
||||
)
|
||||
|
||||
function update(ctx, dt)
|
||||
command.update(dt)
|
||||
end
|
||||
```
|
||||
|
||||
## CHANGELOG
|
||||
|
||||
### v0.1.0 (P.3.7)
|
||||
- Initial release: move-only commands + handle-registry + auto-wire (action + target-provider) + per-frame execution.
|
||||
|
||||
## References
|
||||
|
||||
- Spec v0.1.0 (P.3.7): `meta/docs/superpowers/specs/2026-05-14-p3-7-lib-command-design.md`
|
||||
- ADR-0001 (engine knows verbs, libs bring nouns)
|
||||
- ADR-0031 (pixel-convention: Y-down-positive)
|
||||
- ADR-0038 (API-Doc-Convention)
|
||||
|
||||
3
scripts/install-hooks.sh
Normal file
3
scripts/install-hooks.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
git config core.hooksPath .githooks
|
||||
echo "Hooks activated."
|
||||
Reference in New Issue
Block a user