extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_build_menu_hides_locked.gd # Forgejo #21: a structure the player has not researched should not be in the # build menu at all, rather than sitting there greyed out. # # The menu is a fixed grid in Main.tscn, so simply hiding entries would leave # holes where they used to be - it is repacked from whatever is researched. # # Verifies: # 1. with nothing researched only the mothership is offered (it is # deliberately never gated - it is where research happens) # 2. researching a structure adds it, and it is placed, not left at # whatever offsets it happened to have in the scene file # 3. no gaps: what is shown occupies consecutive grid slots # 4. an entry alone on its row is centred, not hanging in the left column # 5. researched-but-not-usable is still shown greyed (an extractor with no # planet under the fleet is "not now", not "not yet") var game_state: Node var scene: Node var controller: Node var stage: int = 0 var elapsed: float = 0.0 func _initialize() -> void: game_state = root.get_node("GameState") game_state.buildings.clear() game_state.extractors.clear() game_state.transporters.clear() game_state.reset_progression() game_state.rocks = 500 scene = load("res://world/Main.tscn").instantiate() root.add_child(scene) controller = scene.get_node("BuildController") func _shown() -> Array: var out: Array = [] for entry in controller.build_menu_entries: if entry[1].visible: out.append(entry) return out func _slots() -> Array: # (row, x) of every visible button, in menu order. var out: Array = [] for entry in _shown(): var b: Control = entry[1] out.append([b.offset_top, b.offset_left + (b.offset_right - b.offset_left) / 2.0]) return out func _process(delta: float) -> bool: elapsed += delta match stage: 0: if elapsed < 0.2: return false scene.get_node("AsteroidField").set_process(false) scene.get_node("EnemySpawner").set_process(false) # --- 1. nothing researched ---------------------------------- controller._refresh_build_status() var shown: Array = _shown() if shown.size() != 1 or shown[0][0] != GameState.BUILDING_MOTHERSHIP: var names: Array = [] for e in shown: names.append(e[0]) print("TEST_BUILD_MENU_HIDES_LOCKED: FAIL (unresearched structures on offer: %s)" % [names]) quit(1) return true # --- 4. and it is centred, being alone on its row ----------- var only: Array = _slots()[0] if absf(only[1]) > 0.01: print("TEST_BUILD_MENU_HIDES_LOCKED: FAIL (the only entry sits at x=%.1f instead of centred)" % only[1]) quit(1) return true print("nothing researched -> only the mothership, centred") # --- 2. research three more --------------------------------- game_state.unlocked = ["shipyards", "logistics", "coordination"] controller._refresh_build_status() stage = 1 return false 1: var shown2: Array = _shown() if shown2.size() != 4: print("TEST_BUILD_MENU_HIDES_LOCKED: FAIL (researched 3, expected 4 entries, got %d)" % shown2.size()) quit(1) return true for e in shown2: if e[0] == GameState.BUILDING_TRANSPORT_HUB: print("TEST_BUILD_MENU_HIDES_LOCKED: FAIL (the unresearched transport hub is still listed)") quit(1) return true # --- 3. consecutive slots, no holes ------------------------- var slots: Array = _slots() var rows: Array = controller.MENU_ROW_TOPS for i in range(slots.size()): var want_row: float = rows[i / 2] if absf(slots[i][0] - want_row) > 0.01: print("slot %d at y=%.1f, expected row %d at y=%.1f" % [i, slots[i][0], i / 2, want_row]) print("TEST_BUILD_MENU_HIDES_LOCKED: FAIL (a gap was left where a hidden entry used to be)") quit(1) return true var want_x: float = -controller.MENU_COL_X if i % 2 == 0 else controller.MENU_COL_X if absf(slots[i][1] - want_x) > 0.01: print("TEST_BUILD_MENU_HIDES_LOCKED: FAIL (entry %d at x=%.1f, expected %.1f)" % [i, slots[i][1], want_x]) quit(1) return true print("4 entries packed into consecutive slots, no gaps") # --- 5. researched but not usable is shown, greyed ---------- var extractor_shown: bool = false for e in shown2: if e[0] == GameState.BUILDING_EXTRACTOR: extractor_shown = true if e[1].enabled: print("TEST_BUILD_MENU_HIDES_LOCKED: FAIL (extractor offered as usable with no planet under the fleet)") quit(1) return true if not extractor_shown: print("TEST_BUILD_MENU_HIDES_LOCKED: FAIL (researched extractor hidden - 'not now' is not 'not yet')") quit(1) return true print("researched extractor shown but greyed, with no planet in reach") print("TEST_BUILD_MENU_HIDES_LOCKED: PASS") quit(0) return true return false