602 lines
23 KiB
GDScript
602 lines
23 KiB
GDScript
extends Node
|
|
|
|
|
|
signal device_changed(device: String, device_index: int)
|
|
signal keyboard_input_changed(action: String, input: InputEvent)
|
|
signal joypad_input_changed(action: String, input: InputEvent)
|
|
signal joypad_changed(device_index: int, is_connected: bool)
|
|
|
|
|
|
const DEVICE_KEYBOARD = "keyboard"
|
|
const DEVICE_XBOX_CONTROLLER = "xbox"
|
|
const DEVICE_SWITCH_CONTROLLER = "switch"
|
|
const DEVICE_PLAYSTATION_CONTROLLER = "playstation"
|
|
const DEVICE_STEAMDECK_CONTROLLER = "steamdeck"
|
|
const DEVICE_GENERIC = "generic"
|
|
|
|
const SUB_DEVICE_XBOX_ONE_CONTROLLER = "xbox_one"
|
|
const SUB_DEVICE_XBOX_SERIES_CONTROLLER = "xbox_series"
|
|
|
|
const SUB_DEVICE_PLAYSTATION3_CONTROLLER = "playstation3"
|
|
const SUB_DEVICE_PLAYSTATION4_CONTROLLER = "playstation4"
|
|
const SUB_DEVICE_PLAYSTATION5_CONTROLLER = "playstation5"
|
|
|
|
const SUB_DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER = "switch_left_joycon"
|
|
const SUB_DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER = "switch_right_joycon"
|
|
|
|
const XBOX_BUTTON_LABELS = ["A", "B", "X", "Y", "Back", "Guide", "Start", "Left Stick", "Right Stick", "LB", "RB", "Up", "Down", "Left", "Right", "Share", "Paddle 1", "Paddle 2", "Paddle 3", "Paddle 4"]
|
|
const XBOX_ONE_BUTTON_LABELS = ["A", "B", "X", "Y", "View", "Guide", "Menu", "Left Stick", "Right Stick", "LB", "RB", "Up", "Down", "Left", "Right", "Share", "Paddle 1", "Paddle 2", "Paddle 3", "Paddle 4"]
|
|
const XBOX_SERIES_BUTTON_LABELS = ["A", "B", "X", "Y", "View", "Guide", "Menu", "Left Stick", "Right Stick", "LB", "RB", "Up", "Down", "Left", "Right", "Share", "Paddle 1", "Paddle 2", "Paddle 3", "Paddle 4"]
|
|
const STEAMDECK_BUTTON_LABELS = ["A", "B", "X", "Y", "View", "?", "Options", "Left Stick", "Right Stick", "L1", "R1", "Up", "Down", "Left", "Right", "", "", "", "", ""]
|
|
# Note: share and home buttons are not recognized
|
|
const SWITCH_BUTTON_LABELS = ["B", "A", "Y", "X", "Minus", "", "Plus", "Left Stick", "Right Stick", "LS", "RS", "Up", "Down", "Left", "Right", "Capture"]
|
|
# Mapping for left and right joypad connected together (extended gamepad)
|
|
# Left Stick is Axis 0 and 1
|
|
# Right Stick is Axis 2 and 3
|
|
# ZL and ZR are Axis 4 and 5
|
|
const SWITCH_EXTENDED_GAMEPAD_BUTTON_LABELS = ["B", "A", "Y", "X", "Minus", "", "Plus", "Left Stick", "Right Stick", "L", "R", "Up", "Down", "Left", "Right", "Capture"]
|
|
const PLAYSTATION_3_4_BUTTON_LABELS = ["Cross", "Circle", "Square", "Triangle", "Share", "PS", "Options", "L3", "R3", "L1", "R1", "Up", "Down", "Left", "Right", "Microphone", "", "", "", "", "Touchpad"]
|
|
# Note: Microphone does not work on PC / touchpad is not recognized
|
|
const PLAYSTATION_5_BUTTON_LABELS = ["Cross", "Circle", "Square", "Triangle", "Create", "PS", "Options", "L3", "R3", "L1", "R1", "Up", "Down", "Left", "Right", "Microphone", "", "", "", "", "Touchpad"]
|
|
|
|
const SERIAL_VERSION = 1
|
|
|
|
## The deadzone to ignore for joypad motion
|
|
var deadzone: float = 0.5
|
|
## The mouse distance to ignore before movement is assumed
|
|
var mouse_motion_threshold: int = 100
|
|
## The last known joypad device name (or "" if no joypad detected)
|
|
var last_known_joypad_device: String = get_simplified_device_name(Input.get_joy_name(0))
|
|
## The last known joypad index
|
|
var last_known_joypad_index: int = 0 if Input.get_connected_joypads().size() > 0 else -1
|
|
|
|
## Used internally
|
|
var device_last_changed_at: int = 0
|
|
var _last_known_granular_joypad_device: String = get_simplified_device_name(Input.get_joy_name(0), true)
|
|
|
|
@onready var device: String = guess_device_name()
|
|
@onready var device_index: int = 0 if has_joypad() else -1
|
|
|
|
|
|
func _ready() -> void:
|
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
|
|
|
if not Engine.has_singleton("InputHelper"):
|
|
Engine.register_singleton("InputHelper", self)
|
|
|
|
Input.joy_connection_changed.connect(func(device_index, is_connected): joypad_changed.emit(device_index, is_connected))
|
|
|
|
|
|
func _input(event: InputEvent) -> void:
|
|
var next_device: String = device
|
|
var next_device_index: int = device_index
|
|
|
|
# Did we just press a key on the keyboard or move the mouse?
|
|
if event is InputEventKey \
|
|
or event is InputEventMouseButton \
|
|
or (event is InputEventMouseMotion and (event as InputEventMouseMotion).relative.length_squared() > mouse_motion_threshold):
|
|
next_device = DEVICE_KEYBOARD
|
|
next_device_index = -1
|
|
|
|
# Did we just use a joypad?
|
|
elif event is InputEventJoypadButton \
|
|
or (event is InputEventJoypadMotion and abs(event.axis_value) > deadzone):
|
|
next_device = get_simplified_device_name(get_joy_name(event.device))
|
|
last_known_joypad_device = next_device
|
|
next_device_index = event.device
|
|
last_known_joypad_index = next_device_index
|
|
|
|
_last_known_granular_joypad_device = get_simplified_device_name(get_joy_name(event.device), true)
|
|
|
|
# Debounce changes for 1 second because some joypads register twice in Windows for some reason
|
|
var not_changed_in_last_second = Engine.get_frames_drawn() - device_last_changed_at > Engine.get_frames_per_second()
|
|
if (next_device != device or next_device_index != device_index) and not_changed_in_last_second:
|
|
device_last_changed_at = Engine.get_frames_drawn()
|
|
|
|
device = next_device
|
|
device_index = next_device_index
|
|
device_changed.emit(device, device_index)
|
|
|
|
|
|
## Get the name of a joypad
|
|
func get_joy_name(at_device_index: int) -> String:
|
|
var joy_name: String = Input.get_joy_name(at_device_index)
|
|
if joy_name == "" and Input.get_joy_info(at_device_index).size() > 0 and "xinput" in Input.get_joy_info(at_device_index).keys()[0]:
|
|
joy_name = "XInput"
|
|
return joy_name
|
|
|
|
|
|
## Get the device name for an event
|
|
func get_device_from_event(event: InputEvent) -> String:
|
|
if event is InputEventKey or event is InputEventMouseButton or event is InputEventMouseMotion:
|
|
return DEVICE_KEYBOARD
|
|
elif event is InputEventJoypadButton or event is InputEventJoypadMotion:
|
|
return get_simplified_device_name(get_joy_name(event.device))
|
|
else:
|
|
return DEVICE_GENERIC
|
|
|
|
|
|
## Get the device name for an event
|
|
func get_device_index_from_event(event: InputEvent) -> int:
|
|
if event is InputEventJoypadButton or event is InputEventJoypadMotion:
|
|
return event.device
|
|
else:
|
|
return -1
|
|
|
|
|
|
## Convert a Godot device identifier to a simplified string
|
|
func get_simplified_device_name(raw_name: String, force_granular_identifier: bool = false) -> String:
|
|
var use_granular_identifier: bool = force_granular_identifier or InputHelperSettings.get_setting(InputHelperSettings.USE_GRANULAR_DEVICE_IDENTIFIERS, false)
|
|
|
|
var keywords: Dictionary = {
|
|
SUB_DEVICE_XBOX_ONE_CONTROLLER: ["Xbox One Controller"],
|
|
SUB_DEVICE_XBOX_SERIES_CONTROLLER: ["Xbox Series Controller", "Xbox Wireless Controller"],
|
|
DEVICE_XBOX_CONTROLLER: ["XInput", "XBox"],
|
|
SUB_DEVICE_PLAYSTATION3_CONTROLLER: ["PS3"],
|
|
SUB_DEVICE_PLAYSTATION4_CONTROLLER:["Nacon Revolution Unlimited Pro Controller", "PS4", "DUALSHOCK 4"],
|
|
SUB_DEVICE_PLAYSTATION5_CONTROLLER:["Sony DualSense", "PS5", "DualSense Wireless Controller"],
|
|
DEVICE_STEAMDECK_CONTROLLER: ["Steam"],
|
|
DEVICE_SWITCH_CONTROLLER: ["Switch", "Joy-Con (L/R)", "PowerA Core Controller"],
|
|
SUB_DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER: ["Joy-Con (L)"],
|
|
SUB_DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER: ["joy-Con (R)"],
|
|
} if use_granular_identifier else {
|
|
DEVICE_XBOX_CONTROLLER: ["XBox", "XInput"],
|
|
DEVICE_PLAYSTATION_CONTROLLER: ["Sony", "PS3", "PS5", "PS4", "DUALSHOCK 4", "DualSense", "Nacon Revolution Unlimited Pro Controller"],
|
|
DEVICE_STEAMDECK_CONTROLLER: ["Steam"],
|
|
DEVICE_SWITCH_CONTROLLER: ["Switch", "Joy-Con", "PowerA Core Controller"],
|
|
}
|
|
|
|
for device_key in keywords:
|
|
for keyword in keywords[device_key]:
|
|
if keyword.to_lower() in raw_name.to_lower():
|
|
return device_key
|
|
|
|
return DEVICE_GENERIC
|
|
|
|
|
|
## Check if there is a connected joypad
|
|
func has_joypad() -> bool:
|
|
return Input.get_connected_joypads().size() > 0
|
|
|
|
|
|
## Guess the initial input device
|
|
func guess_device_name() -> String:
|
|
if has_joypad():
|
|
return get_simplified_device_name(get_joy_name(0))
|
|
else:
|
|
return DEVICE_KEYBOARD
|
|
|
|
|
|
#region Mapping
|
|
|
|
|
|
func reset_all_actions() -> void:
|
|
InputMap.load_from_project_settings()
|
|
for action in InputMap.get_actions():
|
|
var input: InputEvent = get_joypad_input_for_action(action)
|
|
if input != null:
|
|
joypad_input_changed.emit(action, input)
|
|
|
|
input = get_keyboard_input_for_action(action)
|
|
if input != null:
|
|
keyboard_input_changed.emit(action, input)
|
|
|
|
|
|
## Set the key or button for an action
|
|
func set_keyboard_or_joypad_input_for_action(action: String, event: InputEvent, swap_if_taken: bool = true) -> void:
|
|
if event is InputEventKey or event is InputEventMouse:
|
|
set_keyboard_input_for_action(action, event, swap_if_taken)
|
|
elif event is InputEventJoypadButton:
|
|
set_joypad_input_for_action(action, event, swap_if_taken)
|
|
|
|
|
|
## Get the key or button for a given action depending on the current device
|
|
func get_keyboard_or_joypad_input_for_action(action: String) -> InputEvent:
|
|
if device == DEVICE_KEYBOARD:
|
|
return get_keyboard_input_for_action(action)
|
|
else:
|
|
return get_joypad_input_for_action(action)
|
|
|
|
|
|
## Get the key or button for a given action depending on the current device
|
|
func get_keyboard_or_joypad_inputs_for_action(action: String) -> Array[InputEvent]:
|
|
if device == DEVICE_KEYBOARD:
|
|
return get_keyboard_inputs_for_action(action)
|
|
else:
|
|
return get_joypad_inputs_for_action(action)
|
|
|
|
|
|
## Get a text label for a given input
|
|
func get_label_for_input(input: InputEvent) -> String:
|
|
if input == null: return ""
|
|
|
|
if input is InputEventKey:
|
|
if input.physical_keycode > 0 :
|
|
var keycode: Key = DisplayServer.keyboard_get_keycode_from_physical(input.physical_keycode) if DisplayServer.keyboard_get_current_layout() > -1 else input.physical_keycode
|
|
return OS.get_keycode_string(keycode)
|
|
elif input.keycode > 0:
|
|
return OS.get_keycode_string(input.keycode)
|
|
else:
|
|
return input.as_text()
|
|
|
|
elif input is InputEventMouseButton:
|
|
match input.button_index:
|
|
MOUSE_BUTTON_LEFT:
|
|
return "Mouse Left Button"
|
|
MOUSE_BUTTON_MIDDLE:
|
|
return "Mouse Middle Button"
|
|
MOUSE_BUTTON_RIGHT:
|
|
return "Mouse Right Button"
|
|
return "Mouse Button %d" % input.button_index
|
|
|
|
elif input is InputEventJoypadButton:
|
|
var labels = []
|
|
match _last_known_granular_joypad_device:
|
|
DEVICE_XBOX_CONTROLLER, DEVICE_GENERIC:
|
|
labels = XBOX_BUTTON_LABELS
|
|
SUB_DEVICE_XBOX_ONE_CONTROLLER:
|
|
labels = XBOX_ONE_BUTTON_LABELS
|
|
SUB_DEVICE_XBOX_SERIES_CONTROLLER:
|
|
labels = XBOX_SERIES_BUTTON_LABELS
|
|
SUB_DEVICE_SWITCH_JOYCON_LEFT_CONTROLLER, SUB_DEVICE_SWITCH_JOYCON_RIGHT_CONTROLLER:
|
|
labels = SWITCH_BUTTON_LABELS
|
|
DEVICE_SWITCH_CONTROLLER:
|
|
labels = SWITCH_EXTENDED_GAMEPAD_BUTTON_LABELS
|
|
SUB_DEVICE_PLAYSTATION3_CONTROLLER, SUB_DEVICE_PLAYSTATION4_CONTROLLER:
|
|
labels = PLAYSTATION_3_4_BUTTON_LABELS
|
|
DEVICE_PLAYSTATION_CONTROLLER, SUB_DEVICE_PLAYSTATION5_CONTROLLER:
|
|
labels = PLAYSTATION_5_BUTTON_LABELS
|
|
DEVICE_STEAMDECK_CONTROLLER:
|
|
labels = STEAMDECK_BUTTON_LABELS
|
|
if input.button_index < labels.size():
|
|
return "%s Button" % labels[input.button_index]
|
|
else:
|
|
return "Button %d" % input.button_index
|
|
|
|
elif input is InputEventJoypadMotion:
|
|
var motion: InputEventJoypadMotion = input as InputEventJoypadMotion
|
|
match motion.axis:
|
|
JOY_AXIS_LEFT_X:
|
|
return "Left Stick %s" % ("Left" if motion.axis_value < 0 else "Right")
|
|
JOY_AXIS_LEFT_Y:
|
|
return "Left Stick %s" % ("Up" if motion.axis_value < 0 else "Down")
|
|
JOY_AXIS_RIGHT_X:
|
|
return "Right Stick %s" % ("Left" if motion.axis_value < 0 else "Right")
|
|
JOY_AXIS_RIGHT_Y:
|
|
return "Right Stick %s" % ("Up" if motion.axis_value < 0 else "Down")
|
|
JOY_AXIS_TRIGGER_LEFT:
|
|
return "Left Trigger"
|
|
JOY_AXIS_TRIGGER_RIGHT:
|
|
return "Right Trigger"
|
|
|
|
return input.as_text()
|
|
|
|
|
|
## Serialize a single action's inputs.
|
|
func serialize_inputs_for_action(action: StringName) -> String:
|
|
var action_inputs: PackedStringArray = []
|
|
var inputs: Array[InputEvent] = InputMap.action_get_events(action)
|
|
for input in inputs:
|
|
if input is InputEventKey:
|
|
var s: String = get_label_for_input(input)
|
|
var modifiers: Array[String] = []
|
|
if input.alt_pressed:
|
|
modifiers.append("alt")
|
|
if input.shift_pressed:
|
|
modifiers.append("shift")
|
|
if input.ctrl_pressed:
|
|
modifiers.append("ctrl")
|
|
if input.meta_pressed:
|
|
modifiers.append("meta")
|
|
if not modifiers.is_empty():
|
|
s += "|" + ",".join(modifiers)
|
|
action_inputs.append("key:%s" % s)
|
|
elif input is InputEventMouseButton:
|
|
action_inputs.append("mouse:%d" % input.button_index)
|
|
elif input is InputEventJoypadButton:
|
|
action_inputs.append("joypad:%d" % input.button_index)
|
|
elif input is InputEventJoypadMotion:
|
|
action_inputs.append("joypad:%d|%f" % [input.axis, input.axis_value])
|
|
|
|
return ";".join(action_inputs)
|
|
|
|
|
|
## Serialize a list of action inputs to string. If actions is empty then it will serialize
|
|
## all actions.
|
|
func serialize_inputs_for_actions(actions: PackedStringArray = []) -> String:
|
|
if actions == null or actions.is_empty():
|
|
actions = InputMap.get_actions()
|
|
|
|
var map: Dictionary = {}
|
|
for action in actions:
|
|
map[action] = serialize_inputs_for_action(action)
|
|
|
|
return JSON.stringify({
|
|
version = SERIAL_VERSION,
|
|
map = map
|
|
})
|
|
|
|
|
|
## Deserialize a single action's inputs.
|
|
func deserialize_inputs_for_action(action: String, string: String) -> void:
|
|
InputMap.action_erase_events(action)
|
|
var action_inputs: PackedStringArray = string.split(";")
|
|
for action_input in action_inputs:
|
|
var bits: PackedStringArray = action_input.split(":")
|
|
|
|
# Ignore any empty actions
|
|
if bits.size() < 2: continue
|
|
|
|
var input_type: String = bits[0]
|
|
var input_details: String = bits[1]
|
|
|
|
match input_type:
|
|
"key":
|
|
var keyboard_input = InputEventKey.new()
|
|
if "|" in input_details:
|
|
var detail_bits = input_details.split("|")
|
|
keyboard_input.keycode = OS.find_keycode_from_string(detail_bits[0])
|
|
detail_bits = detail_bits[1].split(",")
|
|
if detail_bits.has("alt"):
|
|
keyboard_input.alt_pressed = true
|
|
if detail_bits.has("shift"):
|
|
keyboard_input.shift_pressed = true
|
|
if detail_bits.has("ctrl"):
|
|
keyboard_input.ctrl_pressed = true
|
|
if detail_bits.has("meta"):
|
|
keyboard_input.meta_pressed = true
|
|
else:
|
|
keyboard_input.keycode = OS.find_keycode_from_string(input_details)
|
|
InputMap.action_add_event(action, keyboard_input)
|
|
keyboard_input_changed.emit(action, keyboard_input)
|
|
|
|
"mouse":
|
|
var mouse_input = InputEventMouseButton.new()
|
|
mouse_input.button_index = int(input_details)
|
|
InputMap.action_add_event(action, mouse_input)
|
|
keyboard_input_changed.emit(action, mouse_input)
|
|
|
|
"joypad":
|
|
if "|" in str(input_details):
|
|
var joypad_motion_input = InputEventJoypadMotion.new()
|
|
var joypad_bits = input_details.split("|")
|
|
joypad_motion_input.axis = int(joypad_bits[0])
|
|
joypad_motion_input.axis_value = float(joypad_bits[1])
|
|
InputMap.action_add_event(action, joypad_motion_input)
|
|
joypad_input_changed.emit(action, joypad_motion_input)
|
|
else:
|
|
var joypad_input = InputEventJoypadButton.new()
|
|
joypad_input.button_index = int(input_details)
|
|
InputMap.action_add_event(action, joypad_input)
|
|
joypad_input_changed.emit(action, joypad_input)
|
|
|
|
|
|
## Deserialise a list of actions' inputs.
|
|
func deserialize_inputs_for_actions(string: String) -> void:
|
|
var data: Dictionary = JSON.parse_string(string)
|
|
|
|
# Use legacy deserialization
|
|
if not data.has("version"):
|
|
_deprecated_deserialize_inputs_for_actions(string)
|
|
return
|
|
|
|
# Version 1
|
|
for action in data.map.keys():
|
|
deserialize_inputs_for_action(action, data.map[action])
|
|
|
|
|
|
# Load inputs from a serialized string. [deprecated]
|
|
func _deprecated_deserialize_inputs_for_actions(string: String) -> void:
|
|
var map: Dictionary = JSON.parse_string(string)
|
|
for action in map.keys():
|
|
InputMap.action_erase_events(action)
|
|
|
|
for key in map[action]["keyboard"]:
|
|
var keyboard_input = InputEventKey.new()
|
|
if "|" in key:
|
|
var bits = key.split("|")
|
|
keyboard_input.keycode = OS.find_keycode_from_string(bits[0])
|
|
bits = bits[1].split(",")
|
|
if bits.has("alt"):
|
|
keyboard_input.alt_pressed = true
|
|
if bits.has("shift"):
|
|
keyboard_input.shift_pressed = true
|
|
if bits.has("ctrl"):
|
|
keyboard_input.ctrl_pressed = true
|
|
if bits.has("meta"):
|
|
keyboard_input.meta_pressed = true
|
|
else:
|
|
keyboard_input.keycode = OS.find_keycode_from_string(key)
|
|
InputMap.action_add_event(action, keyboard_input)
|
|
|
|
for button_index in map[action]["mouse"]:
|
|
var mouse_input = InputEventMouseButton.new()
|
|
mouse_input.button_index = int(button_index)
|
|
InputMap.action_add_event(action, mouse_input)
|
|
|
|
for button_index_or_motion in map[action]["joypad"]:
|
|
if "|" in str(button_index_or_motion):
|
|
var joypad_motion_input = InputEventJoypadMotion.new()
|
|
var bits = button_index_or_motion.split("|")
|
|
joypad_motion_input.axis = int(bits[0])
|
|
joypad_motion_input.axis_value = float(bits[1])
|
|
InputMap.action_add_event(action, joypad_motion_input)
|
|
else:
|
|
var joypad_input = InputEventJoypadButton.new()
|
|
joypad_input.button_index = int(button_index_or_motion)
|
|
InputMap.action_add_event(action, joypad_input)
|
|
|
|
|
|
#endregion
|
|
|
|
#region Keyboard/mouse input
|
|
|
|
|
|
## Get all of the keys/mouse buttons used for an action.
|
|
func get_keyboard_inputs_for_action(action: String) -> Array[InputEvent]:
|
|
return InputMap.action_get_events(action).filter(func(event):
|
|
return event is InputEventKey or event is InputEventMouseButton
|
|
)
|
|
|
|
|
|
## Get the first key for an action
|
|
func get_keyboard_input_for_action(action: String) -> InputEvent:
|
|
var inputs: Array[InputEvent] = get_keyboard_inputs_for_action(action)
|
|
return null if inputs.is_empty() else inputs[0]
|
|
|
|
|
|
## Set the key used for an action
|
|
func set_keyboard_input_for_action(action: String, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
|
return _update_keyboard_input_for_action(action, input, swap_if_taken, null)
|
|
|
|
|
|
## Replace a specific key with another key
|
|
func replace_keyboard_input_for_action(action: String, current_input: InputEvent, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
|
return _update_keyboard_input_for_action(action, input, swap_if_taken, current_input)
|
|
|
|
|
|
## Replace a specific key, given its index
|
|
func replace_keyboard_input_at_index(action: String, index: int, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
|
var inputs: Array[InputEvent] = get_keyboard_inputs_for_action(action)
|
|
var replacing_input = InputEventKey.new() if (inputs.is_empty() or inputs.size() <= index) else inputs[index]
|
|
return _update_keyboard_input_for_action(action, input, swap_if_taken, replacing_input)
|
|
|
|
|
|
func _update_keyboard_input_for_action(action: String, input: InputEvent, swap_if_taken: bool, replacing_input: InputEvent = null) -> Error:
|
|
if not (input is InputEventKey or input is InputEventMouseButton): return ERR_INVALID_DATA
|
|
|
|
var is_valid_keyboard_event = func(event):
|
|
return event is InputEventKey or event is InputEventMouseButton
|
|
|
|
return _update_input_for_action(action, input, swap_if_taken, replacing_input, is_valid_keyboard_event, keyboard_input_changed)
|
|
|
|
|
|
#endregion
|
|
|
|
#region Joypad input
|
|
|
|
|
|
## Get all buttons used for an action
|
|
func get_joypad_inputs_for_action(action: String) -> Array[InputEvent]:
|
|
return InputMap.action_get_events(action).filter(func(event):
|
|
return event is InputEventJoypadButton or event is InputEventJoypadMotion
|
|
)
|
|
|
|
|
|
## Get the first button for an action
|
|
func get_joypad_input_for_action(action: String) -> InputEvent:
|
|
var buttons: Array[InputEvent] = get_joypad_inputs_for_action(action)
|
|
return null if buttons.is_empty() else buttons[0]
|
|
|
|
|
|
## Set the button for an action
|
|
func set_joypad_input_for_action(action: String, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
|
return _update_joypad_input_for_action(action, input, swap_if_taken, null)
|
|
|
|
|
|
## Replace a specific button for an action
|
|
func replace_joypad_input_for_action(action: String, current_input: InputEvent, input: InputEventJoypadButton, swap_if_taken: bool = true) -> Error:
|
|
return _update_joypad_input_for_action(action, input, swap_if_taken, current_input)
|
|
|
|
|
|
## Replace a button, given its index
|
|
func replace_joypad_input_at_index(action: String, index: int, input: InputEvent, swap_if_taken: bool = true) -> Error:
|
|
var inputs: Array[InputEvent] = get_joypad_inputs_for_action(action)
|
|
var replacing_input
|
|
if inputs.is_empty() or inputs.size() <= index:
|
|
replacing_input = InputEventJoypadButton.new()
|
|
replacing_input.button_index = JOY_BUTTON_INVALID
|
|
else:
|
|
replacing_input = inputs[index]
|
|
return _update_joypad_input_for_action(action, input, swap_if_taken, replacing_input)
|
|
|
|
|
|
## Set the action used for a button
|
|
func _update_joypad_input_for_action(action: String, input: InputEvent, swap_if_taken: bool = true, replacing_input: InputEvent = null) -> Error:
|
|
var is_valid_keyboard_event = func(event):
|
|
return event is InputEventJoypadButton or event is InputEventJoypadMotion
|
|
|
|
return _update_input_for_action(action, input, swap_if_taken, replacing_input, is_valid_keyboard_event, joypad_input_changed)
|
|
|
|
|
|
func _update_input_for_action(action: String, input: InputEvent, swap_if_taken: bool, replacing_input: InputEvent, check_is_valid: Callable, did_change_signal: Signal) -> Error:
|
|
# Find any action that is already mapped to this input
|
|
var clashing_action = ""
|
|
var clashing_event
|
|
if swap_if_taken:
|
|
for other_action in InputMap.get_actions():
|
|
if other_action == action: continue
|
|
|
|
for event in InputMap.action_get_events(other_action):
|
|
if event.is_match(input):
|
|
clashing_action = other_action
|
|
clashing_event = event
|
|
|
|
# Find the key based event for the target action
|
|
var action_events: Array[InputEvent] = InputMap.action_get_events(action)
|
|
var is_replacing: bool = false
|
|
for i in range(0, action_events.size()):
|
|
var event: InputEvent = action_events[i]
|
|
if check_is_valid.call(event):
|
|
if replacing_input != null and not event.is_match(replacing_input):
|
|
continue
|
|
|
|
# Remap the other event if there is a clashing one
|
|
if clashing_action:
|
|
_update_input_for_action(clashing_action, event, false, clashing_event, check_is_valid, did_change_signal)
|
|
|
|
# Replace the event
|
|
action_events[i] = input
|
|
is_replacing = true
|
|
break
|
|
|
|
# If we were trying to replace something but didn't find it then just add it to the end
|
|
if not is_replacing:
|
|
action_events.append(input)
|
|
|
|
# Apply the changes
|
|
InputMap.action_erase_events(action)
|
|
for event in action_events:
|
|
if event != null:
|
|
InputMap.action_add_event(action, event)
|
|
|
|
did_change_signal.emit(action, input)
|
|
|
|
return OK
|
|
|
|
|
|
#endregion
|
|
|
|
#region Rumbling
|
|
|
|
|
|
func rumble_small(target_device: int = 0) -> void:
|
|
Input.start_joy_vibration(target_device, 0.4, 0, 0.1)
|
|
|
|
|
|
func rumble_medium(target_device: int = 0) -> void:
|
|
Input.start_joy_vibration(target_device, 0, 0.7, 0.1)
|
|
|
|
|
|
func rumble_large(target_device: int = 0) -> void:
|
|
Input.start_joy_vibration(target_device, 0, 1, 0.1)
|
|
|
|
|
|
func start_rumble_small(target_device: int = 0) -> void:
|
|
Input.start_joy_vibration(target_device, 0.4, 0, 0)
|
|
|
|
|
|
func start_rumble_medium(target_device: int = 0) -> void:
|
|
Input.start_joy_vibration(target_device, 0, 0.7, 0)
|
|
|
|
|
|
func start_rumble_large(target_device: int = 0) -> void:
|
|
Input.start_joy_vibration(target_device, 0, 1, 0)
|
|
|
|
|
|
func stop_rumble(target_device: int = 0) -> void:
|
|
Input.stop_joy_vibration(target_device)
|
|
|
|
|
|
#endregion
|