Files
gymkhana-actions/addons/escoria-ui-simplemouse/game.gd
bolinfest d0d97ba9bf feat: add gamepad support to escoria-ui-simplemouse (#518)
This commit starts by introducing a new action, `ESC_UI_PRIMARY_ACTION`,
that is used to represent a "primary action" from an input device, such
as a left-click on a mouse or the press of the primary button on a
gamepad. Although this action is not added to `InputMap` by default in
`escoria-core`, `_unhandled_input()` in `esc_item.gd` is updated to look
for it.

The other portion of this commit is an update to
`addons/escoria-ui-simplemouse/game.gd` that adds a gamepad mapping to
the `InputMap` for both `ESC_UI_PRIMARY_ACTION` as well as another new
action `ESC_UI_CHANGE_VERB_ACTION`. These actions are mapped to X and Y
on an XBox controller, respectively.

Note that `game.gd` is also updated to implement `_process()` such that
moving `JOY_AXIS_0` and `JOY_AXIS_1` on "gamepad 0" will move the
cursor around on the screen as the mouse would.

Overall, this makes the game fairly playable with a gamepad, though
admittedly a mouse is still currently required to click the "New game"
button on the initial menu because `BaseButton::gui_input()` appears to
be hardcoded to check for mouse events exclusively:

godotengine/godot@a09814e/scene/gui/base_button.cpp#L55-L81
2022-03-14 22:15:01 +01:00

376 lines
11 KiB
GDScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends ESCGame
const VERB_WALK = "walk"
"""
Implement methods to react to inputs.
- left_click_on_bg(position: Vector2)
- right_click_on_bg(position: Vector2)
- left_double_click_on_bg(position: Vector2)
- element_focused(element_id: String)
- element_unfocused()
- left_click_on_item(item_global_id: String, event: InputEvent)
- right_click_on_item(item_global_id: String, event: InputEvent)
- left_double_click_on_item(item_global_id: String, event: InputEvent)
- left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent)
- right_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent)
- left_double_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent)
- inventory_item_focused(inventory_item_global_id: String)
- inventory_item_unfocused()
- open_inventory()
- close_inventory()
- mousewheel_action(direction: int)
- hide_ui()
- show_ui()
- pause_game()
- unpause_game()
- show_main_menu()
- hide_main_menu()
- apply_custom_settings()
- _on_event_done(event_name: String)
"""
# Value to use for `device` argument to various `Input.get_joy` methods.
const JOY_DEVICE = 0
# See https://docs.godotengine.org/en/stable/tutorials/inputs/controllers_gamepads_joysticks.html?#dead-zone
const DEADZONE = 0.2
# Multiplier to apply to axis when it exceeds DEADZONE.
const AXIS_WEIGHT = 50.0
# JOY_BUTTON_2 corresponds to the "X" button on an XBox controller
# or the Square button on a Playstation controller. These appear to
# map to the "primary action," in practice, so we treat it like a left click.
const PRIMARY_ACTION_BUTTON = JOY_BUTTON_2
# JOY_BUTTON_3 corresponds to the "Y" button on an XBox controller
# or the Triangle button on a Playstation controller. These appear to
# map to the "secondary action," in practice, so we treat it like a right click.
const CHANGE_VERB_BUTTON = JOY_BUTTON_3
# Input action for use by InputMap
const ESC_UI_CHANGE_VERB_ACTION = "esc_change_verb"
# true when a gamepad is connected.
var _is_gamepad_connected = false
func _enter_tree():
var room_selector_parent = $CanvasLayer/ui/HBoxContainer/VBoxContainer
if ProjectSettings.get_setting("escoria/debug/enable_room_selector") and \
room_selector_parent.get_node_or_null("room_select") == null:
room_selector_parent.add_child(
preload(
"res://addons/escoria-core/ui_library/tools/room_select" +\
"/room_select.tscn"
).instance()
)
var input_handler = funcref(self, "_process_input")
escoria.inputs_manager.register_custom_input_handler(input_handler)
_is_gamepad_connected = Input.is_joy_known(JOY_DEVICE)
if _is_gamepad_connected:
_on_gamepad_connected()
Input.connect("joy_connection_changed", self, "_on_joy_connection_changed")
func _exit_tree():
escoria.inputs_manager.register_custom_input_handler(null)
Input.disconnect("joy_connection_changed", self, "_on_joy_connection_changed")
_on_gamepad_disconnected()
func _input(event: InputEvent) -> void:
if escoria.main.current_scene and escoria.main.current_scene.game:
if event is InputEventMouseMotion:
escoria.main.current_scene.game. \
update_tooltip_following_mouse_position(event.position)
# https://github.com/godotengine/godot-demo-projects/blob/3.4-585455e/misc/joypads/joypads.gd
# was informative in wiring up the gamepad properly.
func _on_gamepad_connected():
set_physics_process(true)
var primary_event = InputEventJoypadButton.new()
primary_event.button_index = PRIMARY_ACTION_BUTTON
InputMap.add_action(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION)
InputMap.action_add_event(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION, primary_event)
var verb_event = InputEventJoypadButton.new()
verb_event.button_index = CHANGE_VERB_BUTTON
InputMap.add_action(ESC_UI_CHANGE_VERB_ACTION)
InputMap.action_add_event(ESC_UI_CHANGE_VERB_ACTION, verb_event)
func _on_gamepad_disconnected():
InputMap.action_erase_events(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION)
InputMap.erase_action(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION)
InputMap.action_erase_events(ESC_UI_CHANGE_VERB_ACTION)
InputMap.erase_action(ESC_UI_CHANGE_VERB_ACTION)
set_physics_process(false)
_is_gamepad_connected = false
func _on_joy_connection_changed(device: int, connected: bool) -> void:
if device != JOY_DEVICE:
return
elif connected:
_on_gamepad_connected()
else:
_on_gamepad_disconnected()
func _process(_delta) -> void:
if !_is_gamepad_connected:
return
var x = Input.get_joy_axis(JOY_DEVICE, JOY_AXIS_0)
var y = Input.get_joy_axis(JOY_DEVICE, JOY_AXIS_1)
var delta_x = int(x * AXIS_WEIGHT) if abs(x) > DEADZONE else 0
var delta_y = int(y * AXIS_WEIGHT) if abs(y) > DEADZONE else 0
if delta_x or delta_y:
var direction: Vector2
direction.x = delta_x
direction.y = delta_y
escoria.logger.trace("gamepad direction:", [direction])
var viewport = get_viewport()
viewport.warp_mouse(viewport.get_mouse_position() + direction)
func _process_input(event: InputEvent, is_default_state: bool) -> bool:
if not is_default_state:
# ESCBackground is not guaranteed to be set, as we may be on
# the "New Game" screen.
return false
elif _is_gamepad_connected and event is InputEventJoypadButton:
escoria.logger.trace("InputEventJoypadButton:", [event.as_text()])
if event.is_action_pressed(escoria.inputs_manager.ESC_UI_PRIMARY_ACTION):
# Admittedly, this breaks abstraction barriers and is completely
# inappropriate, but it's what works right now.
escoria.inputs_manager._on_left_click_on_bg(get_global_mouse_position())
return true
elif event.is_action_pressed(ESC_UI_CHANGE_VERB_ACTION):
mousewheel_action(1)
return true
return false
## BACKGROUND ##
func left_click_on_bg(position: Vector2) -> void:
if escoria.main.current_scene.player:
escoria.action_manager.do(
escoria.action_manager.ACTION.BACKGROUND_CLICK,
[escoria.main.current_scene.player.global_id, position],
true
)
$mouse_layer/verbs_menu.set_by_name(VERB_WALK)
$mouse_layer/verbs_menu.clear_tool_texture()
func right_click_on_bg(position: Vector2) -> void:
mousewheel_action(1)
func left_double_click_on_bg(position: Vector2) -> void:
if escoria.main.current_scene.player:
escoria.action_manager.do(
escoria.action_manager.ACTION.BACKGROUND_CLICK,
[escoria.main.current_scene.player.global_id, position, true],
true
)
$mouse_layer/verbs_menu.set_by_name(VERB_WALK)
$mouse_layer/verbs_menu.clear_tool_texture()
## ITEM/HOTSPOT FOCUS ##
func element_focused(element_id: String) -> void:
var target_obj = escoria.object_manager.get_object(element_id).node
$tooltip_layer/tooltip.set_target(target_obj.tooltip_name)
if escoria.action_manager.current_action != "use" \
and escoria.action_manager.current_tool == null \
and target_obj is ESCItem:
$mouse_layer/verbs_menu.set_by_name(target_obj.default_action)
func element_unfocused() -> void:
$tooltip_layer/tooltip.set_target("")
## ITEMS ##
func left_click_on_item(item_global_id: String, event: InputEvent) -> void:
escoria.action_manager.do(
escoria.action_manager.ACTION.ITEM_LEFT_CLICK,
[item_global_id, event],
true
)
func right_click_on_item(item_global_id: String, event: InputEvent) -> void:
mousewheel_action(1)
func left_double_click_on_item(item_global_id: String, event: InputEvent) -> void:
escoria.action_manager.do(
escoria.action_manager.ACTION.ITEM_LEFT_CLICK,
[item_global_id, event],
true
)
## INVENTORY ##
func left_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void:
escoria.action_manager.do(
escoria.action_manager.ACTION.ITEM_LEFT_CLICK,
[inventory_item_global_id, event]
)
if escoria.action_manager.current_action == "use":
var item = escoria.object_manager.get_object(
inventory_item_global_id
).node
if item.has_method("get_sprite") and item.get_sprite().texture:
$mouse_layer/verbs_menu.set_tool_texture(
item.get_sprite().texture
)
elif item.inventory_item.texture_normal:
$mouse_layer/verbs_menu.set_tool_texture(
item.inventory_item.texture_normal
)
func right_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void:
mousewheel_action(1)
func left_double_click_on_inventory_item(inventory_item_global_id: String, event: InputEvent) -> void:
pass
func inventory_item_focused(inventory_item_global_id: String) -> void:
$tooltip_layer/tooltip.set_target(
escoria.object_manager.get_object(
inventory_item_global_id
).node.tooltip_name
)
func inventory_item_unfocused() -> void:
$tooltip_layer/tooltip.set_target("")
func open_inventory():
$CanvasLayer/ui/HBoxContainer/inventory_ui.show_inventory()
func close_inventory():
$CanvasLayer/ui/HBoxContainer/inventory_ui.hide_inventory()
func mousewheel_action(direction: int):
$mouse_layer/verbs_menu.iterate_actions_cursor(direction)
func hide_ui():
$CanvasLayer/ui/HBoxContainer/inventory_ui.hide()
func show_ui():
$CanvasLayer/ui/HBoxContainer/inventory_ui.show()
func hide_main_menu():
if get_node(main_menu).visible:
get_node(main_menu).hide()
func show_main_menu():
if not get_node(main_menu).visible:
get_node(main_menu).reset()
get_node(main_menu).show()
func unpause_game():
if get_node(pause_menu).visible:
get_node(pause_menu).hide()
escoria.object_manager.get_object("_camera").node.current = true
escoria.main.current_scene.game.show_ui()
escoria.main.current_scene.show()
func pause_game():
if not get_node(pause_menu).visible:
get_node(main_menu).reset()
get_node(pause_menu).set_save_enabled(
escoria.save_manager.save_enabled
)
get_node(pause_menu).show()
escoria.object_manager.get_object("_camera").node.current = false
escoria.main.current_scene.game.hide_ui()
escoria.main.current_scene.hide()
func apply_custom_settings(custom_settings: Dictionary):
if custom_settings.has("a_custom_setting"):
escoria.logger.info(
"custom setting value loaded:",
[custom_settings["a_custom_setting"]]
)
func get_custom_data() -> Dictionary:
return {
"ui_type": "simplemouse"
}
# Update the tooltip
#
# #### Parameters
#
# - p_position: Position of the mouse
func update_tooltip_following_mouse_position(p_position: Vector2):
var corrected_position = p_position
# clamp TOP
if tooltip_node.tooltip_distance_to_edge_top(p_position) <= mouse_tooltip_margin:
corrected_position.y = mouse_tooltip_margin
# clamp BOTTOM
if tooltip_node.tooltip_distance_to_edge_bottom(p_position + tooltip_node.rect_size) <= mouse_tooltip_margin:
corrected_position.y = escoria.game_size.y - mouse_tooltip_margin - tooltip_node.rect_size.y
# clamp LEFT
if tooltip_node.tooltip_distance_to_edge_left(p_position - tooltip_node.rect_size/2) <= mouse_tooltip_margin:
corrected_position.x = mouse_tooltip_margin
# clamp RIGHT
if tooltip_node.tooltip_distance_to_edge_right(p_position + tooltip_node.rect_size/2) <= mouse_tooltip_margin:
corrected_position.x = escoria.game_size.x - mouse_tooltip_margin - tooltip_node.rect_size.x
tooltip_node.anchor_right = 0.2
tooltip_node.rect_position = corrected_position + tooltip_node.offset_from_cursor
func _on_action_finished():
$mouse_layer/verbs_menu.clear_tool_texture()
$mouse_layer/verbs_menu.iterate_actions_cursor(0)
func _on_event_done(_return_code: int, _event_name: String):
escoria.action_manager.clear_current_action()
$mouse_layer/verbs_menu.clear_tool_texture()
func _on_MenuButton_pressed() -> void:
pause_game()