94 lines
2.7 KiB
GDScript
94 lines
2.7 KiB
GDScript
extends Node
|
|
|
|
## Global keybinding manager (autoload `Controls`).
|
|
## Loads saved keyboard bindings on startup and applies them to InputMap so
|
|
## rebinds made in the intro menu persist into the game scene. Only the
|
|
## keyboard event of each action is overridden; joypad/mouse events are kept.
|
|
|
|
const SAVE_PATH := "user://controls.cfg"
|
|
const SECTION := "keybinds"
|
|
|
|
## Actions the options menu lets the player rebind (keyboard only).
|
|
const REBINDABLE_ACTIONS: PackedStringArray = [
|
|
&"move_forward",
|
|
&"move_back",
|
|
&"move_left",
|
|
&"move_right",
|
|
&"charge",
|
|
&"ability_kick",
|
|
&"ability_slam",
|
|
&"ability_dash",
|
|
]
|
|
|
|
|
|
func _ready() -> void:
|
|
load_saved()
|
|
|
|
|
|
## Returns the first keyboard event bound to an action, or null.
|
|
func get_key_event(action: StringName) -> InputEventKey:
|
|
for event: InputEvent in InputMap.action_get_events(action):
|
|
if event is InputEventKey:
|
|
return event
|
|
return null
|
|
|
|
|
|
## Human-readable label for the action's current key (e.g. "W", "Space").
|
|
func get_key_label(action: StringName) -> String:
|
|
var event := get_key_event(action)
|
|
if event == null:
|
|
return "—"
|
|
return event.as_text_physical_keycode()
|
|
|
|
|
|
## Replaces the primary keyboard event of `action` with `event`, keeping
|
|
## every other event (e.g. arrow-key alternates, joypad) in its place.
|
|
func rebind(action: StringName, event: InputEventKey) -> void:
|
|
rebind_no_save(action, event)
|
|
save()
|
|
|
|
|
|
func save() -> void:
|
|
var cfg := ConfigFile.new()
|
|
for action: StringName in REBINDABLE_ACTIONS:
|
|
var event := get_key_event(action)
|
|
if event != null:
|
|
cfg.set_value(SECTION, String(action), event.physical_keycode)
|
|
cfg.save(SAVE_PATH)
|
|
|
|
|
|
func load_saved() -> void:
|
|
var cfg := ConfigFile.new()
|
|
if cfg.load(SAVE_PATH) != OK:
|
|
return
|
|
for action: StringName in REBINDABLE_ACTIONS:
|
|
var key := String(action)
|
|
if not cfg.has_section_key(SECTION, key):
|
|
continue
|
|
var keycode: int = cfg.get_value(SECTION, key)
|
|
var event := InputEventKey.new()
|
|
event.physical_keycode = keycode as Key
|
|
rebind_no_save(action, event)
|
|
|
|
|
|
## Rebuilds the action's event list, swapping the first keyboard event for
|
|
## `event` in place so ordering (and thus the "primary" key) is preserved.
|
|
func rebind_no_save(action: StringName, event: InputEventKey) -> void:
|
|
var events := InputMap.action_get_events(action)
|
|
InputMap.action_erase_events(action)
|
|
var replaced := false
|
|
for existing: InputEvent in events:
|
|
if not replaced and existing is InputEventKey:
|
|
InputMap.action_add_event(action, event)
|
|
replaced = true
|
|
else:
|
|
InputMap.action_add_event(action, existing)
|
|
if not replaced:
|
|
InputMap.action_add_event(action, event)
|
|
|
|
|
|
func reset_defaults() -> void:
|
|
InputMap.load_from_project_settings()
|
|
if FileAccess.file_exists(SAVE_PATH):
|
|
DirAccess.remove_absolute(SAVE_PATH)
|