# lib-core.player_control Single-player movement lib. Top-left position + size + speed, reads input-direction via `lib-core.input`, applies 4-corner-AABB collision against `lib-core.maps.is_walkable`. All-or-nothing block (no wall-sliding in v0.1.0). **Version:** 0.1.0 **Lib-ID:** lib-core.player_control **Requires:** lib-core.maps v>=0.1.1, lib-core.input v>=0.4.0 **Tags:** player, movement, aabb, collision ## Topology ```mermaid graph LR this["lib-core.player_control"] lib_core_maps["lib-core.maps"] this --> lib_core_maps lib_core_input["lib-core.input"] this --> lib_core_input engine["engine.*"] this --> engine ``` ## API ### `player.set_position(x, y)` **Syntax:** `player.set_position(x: number, y: number) -> void` **Example:** ```lua player.set_position(244, 244) ``` **Description:** Sets the player's top-left position in pixel-coords. Lua `error(...)` on non-number args. ### `player.position()` **Syntax:** `player.position() -> {x: number, y: number}` **Description:** Returns the current top-left position. Compute center as `p.x + size.w/2`, `p.y + size.h/2`. ### `player.set_size(w, h)` **Syntax:** `player.set_size(w: number, h: number) -> void` **Description:** Sets the AABB bounding-box size in pixels. Default 24x24. ### `player.size()` **Syntax:** `player.size() -> {w: number, h: number}` **Description:** Returns current bounding-box size. ### `player.set_speed(s)` **Syntax:** `player.set_speed(s: number) -> void` **Description:** Sets move-speed in px/sec. Default 200. ### `player.speed()` **Syntax:** `player.speed() -> number` **Description:** Returns current speed. ### `player.bind_movement(left, right, up, down)` **Syntax:** `player.bind_movement(left: string, right: string, up: string, down: string) -> void` **Example:** ```lua input.bind("move_left", { "a", "left" }) input.bind("move_right", { "d", "right" }) input.bind("move_up", { "w", "up" }) input.bind("move_down", { "s", "down" }) player.bind_movement("move_left", "move_right", "move_up", "move_down") ``` **Description:** Registers 4 input-action names for the player's WASD-style direction. Until called, `update(dt)` is a silent no-op. ### `player.update(dt)` **Syntax:** `player.update(dt: number) -> void` **Example:** ```lua function update(ctx, dt) player.update(dt) end ``` **Description:** Reads bound actions, computes direction-vector via `lib-core.input.direction`, predicts next position, runs 4-corner-AABB walkability check against the current map, and mutates position only if all corners walkable (all-or-nothing block; no wall-sliding in v0.1.0). Silent no-op until `bind_movement` is called. ## Conventions - Position is top-left of the AABB (matches `engine.render.draw_rect` origin). - Center calculation lives in caller: `cx = position.x + size.w/2`. - All-or-nothing block on collision (no wall-sliding in v0.1.0). - Module owns rendering (lib has no `draw` fn). Module composes camera-follow. - Silent no-op for `update()` before `bind_movement` (debug-friendly). - Lua `error(...)` for setter misuse (caller-bug fast-fail). - Y-down-positive per ADR-0031. ## Consumer pattern ```lua local player = require("lib-core.player_control") local input = require("lib-core.input") local camera = require("lib-core.camera") input.bind("move_left", { "a", "left" }) input.bind("move_right", { "d", "right" }) input.bind("move_up", { "w", "up" }) input.bind("move_down", { "s", "down" }) player.set_position(244, 244) player.bind_movement("move_left", "move_right", "move_up", "move_down") function update(ctx, dt) player.update(dt) local p = player.position() local s = player.size() camera.set_target(p.x + s.w/2, p.y + s.h/2) end function render(ctx) local p = player.position() local s = player.size() engine.render.draw_rect(p.x, p.y, s.w, s.h, engine.render.rgb(200, 60, 80)) end ``` ## CHANGELOG ### v0.1.0 (P.0) - Initial release: position/size/speed + bind_movement + per-frame update with 4-corner-AABB collision. ## References - Spec v0.1.0 (P.0): `meta/docs/superpowers/specs/2026-05-09-p0-lib-player_control-design.md` - ADR-0001 (engine knows verbs, libs bring nouns) - ADR-0031 (pixel-convention: Y-down-positive) - ADR-0038 (API-Doc-Convention)