From 092f786c077b8e742e48610cc1dfc11131b0dc10 Mon Sep 17 00:00:00 2001 From: iBerserker89 Date: Wed, 21 Jan 2026 16:52:53 -0300 Subject: [PATCH 1/3] =?UTF-8?q?fazer=20menu=20de=20pausa,=20=C3=ADcone=20d?= =?UTF-8?q?e=20settings=20e=20som=20ficarem=20acess=C3=ADveis=20durante=20?= =?UTF-8?q?o=20tutorial?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scenes/game/tutorial_dialogue_balloon.tscn | 2 +- scenes/globals/gui_manager.tscn | 4 ++++ scenes/globals/settings_icon.tscn | 1 + scripts/game/tutorial_dialogue_balloon.gd | 13 +++++++++++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/scenes/game/tutorial_dialogue_balloon.tscn b/scenes/game/tutorial_dialogue_balloon.tscn index fe32f64..d6db275 100644 --- a/scenes/game/tutorial_dialogue_balloon.tscn +++ b/scenes/game/tutorial_dialogue_balloon.tscn @@ -64,7 +64,7 @@ MarginContainer/constants/margin_top = 15 PanelContainer/styles/panel = SubResource("StyleBoxFlat_qkmqt") [node name="TutorialDialogueBalloon" type="CanvasLayer"] -layer = 100 +layer = 10 script = ExtResource("1_ekbxt") [node name="Balloon" type="Control" parent="."] diff --git a/scenes/globals/gui_manager.tscn b/scenes/globals/gui_manager.tscn index e7f9c52..e1e16b1 100644 --- a/scenes/globals/gui_manager.tscn +++ b/scenes/globals/gui_manager.tscn @@ -442,6 +442,7 @@ text = "Back" flat = true [node name="SettingsMenu" type="CanvasLayer" parent="."] +layer = 60 visible = false [node name="TextureRect" type="TextureRect" parent="SettingsMenu"] @@ -570,6 +571,7 @@ vertical_alignment = 1 visible = false [node name="InputSettings" type="CanvasLayer" parent="."] +layer = 60 visible = false [node name="Control" type="Control" parent="InputSettings"] @@ -723,6 +725,7 @@ offset_bottom = 412.0 text = "Restart" [node name="PauseMenu" type="CanvasLayer" parent="."] +layer = 50 visible = false [node name="TextureRect" type="TextureRect" parent="PauseMenu"] @@ -908,6 +911,7 @@ visible = false [node name="StatsTable" parent="StatsTable" instance=ExtResource("15_7aiwd")] [node name="SoundIcon" parent="." instance=ExtResource("6_haemp")] +layer = 150 [connection signal="pressed" from="InputSettings/Control/PanelContainer/MarginContainer/VBoxContainer/ResetButton" to="InputSettings/Control" method="_on_reset_button_pressed"] [connection signal="pressed" from="InputSettings/Control/PanelContainer/MarginContainer/VBoxContainer/BackToSettingsButton" to="InputSettings/Control" method="_on_back_to_settings_button_pressed"] diff --git a/scenes/globals/settings_icon.tscn b/scenes/globals/settings_icon.tscn index 74d1b4c..7c620f4 100644 --- a/scenes/globals/settings_icon.tscn +++ b/scenes/globals/settings_icon.tscn @@ -4,6 +4,7 @@ [ext_resource type="Texture2D" uid="uid://bbb3yar7vfie3" path="res://assets/sprites/icons/settings-icon.svg" id="2_1jmkx"] [node name="SettingsIcon" type="CanvasLayer"] +layer = 100 script = ExtResource("1_knx0a") [node name="VBoxContainer" type="VBoxContainer" parent="."] diff --git a/scripts/game/tutorial_dialogue_balloon.gd b/scripts/game/tutorial_dialogue_balloon.gd index a4f9b9f..4edc461 100644 --- a/scripts/game/tutorial_dialogue_balloon.gd +++ b/scripts/game/tutorial_dialogue_balloon.gd @@ -70,8 +70,17 @@ func _ready() -> void: add_child(mutation_cooldown) -func _unhandled_input(_event: InputEvent) -> void: - # Only the balloon is allowed to handle input while it's showing +#func _unhandled_input(_event: InputEvent) -> void: + ## Only the balloon is allowed to handle input while it's showing + #get_viewport().set_input_as_handled() +func _unhandled_input(event: InputEvent) -> void: + # Se o balão n está visível, não bloqueia nada + if not is_instance_valid(balloon) or not balloon.visible: + return + # Deixa o jogo abrir o pause/menu principal mesmo durante o tutorial + if event.is_action_pressed("pause") or event.is_action_pressed("ui_cancel"): + return + # Bloqueia o resto para não "vazar" input pro gameplay get_viewport().set_input_as_handled() From 4625ded19b8f720a364f2e820e9ce16eac464293 Mon Sep 17 00:00:00 2001 From: iBerserker89 Date: Thu, 22 Jan 2026 13:15:06 -0300 Subject: [PATCH 2/3] permitir que o tutorial seja pulado --- project.godot | 9 +++-- scenes/game/tutorial_dialogue_balloon.tscn | 13 ++++++++ scenes/globals/gui_manager.tscn | 2 +- scripts/game/tutorial_dialogue_balloon.gd | 38 ++++++++++++++++++++-- scripts/globals/singleton.gd | 3 ++ scripts/menus/input_settings.gd | 22 ++----------- 6 files changed, 63 insertions(+), 24 deletions(-) diff --git a/project.godot b/project.godot index 2f0a84a..4a4925a 100644 --- a/project.godot +++ b/project.godot @@ -68,7 +68,7 @@ pause={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } -enter={ +accept={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] @@ -85,7 +85,12 @@ god={ } skip={ "deadzone": 0.2, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +] +} +close_tutorial={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } debug_open_portal={ diff --git a/scenes/game/tutorial_dialogue_balloon.tscn b/scenes/game/tutorial_dialogue_balloon.tscn index d6db275..86fd247 100644 --- a/scenes/game/tutorial_dialogue_balloon.tscn +++ b/scenes/game/tutorial_dialogue_balloon.tscn @@ -144,5 +144,18 @@ visible = false layout_mode = 2 text = "Response example" +[node name="CloseTutorialButton" type="Button" parent="Balloon"] +layout_mode = 0 +offset_left = 760.0 +offset_top = 495.0 +offset_right = 1032.0 +offset_bottom = 529.0 +size_flags_horizontal = 8 +size_flags_vertical = 0 +focus_mode = 0 +text = "Press Enter to close tutorial" +flat = true + [connection signal="gui_input" from="Balloon" to="." method="_on_balloon_gui_input"] [connection signal="response_selected" from="Balloon/ResponsesMenu" to="." method="_on_responses_menu_response_selected"] +[connection signal="pressed" from="Balloon/CloseTutorialButton" to="." method="_on_close_tutorial_button_pressed"] diff --git a/scenes/globals/gui_manager.tscn b/scenes/globals/gui_manager.tscn index e1e16b1..65fb5a6 100644 --- a/scenes/globals/gui_manager.tscn +++ b/scenes/globals/gui_manager.tscn @@ -603,7 +603,7 @@ anchor_bottom = 0.5 offset_left = -283.0 offset_top = -196.0 offset_right = 243.0 -offset_bottom = 157.0 +offset_bottom = 204.0 grow_horizontal = 2 grow_vertical = 2 focus_neighbor_top = NodePath("MarginContainer/VBoxContainer/BackToSettingsButton") diff --git a/scripts/game/tutorial_dialogue_balloon.gd b/scripts/game/tutorial_dialogue_balloon.gd index 4edc461..724c74d 100644 --- a/scripts/game/tutorial_dialogue_balloon.gd +++ b/scripts/game/tutorial_dialogue_balloon.gd @@ -1,6 +1,8 @@ class_name TutorialDialogueBalloon extends CanvasLayer ## A basic dialogue balloon for use with Dialogue Manager. +@onready var close_tutorial_button: Button = $Balloon/CloseTutorialButton + ## The action to use for advancing the dialogue @export var next_action: StringName = &"ui_accept" @@ -61,13 +63,13 @@ var mutation_cooldown: Timer = Timer.new() func _ready() -> void: balloon.hide() Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated) - # If the responses menu doesn't have a next action set, use this one if responses_menu.next_action.is_empty(): responses_menu.next_action = next_action - mutation_cooldown.timeout.connect(_on_mutation_cooldown_timeout) add_child(mutation_cooldown) + if is_instance_valid(close_tutorial_button): + _update_close_tutorial_text() #func _unhandled_input(_event: InputEvent) -> void: @@ -80,6 +82,11 @@ func _unhandled_input(event: InputEvent) -> void: # Deixa o jogo abrir o pause/menu principal mesmo durante o tutorial if event.is_action_pressed("pause") or event.is_action_pressed("ui_cancel"): return + # fechar tutorial (tecla configurável) + if event.is_action_pressed("close_tutorial"): + _on_close_tutorial_button_pressed() + get_viewport().set_input_as_handled() + return # Bloqueia o resto para não "vazar" input pro gameplay get_viewport().set_input_as_handled() @@ -193,3 +200,30 @@ func _on_responses_menu_response_selected(response: DialogueResponse) -> void: #endregion + + +func _on_close_tutorial_button_pressed() -> void: + # Não rodar tutorial de novo nessa run + Singleton.skip_tutorial = true + # Para qualquer avanço automático enquanto fecha + advance_after_blocking_mutation = false + is_waiting_for_input = false + balloon.hide() + # espera 1 frame pra não matar o state "self" + # enquanto o DialogueManager ainda está resolvendo/mutando. + await get_tree().process_frame + queue_free() + + + +func _update_close_tutorial_text() -> void: + var key_text := _get_action_first_bind_text(&"close_tutorial") + close_tutorial_button.text = "Press %s to close tutorial" % key_text + + +func _get_action_first_bind_text(action: StringName) -> String: + var events := InputMap.action_get_events(action) + if events.is_empty(): + return "?" + # pega o primeiro bind + return events[0].as_text().trim_suffix(" (Physical)") diff --git a/scripts/globals/singleton.gd b/scripts/globals/singleton.gd index f03f973..25130fd 100644 --- a/scripts/globals/singleton.gd +++ b/scripts/globals/singleton.gd @@ -104,6 +104,7 @@ func _close_all_dialogue_balloons() -> void: for balloon in get_tree().get_nodes_in_group("dialogue_balloon"): if is_instance_valid(balloon): balloon.visible = false + active_balloons.clear() ## Retoma o jogo a partir do estado de pausa. @@ -173,6 +174,8 @@ func open_credits() -> void: ## Abre o Main Menu. func open_main_menu() -> void: get_tree().paused = true + _close_all_dialogue_balloons() + _tutorial_running = false if gui_manager: if gui_manager.is_paused: if gui_manager.has_method("hide_pause_overlay_only"): diff --git a/scripts/menus/input_settings.gd b/scripts/menus/input_settings.gd index 22d5be8..faecf71 100644 --- a/scripts/menus/input_settings.gd +++ b/scripts/menus/input_settings.gd @@ -20,9 +20,11 @@ var input_actions := { "move_down": "Move down", "move_left": "Move left", "pause": "Pause", - "enter": "Enter", + "accept": "Accept", "boost": "Boost", "god": "God", + "skip": "Skip tutorial", + "close_tutorial": "Close tutorial", } @@ -55,7 +57,6 @@ func _on_input_button_pressed(button: Node, action: String) -> void: remap_armed = false action_to_remap = action remapping_button = button - # limpa estado visual anterior, se houver _set_error_ui(button, false) button.find_child("LabelInput").text = "Press key to bind..." @@ -72,20 +73,17 @@ func _on_input_button_pressed(button: Node, action: String) -> void: func _create_action_list() -> void: for item in action_list.get_children(): item.queue_free() - for action in input_actions: var button := input_button_scene.instantiate() var action_label := button.find_child("LabelAction") var input_label := button.find_child("LabelInput") action_label.text = input_actions[action] - # Pega os eventos atuais da ação. var events := InputMap.action_get_events(action) if events.size() > 0: input_label.text = events[0].as_text().trim_suffix(" (Physical)") else: input_label.text = "" - action_list.add_child(button) button.pressed.connect(_on_input_button_pressed.bind(button, action)) @@ -105,26 +103,21 @@ func _input(event: InputEvent) -> void: # só captura se estiver remapeando if not is_remapping or not remap_armed: return - # --- Allowlist dos tipos aceitos --- var is_key := event is InputEventKey var is_mouse_btn := event is InputEventMouseButton var is_pad_btn := event is InputEventJoypadButton - if not (is_key or is_mouse_btn or is_pad_btn): return - if is_key and not event.pressed: return if is_mouse_btn and not event.pressed: return if is_pad_btn and not event.pressed: return - # evita duplicidade por double click no mouse if is_mouse_btn and event.double_click: event.double_click = false - var conflict_with := _is_event_in_use(event, action_to_remap) if conflict_with != "": if remapping_button: @@ -132,25 +125,20 @@ func _input(event: InputEvent) -> void: if label: label.text = "Shortcut already in use. Try another one." _set_error_ui(remapping_button, true) - accept_event() return - # aplica o novo atalho InputMap.action_erase_events(action_to_remap) InputMap.action_add_event(action_to_remap, event) _update_action_list(remapping_button, event) - # restaura UI (cor padrão + largura original) if remapping_button: _set_error_ui(remapping_button, false) - # encerra remapeamento is_remapping = false remap_armed = false action_to_remap = "" remapping_button = null - accept_event() @@ -170,16 +158,13 @@ func _save_input_map() -> void: func _load_input_map() -> void: if not SaveManager: return - var profile := SaveManager.profile if typeof(profile) != TYPE_DICTIONARY or not profile.has("participants"): return - # chave usada pelo SaveManager var key := "input_settings" if not profile.participants.has(key): return - var bindings: Dictionary = profile.participants[key].get("bindings", {}) for action_name in input_actions.keys(): InputMap.action_erase_events(action_name) @@ -319,7 +304,6 @@ func _is_event_in_use(new_event: InputEvent, except_action: String) -> String: func _set_error_ui(line_button: Node, is_error: bool) -> void: var label: Label = line_button.find_child("LabelInput") var left_column: Control = line_button.find_child("LeftColumn") - if is_error: if label: label.add_theme_color_override("font_color", Color(1, 0.3, 0.1)) From 98ad3213313605ab6ebb0ec14c7dbc4209957798 Mon Sep 17 00:00:00 2001 From: iBerserker89 Date: Thu, 22 Jan 2026 15:58:21 -0300 Subject: [PATCH 3/3] refatorar --- scripts/game/tutorial_dialogue_balloon.gd | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/scripts/game/tutorial_dialogue_balloon.gd b/scripts/game/tutorial_dialogue_balloon.gd index 724c74d..fc8c7f7 100644 --- a/scripts/game/tutorial_dialogue_balloon.gd +++ b/scripts/game/tutorial_dialogue_balloon.gd @@ -72,11 +72,8 @@ func _ready() -> void: _update_close_tutorial_text() -#func _unhandled_input(_event: InputEvent) -> void: - ## Only the balloon is allowed to handle input while it's showing - #get_viewport().set_input_as_handled() func _unhandled_input(event: InputEvent) -> void: - # Se o balão n está visível, não bloqueia nada + # Se o balão nao está visível, não bloqueia nada if not is_instance_valid(balloon) or not balloon.visible: return # Deixa o jogo abrir o pause/menu principal mesmo durante o tutorial @@ -112,29 +109,22 @@ func start(dialogue_resource: DialogueResource, title: String, extra_game_states ## Apply any changes to the balloon given a new [DialogueLine]. func apply_dialogue_line() -> void: mutation_cooldown.stop() - is_waiting_for_input = false balloon.focus_mode = Control.FOCUS_ALL balloon.grab_focus() - character_label.visible = not dialogue_line.character.is_empty() character_label.text = tr(dialogue_line.character, "dialogue") - dialogue_label.hide() dialogue_label.dialogue_line = dialogue_line - responses_menu.hide() responses_menu.responses = dialogue_line.responses - # Show our balloon balloon.show() will_hide_balloon = false - dialogue_label.show() if not dialogue_line.text.is_empty(): dialogue_label.type_out() await dialogue_label.finished_typing - # Wait for input if dialogue_line.responses.size() > 0: balloon.focus_mode = Control.FOCUS_NONE @@ -182,13 +172,10 @@ func _on_balloon_gui_input(event: InputEvent) -> void: get_viewport().set_input_as_handled() dialogue_label.skip_typing() return - if not is_waiting_for_input: return if dialogue_line.responses.size() > 0: return - # When there are no response options the balloon itself is the clickable thing get_viewport().set_input_as_handled() - if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT: next(dialogue_line.next_id) elif event.is_action_pressed(next_action) and get_viewport().gui_get_focus_owner() == balloon: @@ -209,7 +196,7 @@ func _on_close_tutorial_button_pressed() -> void: advance_after_blocking_mutation = false is_waiting_for_input = false balloon.hide() - # espera 1 frame pra não matar o state "self" + # Espera 1 frame pra não matar o state "self" # enquanto o DialogueManager ainda está resolvendo/mutando. await get_tree().process_frame queue_free()