diff --git a/init.lua b/init.lua index cd0bed4..5e27307 100644 --- a/init.lua +++ b/init.lua @@ -1,56 +1,78 @@ -- ===================================================================== --- lib-core.input — Action-Mapping + Direction-Vector (v0.2.0) --- See: meta/docs/superpowers/specs/2026-05-11-engine-input-symbolic-keys-design.md +-- lib-core.input — Action-Mapping + Direction-Vector (v0.3.0) +-- See: meta/docs/superpowers/specs/2026-05-12-p3-3-mouse-input-design.md +-- +-- v0.3.0 (P.3.3 2026-05-12): bind() now accepts unified key+mouse +-- strings via BIND_MAP with kind-tagged entries ({kind="key"|"mouse", +-- code=}). Mixed bindings supported: e.g. +-- input.bind("interact", {"e", "mouse_left"}) +-- Backward-compatible with v0.2.0 callsites — existing key-only binds +-- work unchanged. -- -- v0.2.0 (P.2.6 2026-05-11): bind() accepts string-key-names only. --- Integer engine.input.KEY_* args are rejected (modules go through --- string-API; KEY_* stays as low-level engine-primitive). +-- Integer engine.input.KEY_* args are rejected. -- --- Scope: action-mapping (string-key-name-arrays per action) + --- direction-vector helper. DEPRECATED-MVP for: mouse-button actions, --- gamepad bindings, action-context-stack, key-rebinding config, --- modifier-combos, analog-axis, was_action_released edge. +-- Scope v0.3.0: action-mapping (string-key+mouse-arrays per action) + +-- direction-vector helper. DEPRECATED-MVP: mouse-wheel, was_released +-- edge, mouse-delta helper, side-buttons, gamepad bindings, +-- action-context-stack, key-rebinding config. -- ===================================================================== --- string → engine.input.KEY_* mapping. Lowercase-only convention. --- Right-modifier-Aliases (rshift/rctrl/ralt) + function-keys + special-keys --- (delete/insert/home/end/pageup/pagedown) sind forthcoming, additiv wenn --- engine.input rechte-Modifier + F-keys + Special-Keys exposed. -local KEY_MAP = { - -- Letters (26) - a = engine.input.KEY_A, b = engine.input.KEY_B, c = engine.input.KEY_C, - d = engine.input.KEY_D, e = engine.input.KEY_E, f = engine.input.KEY_F, - g = engine.input.KEY_G, h = engine.input.KEY_H, i = engine.input.KEY_I, - j = engine.input.KEY_J, k = engine.input.KEY_K, l = engine.input.KEY_L, - m = engine.input.KEY_M, n = engine.input.KEY_N, o = engine.input.KEY_O, - p = engine.input.KEY_P, q = engine.input.KEY_Q, r = engine.input.KEY_R, - s = engine.input.KEY_S, t = engine.input.KEY_T, u = engine.input.KEY_U, - v = engine.input.KEY_V, w = engine.input.KEY_W, x = engine.input.KEY_X, - y = engine.input.KEY_Y, z = engine.input.KEY_Z, - -- Numbers (10) - ["0"] = engine.input.KEY_ZERO, ["1"] = engine.input.KEY_ONE, - ["2"] = engine.input.KEY_TWO, ["3"] = engine.input.KEY_THREE, - ["4"] = engine.input.KEY_FOUR, ["5"] = engine.input.KEY_FIVE, - ["6"] = engine.input.KEY_SIX, ["7"] = engine.input.KEY_SEVEN, - ["8"] = engine.input.KEY_EIGHT, ["9"] = engine.input.KEY_NINE, - -- Arrows (4) - left = engine.input.KEY_LEFT, right = engine.input.KEY_RIGHT, - up = engine.input.KEY_UP, down = engine.input.KEY_DOWN, - -- Whitespace + control (5) - space = engine.input.KEY_SPACE, - tab = engine.input.KEY_TAB, - enter = engine.input.KEY_ENTER, - backspace = engine.input.KEY_BACKSPACE, - escape = engine.input.KEY_ESCAPE, - -- Modifiers (left-side; aliases for ergonomics) - shift = engine.input.KEY_LEFT_SHIFT, lshift = engine.input.KEY_LEFT_SHIFT, - ctrl = engine.input.KEY_LEFT_CONTROL, lctrl = engine.input.KEY_LEFT_CONTROL, - alt = engine.input.KEY_LEFT_ALT, lalt = engine.input.KEY_LEFT_ALT, - -- Console-toggle (DE: ^, US: `) - ["^"] = engine.input.KEY_GRAVE, +-- Unified string → {kind, code} mapping. Lowercase convention. +-- "mouse_*" namespaces avoid collision with arrow-key "left"/"right". +local BIND_MAP = { + -- Letters (26) — kind="key" + a = {kind="key", code=engine.input.KEY_A}, b = {kind="key", code=engine.input.KEY_B}, + c = {kind="key", code=engine.input.KEY_C}, d = {kind="key", code=engine.input.KEY_D}, + e = {kind="key", code=engine.input.KEY_E}, f = {kind="key", code=engine.input.KEY_F}, + g = {kind="key", code=engine.input.KEY_G}, h = {kind="key", code=engine.input.KEY_H}, + i = {kind="key", code=engine.input.KEY_I}, j = {kind="key", code=engine.input.KEY_J}, + k = {kind="key", code=engine.input.KEY_K}, l = {kind="key", code=engine.input.KEY_L}, + m = {kind="key", code=engine.input.KEY_M}, n = {kind="key", code=engine.input.KEY_N}, + o = {kind="key", code=engine.input.KEY_O}, p = {kind="key", code=engine.input.KEY_P}, + q = {kind="key", code=engine.input.KEY_Q}, r = {kind="key", code=engine.input.KEY_R}, + s = {kind="key", code=engine.input.KEY_S}, t = {kind="key", code=engine.input.KEY_T}, + u = {kind="key", code=engine.input.KEY_U}, v = {kind="key", code=engine.input.KEY_V}, + w = {kind="key", code=engine.input.KEY_W}, x = {kind="key", code=engine.input.KEY_X}, + y = {kind="key", code=engine.input.KEY_Y}, z = {kind="key", code=engine.input.KEY_Z}, + -- Numbers (10) — kind="key" + ["0"] = {kind="key", code=engine.input.KEY_ZERO}, + ["1"] = {kind="key", code=engine.input.KEY_ONE}, + ["2"] = {kind="key", code=engine.input.KEY_TWO}, + ["3"] = {kind="key", code=engine.input.KEY_THREE}, + ["4"] = {kind="key", code=engine.input.KEY_FOUR}, + ["5"] = {kind="key", code=engine.input.KEY_FIVE}, + ["6"] = {kind="key", code=engine.input.KEY_SIX}, + ["7"] = {kind="key", code=engine.input.KEY_SEVEN}, + ["8"] = {kind="key", code=engine.input.KEY_EIGHT}, + ["9"] = {kind="key", code=engine.input.KEY_NINE}, + -- Arrows (4) — kind="key" + left = {kind="key", code=engine.input.KEY_LEFT}, + right = {kind="key", code=engine.input.KEY_RIGHT}, + up = {kind="key", code=engine.input.KEY_UP}, + down = {kind="key", code=engine.input.KEY_DOWN}, + -- Whitespace + control (5) — kind="key" + space = {kind="key", code=engine.input.KEY_SPACE}, + tab = {kind="key", code=engine.input.KEY_TAB}, + enter = {kind="key", code=engine.input.KEY_ENTER}, + backspace = {kind="key", code=engine.input.KEY_BACKSPACE}, + escape = {kind="key", code=engine.input.KEY_ESCAPE}, + -- Modifiers (left-side; aliases for ergonomics) — kind="key" + shift = {kind="key", code=engine.input.KEY_LEFT_SHIFT}, + lshift = {kind="key", code=engine.input.KEY_LEFT_SHIFT}, + ctrl = {kind="key", code=engine.input.KEY_LEFT_CONTROL}, + lctrl = {kind="key", code=engine.input.KEY_LEFT_CONTROL}, + alt = {kind="key", code=engine.input.KEY_LEFT_ALT}, + lalt = {kind="key", code=engine.input.KEY_LEFT_ALT}, + -- Console-toggle (DE: ^, US: `) — kind="key" + ["^"] = {kind="key", code=engine.input.KEY_GRAVE}, + -- Mouse buttons (3) — kind="mouse" (P.3.3) + mouse_left = {kind="mouse", code=engine.input.MOUSE_LEFT}, + mouse_right = {kind="mouse", code=engine.input.MOUSE_RIGHT}, + mouse_middle = {kind="mouse", code=engine.input.MOUSE_MIDDLE}, } -local bindings = {} -- action_name -> array of key-codes (post-resolution) +local bindings = {} -- action_name -> array of {kind, code} entries local M = {} @@ -62,20 +84,20 @@ function M.bind(action_name, keys) error(string.format("input.bind: keys must be a non-empty array of key-name strings (action '%s')", action_name)) end - -- Resolve each string-key to engine keycode via KEY_MAP. Atomic: if any - -- key invalid, the entire bind fails without partial-state mutation. + -- Resolve each string-key to {kind, code} entry via BIND_MAP. Atomic: + -- if any key invalid, the entire bind fails without partial-state mutation. local resolved = {} for i, k in ipairs(keys) do if type(k) ~= "string" then - error(string.format("input.bind: key #%d for action '%s' must be a lowercase string (got %s); use 'a'..'z', '0'..'9', 'left', 'space', 'ctrl', 'escape', etc. — see lib-core.input README for full table", + error(string.format("input.bind: key #%d for action '%s' must be a lowercase string (got %s); use 'a'..'z', 'left', 'space', 'mouse_left', etc. — see BIND_MAP in lib-core.input/init.lua", i, action_name, type(k))) end - local code = KEY_MAP[k] - if code == nil then - error(string.format("input.bind: unknown key-name '%s' for action '%s' — must be lowercase, see KEY_MAP in lib-core.input/init.lua for valid keys", + local entry = BIND_MAP[k] + if entry == nil then + error(string.format("input.bind: unknown key-name '%s' for action '%s' — must be lowercase key ('a'..'z', 'left', 'space', ...) or mouse ('mouse_left', 'mouse_right', 'mouse_middle')", k, action_name)) end - resolved[i] = code + resolved[i] = entry end bindings[action_name] = resolved end @@ -85,19 +107,21 @@ function M.unbind(action_name) end function M.is_action_down(action_name) - local keys = bindings[action_name] - if not keys then return false end - for _, k in ipairs(keys) do - if engine.input.is_key_down(k) then return true end + local entries = bindings[action_name] + if not entries then return false end + for _, e in ipairs(entries) do + if e.kind == "key" and engine.input.is_key_down(e.code) then return true end + if e.kind == "mouse" and engine.input.is_mouse_down(e.code) then return true end end return false end function M.was_action_pressed(action_name) - local keys = bindings[action_name] - if not keys then return false end - for _, k in ipairs(keys) do - if engine.input.was_pressed(k) then return true end + local entries = bindings[action_name] + if not entries then return false end + for _, e in ipairs(entries) do + if e.kind == "key" and engine.input.was_pressed(e.code) then return true end + if e.kind == "mouse" and engine.input.was_mouse_pressed(e.code) then return true end end return false end @@ -118,12 +142,18 @@ function M.action_count() return n end --- DEPRECATED-MVP: bind_mouse(action, button) -- mouse-button slice --- DEPRECATED-MVP: bind_gamepad(action, ...) -- gamepad slice --- DEPRECATED-MVP: push_context(name) / pop_context() -- action-context-stack --- DEPRECATED-MVP: load_bindings_from_config(path) -- key-rebinding slice --- DEPRECATED-MVP: was_action_released(action) -- lift-detection slice --- DEPRECATED-MVP: right-modifier-keys (rshift/rctrl/ralt) -- engine.input.KEY_RIGHT_* erforderlich --- DEPRECATED-MVP: function-keys (f1..f12) + special-keys (delete/insert/home/end/pageup/pagedown) -- engine.input-Erweiterung erforderlich +-- DEPRECATED-MVP (P.3.3-introduced): +-- mouse-wheel binding (wheel_up/wheel_down) — P.3-future-slice (post-P.3.4); continuous-vs-edge brainstorming needed +-- was_action_released(action) — lift-detection-edge (both key + mouse atomic) +-- mouse-delta helper (engine.input.get_mouse_delta) — P.3.5/6 drag-pan / drag-box +-- mouse-button side-buttons (mouse_4 / mouse_5) — additive extension +-- mouse-position lib-wrapper (input.mouse_pos() → {x,y}) — convenience layer +-- +-- DEPRECATED-MVP (existing from v0.2.0): +-- bind_gamepad(action, ...) — gamepad slice +-- push_context(name) / pop_context() — action-context-stack +-- load_bindings_from_config(path) — key-rebinding slice +-- right-modifier-keys (rshift/rctrl/ralt) — engine.input.KEY_RIGHT_* required +-- function-keys (f1..f12) + special-keys (delete/insert/home/end/pageup/pagedown) — engine.input extension required return M diff --git a/manifest.lib b/manifest.lib index a9f9190..f2cb25a 100644 --- a/manifest.lib +++ b/manifest.lib @@ -1 +1 @@ -{"id":"lib-core.input","version":"0.2.0","api_min":"0.1"} +{"id":"lib-core.input","version":"0.3.0","api_min":"0.1"}