157 lines
5.4 KiB
GDScript
157 lines
5.4 KiB
GDScript
extends SceneTree
|
|
|
|
const ControllerMappingManagerType = preload(
|
|
"res://settings/controller_mapping_manager.gd"
|
|
)
|
|
const ControllerMappingPanelType = preload(
|
|
"res://ui/controller_mapping_panel.gd"
|
|
)
|
|
const GameUIType = preload("res://ui/game_ui.gd")
|
|
|
|
|
|
func _init() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
var manager := ControllerMappingManagerType.new()
|
|
root.add_child(manager)
|
|
await process_frame
|
|
var failure: String = _validate_manager(manager)
|
|
if failure.is_empty():
|
|
failure = _validate_auto_map(manager)
|
|
if failure.is_empty():
|
|
failure = _validate_virtual_mouse_bounds()
|
|
if failure.is_empty():
|
|
print("controller mapping validation passed")
|
|
quit(0)
|
|
return
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func _validate_manager(manager: ControllerMappingManagerType) -> String:
|
|
var defaults: Dictionary = manager.get_active_bindings()
|
|
if defaults.size() != ControllerMappingManagerType.ROLE_ORDER.size():
|
|
return "default mapping covers %d of %d controller roles: %s" % [
|
|
defaults.size(),
|
|
ControllerMappingManagerType.ROLE_ORDER.size(),
|
|
str(defaults.keys()),
|
|
]
|
|
var trigger_button := InputEventJoypadButton.new()
|
|
trigger_button.button_index = JOY_BUTTON_MISC1
|
|
trigger_button.pressed = true
|
|
var trigger_binding: Dictionary = manager.binding_from_event(
|
|
ControllerMappingManagerType.ROLE_LT,
|
|
trigger_button,
|
|
)
|
|
if str(trigger_binding.get("kind", "")) != "button":
|
|
return "trigger role did not accept a button-backed handheld trigger"
|
|
var trigger_motion := InputEventJoypadMotion.new()
|
|
trigger_motion.axis = JOY_AXIS_TRIGGER_RIGHT
|
|
trigger_motion.axis_value = 1.0
|
|
trigger_binding = manager.binding_from_event(
|
|
ControllerMappingManagerType.ROLE_LT,
|
|
trigger_motion,
|
|
)
|
|
if str(trigger_binding.get("kind", "")) != "axis":
|
|
return "trigger role did not accept an axis-backed controller trigger"
|
|
var stick_button := manager.binding_from_event(
|
|
ControllerMappingManagerType.ROLE_RIGHT_STICK_X,
|
|
trigger_button,
|
|
)
|
|
if not stick_button.is_empty():
|
|
return "stick axis role accepted a button"
|
|
var keyboard_events_before: int = _keyboard_event_count(&"jump")
|
|
var custom: Dictionary = defaults.duplicate(true)
|
|
custom[str(ControllerMappingManagerType.ROLE_A)] = {
|
|
"kind": "button",
|
|
"button": int(JOY_BUTTON_X),
|
|
}
|
|
custom[str(ControllerMappingManagerType.ROLE_LT)] = trigger_binding
|
|
if not manager.replace_active_bindings(custom):
|
|
return "valid custom mapping could not be saved"
|
|
if not manager.has_custom_mapping():
|
|
return "saved custom mapping did not become active"
|
|
if _keyboard_event_count(&"jump") != keyboard_events_before:
|
|
return "controller remapping changed keyboard bindings"
|
|
if not FileAccess.file_exists(
|
|
ControllerMappingManagerType.PROFILE_PATH
|
|
):
|
|
return "controller mapping was not stored device-locally"
|
|
return ""
|
|
|
|
|
|
func _validate_auto_map(manager: ControllerMappingManagerType) -> String:
|
|
var panel := ControllerMappingPanelType.new()
|
|
root.add_child(panel)
|
|
panel.setup(manager)
|
|
panel.open_panel()
|
|
panel._begin_auto_map()
|
|
for role: StringName in ControllerMappingManagerType.ROLE_ORDER:
|
|
var binding := (
|
|
ControllerMappingManagerType.default_bindings()[str(role)]
|
|
as Dictionary
|
|
)
|
|
if role == ControllerMappingManagerType.ROLE_B:
|
|
binding = (
|
|
ControllerMappingManagerType.default_bindings()[
|
|
str(ControllerMappingManagerType.ROLE_A)
|
|
] as Dictionary
|
|
)
|
|
if str(binding.get("kind", "")) == "button":
|
|
var button := InputEventJoypadButton.new()
|
|
button.button_index = int(binding.get("button", -1))
|
|
button.pressed = true
|
|
panel._input(button)
|
|
else:
|
|
var motion := InputEventJoypadMotion.new()
|
|
motion.axis = int(binding.get("axis", -1))
|
|
motion.axis_value = float(binding.get("direction", -1.0))
|
|
panel._input(motion)
|
|
if panel._auto_map_active:
|
|
return "auto-map did not complete after every requested input"
|
|
if not manager.has_custom_mapping():
|
|
return "completed auto-map did not activate its profile"
|
|
if panel._binding_buttons.size() != (
|
|
ControllerMappingManagerType.ROLE_ORDER.size()
|
|
):
|
|
return "manual override list does not expose every mapped role"
|
|
var conflict_button := panel._binding_buttons.get(
|
|
ControllerMappingManagerType.ROLE_A
|
|
) as Button
|
|
var conflict_style := conflict_button.get_theme_stylebox(
|
|
&"normal"
|
|
) as StyleBoxFlat
|
|
if (
|
|
conflict_style == null
|
|
or conflict_style.bg_color
|
|
!= UtilityPageStyle.OCEAN_DANGER
|
|
):
|
|
return "duplicate controller bindings were not marked in red"
|
|
panel._begin_manual_capture(ControllerMappingManagerType.ROLE_LT)
|
|
if "left trigger" in panel._progress_label.text.to_lower():
|
|
return "manual remapping still dictates a specific physical input"
|
|
if "any button" not in panel._progress_label.text.to_lower():
|
|
return "manual remapping does not request a generic controller input"
|
|
panel._cancel_capture()
|
|
return ""
|
|
|
|
|
|
func _validate_virtual_mouse_bounds() -> String:
|
|
var window_size := Vector2(3840.0, 2160.0)
|
|
var bounds: Rect2 = GameUIType.get_virtual_mouse_window_bounds(window_size)
|
|
var expected_margin := Vector2(42.0, 42.0)
|
|
if not bounds.position.is_equal_approx(expected_margin):
|
|
return "4K virtual cursor minimum bounds are incorrect: %s" % bounds
|
|
if not bounds.end.is_equal_approx(window_size - expected_margin):
|
|
return "4K virtual cursor maximum bounds are incorrect: %s" % bounds
|
|
return ""
|
|
|
|
|
|
func _keyboard_event_count(action: StringName) -> int:
|
|
var count: int = 0
|
|
for event: InputEvent in InputMap.action_get_events(action):
|
|
if event is InputEventKey or event is InputEventMouseButton:
|
|
count += 1
|
|
return count
|