diff --git a/BuildController.gd b/BuildController.gd index 1266c18..aba4f36 100644 --- a/BuildController.gd +++ b/BuildController.gd @@ -148,7 +148,7 @@ var pending_time: float = 0.0 func _ready() -> void: build_button.pressed.connect(_on_build_pressed) - build_close_button.pressed.connect(func(): build_menu.visible = false) + build_close_button.pressed.connect(_close_build_menu) build_mothership_button.pressed.connect(_on_place_mothership) build_crafter_button.pressed.connect(_on_place_crafter) build_command_button.pressed.connect(_on_place_command_center) @@ -394,6 +394,14 @@ func _on_place_command_center() -> void: _show_building_info(GameState.BUILDING_COMMAND_CENTER) +func _close_build_menu() -> void: + # The popup is a child panel of this menu; leaving it "open" while its + # parent is hidden is what blocked every button in the game. + info_popup.visible = false + pending_build_type = "" + build_menu.visible = false + + func _on_place_ship_lab() -> void: _show_building_info(GameState.BUILDING_SHIP_LAB) diff --git a/IconButton.gd b/IconButton.gd index 999e2fe..825cc9b 100644 --- a/IconButton.gd +++ b/IconButton.gd @@ -61,7 +61,13 @@ func _blocked_by_modal() -> bool: # there. So the panels stay transparent to input and the buttons check # instead. for menu in get_tree().get_nodes_in_group("modal_menus"): - if menu.visible and not menu.is_ancestor_of(self): + # is_visible_in_tree(), NOT visible. Hiding a parent leaves the + # child's own `visible` flag true, and the build menu's info popup is + # itself a modal panel - so closing the build menu with the X left an + # invisible popup still claiming to be open, and every button in the + # game was blocked from then on. That is not a state a player can + # get out of. + if menu.is_visible_in_tree() and not menu.is_ancestor_of(self): return true return false diff --git a/tests/test_modal_blocks_input.gd b/tests/test_modal_blocks_input.gd index 19c3e78..2aca0a9 100644 --- a/tests/test_modal_blocks_input.gd +++ b/tests/test_modal_blocks_input.gd @@ -132,6 +132,30 @@ func _process(delta: float) -> bool: quit(1) return true print("touch on the panel consumed, fleet target unchanged") + + # --- 5. a hidden parent must not leave a panel "open" ------- + # The build menu's info popup is itself a modal panel. Hiding the + # build menu leaves the popup's own `visible` flag true, so a + # guard testing `visible` rather than `is_visible_in_tree()` + # blocked EVERY button in the game from then on - with no way for + # a player to get out of it. Reported three times as "the buttons + # do nothing". + skill_menu.visible = false + var controller2: Node = scene.get_node("BuildController") + controller2._on_build_pressed() + controller2._show_building_info(GameState.BUILDING_SHIP_CRAFTER) + scene.get_node("UI/BuildMenu").visible = false + var info: Control = scene.get_node("UI/BuildMenu/InfoPopup") + print("popup after hiding its parent: visible=%s in_tree=%s" % [ + info.visible, info.is_visible_in_tree() + ]) + outside_fired = false + _tap(map_button) + if not outside_fired: + print("TEST_MODAL_BLOCKS_INPUT: FAIL (a panel hidden with its parent still blocks every button)") + quit(1) + return true + print("hidden-with-parent panel no longer blocks anything") print("TEST_MODAL_BLOCKS_INPUT: PASS") quit(0) return true