1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-06-05 08:41:32 +00:00

Simplify inputBinding renderer, allow binding controller buttons

This commit is contained in:
uramer 2024-02-11 16:27:54 +01:00
parent c68dee214e
commit f4fed4ca5f
3 changed files with 166 additions and 63 deletions

View file

@ -143,10 +143,9 @@ Table with the following fields:
* - name * - name
- type (default) - type (default)
- description - description
* - type
- 'keyboardPress', 'keyboardHold'
- The type of input that's allowed to be bound
* - key * - key
- #string - #string
- Key of the action or trigger to which the input is bound - Key of the action or trigger to which the input is bound
* - type
- 'action', 'trigger'
- Type of the key

View file

@ -1,11 +1,7 @@
local core = require('openmw.core')
local input = require('openmw.input') local input = require('openmw.input')
local util = require('openmw.util') local util = require('openmw.util')
local async = require('openmw.async') local async = require('openmw.async')
local storage = require('openmw.storage') local storage = require('openmw.storage')
local ui = require('openmw.ui')
local I = require('openmw.interfaces')
local actionPressHandlers = {} local actionPressHandlers = {}
local function onActionPress(id, handler) local function onActionPress(id, handler)
@ -89,48 +85,87 @@ end
local bindingSection = storage.playerSection('OMWInputBindings') local bindingSection = storage.playerSection('OMWInputBindings')
local keyboardPresses = {} local devices = {
local keybordHolds = {} keyboard = true,
local boundActions = {} mouse = true,
controller = true
}
local function bindAction(action) local function invalidBinding(binding)
if boundActions[action] then return end if not binding.key then
boundActions[action] = true return 'has no key'
input.bindAction(action, async:callback(function() elseif binding.type ~= 'action' and binding.type ~= 'trigger' then
if keybordHolds[action] then return string.format('has invalid type', binding.type)
for _, binding in pairs(keybordHolds[action]) do elseif binding.type == 'action' and not input.actions[binding.key] then
if input.isKeyPressed(binding.code) then return true end return string.format("action %s doesn't exist", binding.key)
elseif binding.type == 'trigger' and not input.triggers[binding.key] then
return string.format("trigger %s doesn't exist", binding.key)
elseif not binding.device or not devices[binding.device] then
return string.format("invalid device %s", binding.device)
elseif not binding.button then
return 'has no button'
end
end
local boundActions = {}
local actionBindings = {}
local function bindAction(binding, id)
local action = binding.key
actionBindings[action] = actionBindings[action] or {}
actionBindings[action][id] = binding
if not boundActions[action] then
boundActions[binding.key] = true
input.bindAction(action, async:callback(function()
for _, binding in pairs(actionBindings[action] or {}) do
if binding.device == 'keyboard' then
if input.isKeyPressed(binding.button) then
return true
end
elseif binding.device == 'mouse' then
if input.isMouseButtonPressed(binding.button) then
return true
end
elseif binding.device == 'controller' then
if input.isControllerButtonPressed(binding.button) then
return true
end
end
end end
end return false
return false end), {})
end), {}) end
end
local triggerBindings = {}
for device in pairs(devices) do triggerBindings[device] = {} end
local function bindTrigger(binding, id)
local deviceBindings = triggerBindings[binding.device]
deviceBindings[binding.button] = deviceBindings[binding.button] or {}
deviceBindings[binding.button][id] = binding
end end
local function registerBinding(binding, id) local function registerBinding(binding, id)
if not input.actions[binding.key] and not input.triggers[binding.key] then local invalid = invalidBinding(binding)
print(string.format('Skipping binding for unknown action or trigger: "%s"', binding.key)) if invalid then
return print(string.format('Skipping invalid binding %s: %s', id, invalid))
end elseif binding.type == 'action' then
if binding.type == 'keyboardPress' then bindAction(binding, id)
local bindings = keyboardPresses[binding.code] or {} elseif binding.type == 'trigger' then
bindings[id] = binding bindTrigger(binding, id)
keyboardPresses[binding.code] = bindings
elseif binding.type == 'keyboardHold' then
local bindings = keybordHolds[binding.key] or {}
bindings[id] = binding
keybordHolds[binding.key] = bindings
bindAction(binding.key)
else
error('Unknown binding type "' .. binding.type .. '"')
end end
end end
function clearBinding(id) function clearBinding(id)
for _, boundTriggers in pairs(keyboardPresses) do for _, deviceBindings in pairs(triggerBindings) do
boundTriggers[id] = nil for _, buttonBindings in pairs(deviceBindings) do
buttonBindings[id] = nil
end
end end
for _, boundKeys in pairs(keybordHolds) do
boundKeys[id] = nil for _, bindings in pairs(actionBindings) do
bindings[id] = nil
end end
end end
@ -170,11 +205,24 @@ return {
end end
end, end,
onKeyPress = function(e) onKeyPress = function(e)
local bindings = keyboardPresses[e.code] local buttonTriggers = triggerBindings.keyboard[e.code]
if bindings then if not buttonTriggers then return end
for _, binding in pairs(bindings) do for _, binding in pairs(buttonTriggers) do
input.activateTrigger(binding.key) input.activateTrigger(binding.key)
end end
end,
onMouseButtonPress = function(button)
local buttonTriggers = triggerBindings.mouse[button]
if not buttonTriggers then return end
for _, binding in pairs(buttonTriggers) do
input.activateTrigger(binding.key)
end
end,
onControllerButtonPress = function(id)
local buttonTriggers = triggerBindings.controller[id]
if not buttonTriggers then return end
for _, binding in pairs(buttonTriggers) do
input.activateTrigger(binding.key)
end end
end, end,
} }

View file

@ -44,14 +44,63 @@ local bindingSection = storage.playerSection('OMWInputBindings')
local recording = nil local recording = nil
local mouseButtonNames = {
[1] = 'Left',
[2] = 'Middle',
[3] = 'Right',
[4] = '4',
[5] = '5',
}
-- TODO: support different controllers, use icons to render controller buttons
local controllerButtonNames = {
[-1] = 'Invalid',
[input.CONTROLLER_BUTTON.A] = "A",
[input.CONTROLLER_BUTTON.B] = "B",
[input.CONTROLLER_BUTTON.X] = "X",
[input.CONTROLLER_BUTTON.Y] = "Y",
[input.CONTROLLER_BUTTON.Back] = "Back",
[input.CONTROLLER_BUTTON.Guide] = "Guide",
[input.CONTROLLER_BUTTON.Start] = "Start",
[input.CONTROLLER_BUTTON.LeftStick] = "Left Stick",
[input.CONTROLLER_BUTTON.RightStick] = "Right Stick",
[input.CONTROLLER_BUTTON.LeftShoulder] = "LB",
[input.CONTROLLER_BUTTON.RightShoulder] = "RB",
[input.CONTROLLER_BUTTON.DPadUp] = "D-pad Up",
[input.CONTROLLER_BUTTON.DPadDown] = "D-pad Down",
[input.CONTROLLER_BUTTON.DPadLeft] = "D-pad Left",
[input.CONTROLLER_BUTTON.DPadRight] = "D-pad Right",
}
local function bindingLabel(recording, binding)
if recording then
return interfaceL10n('N/A')
elseif not binding or not binding.button then
return interfaceL10n('None')
elseif binding.device == 'keyboard' then
return input.getKeyName(binding.button)
elseif binding.device == 'mouse' then
return string.format('Mouse %s', mouseButtonNames[binding.button] or 'Unknown')
elseif binding.device == 'controller' then
return string.format('Controller %s', controllerButtonNames[binding.button] or 'Unknown')
else
return 'Unknown'
end
end
local inputTypes = {
action = input.actions,
trigger = input.triggers,
}
I.Settings.registerRenderer('inputBinding', function(id, set, arg) I.Settings.registerRenderer('inputBinding', function(id, set, arg)
if type(id) ~= 'string' then error('inputBinding: must have a string default value') end if type(id) ~= 'string' then error('inputBinding: must have a string default value') end
if not arg then error('inputBinding: argument with "key" and "type" is required') end if not arg then error('inputBinding: argument with "key" and "type" is required') end
if not arg.type then error('inputBinding: type argument is required') end if not arg.type then error('inputBinding: type argument is required') end
if not inputTypes[arg.type] then error('inputBinding: type must be "action" or "trigger"') end
if not arg.key then error('inputBinding: key argument is required') end if not arg.key then error('inputBinding: key argument is required') end
local info = input.actions[arg.key] or input.triggers[arg.key] local info = inputTypes[arg.type][arg.key]
if not info then return {} end if not info then print(string.format('inputBinding: %s %s not found', arg.type, arg.key)) return end
local l10n = core.l10n(info.key) local l10n = core.l10n(info.key)
@ -70,9 +119,7 @@ I.Settings.registerRenderer('inputBinding', function(id, set, arg)
} }
local binding = bindingSection:get(id) local binding = bindingSection:get(id)
local label = interfaceL10n('None') local label = bindingLabel(recording and recording.id == id, binding)
if binding then label = input.getKeyName(binding.code) end
if recording and recording.id == id then label = interfaceL10n('N/A') end
local recorder = { local recorder = {
template = I.MWUI.templates.textNormal, template = I.MWUI.templates.textNormal,
@ -115,22 +162,31 @@ I.Settings.registerRenderer('inputBinding', function(id, set, arg)
return column return column
end) end)
local function bindButton(device, button)
if recording == nil then return end
local binding = {
device = device,
button = button,
type = recording.arg.type,
key = recording.arg.key,
}
bindingSection:set(recording.id, binding)
local refresh = recording.refresh
recording = nil
refresh()
end
return { return {
engineHandlers = { engineHandlers = {
onKeyPress = function(key) onKeyPress = function(key)
if recording == nil then return end bindButton(key.code ~= input.KEY.Escape and 'keyboard' or nil, key.code)
local binding = { end,
code = key.code, -- TODO: currently never triggers, because mouse events are disabled while inside settings
type = recording.arg.type, onMouseButtonPress = function(button)
key = recording.arg.key, bindButton('mouse', button)
} end,
if key.code == input.KEY.Escape then -- TODO: prevent settings modal from closing onControllerButtonPress = function(id)
binding.code = nil bindButton('controller', id)
end
bindingSection:set(recording.id, binding)
local refresh = recording.refresh
recording = nil
refresh()
end, end,
} }
} }