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