extends SceneTree # A player who is short of research data is told where it comes from. # # It is the one currency with a non-obvious source: ore and gas are visibly # mined, but data comes off conquered worlds and nothing said so at the point # it mattered. The skill tree refused the node in SILENCE, and the ship lab # refused with "Research is already queued or complete" - which is not why it # refused, and sends the player looking at the wrong thing. # # Covers: # A. a doctrine that costs data the player has not got opens the explainer, # naming what was wanted and what they hold # B. the same from the ship lab, instead of blaming the queue # C. with the data in hand, the research goes through and no popup appears var game_state: Node var scene: Node var controller: Node var popup: Control var elapsed: float = 0.0 var stage: int = 0 func _initialize() -> void: game_state = root.get_node("GameState") game_state.buildings.clear() game_state.extractors.clear() game_state.reset_progression() game_state.reset_new_game() game_state.intro_active = false game_state.reset_quests() game_state.rocks = 5000 game_state.bank_inventory.set_amount(GameState.RESOURCE_GAS, 5000) game_state.bank_inventory.set_amount(GameState.RESOURCE_ENERGY, 5000) game_state.research_points = 0 game_state.unlocked = ["shipyards"] scene = load("res://world/Main.tscn").instantiate() root.add_child(scene) current_scene = scene controller = scene.get_node("BuildController") popup = scene.get_node("UI/ResearchPointsMenu") func _costly_doctrine() -> String: for node in GameState.SKILL_TREE: if int(node.get("research_points", 0)) > 0: return String(node.id) return "" func _process(delta: float) -> bool: elapsed += delta if elapsed < 0.4: return false match stage: 0: var doctrine: String = _costly_doctrine() if doctrine == "": return _fail("no doctrine costs research data, so this proves nothing") # Everything but the data: the prerequisite met and the ore paid, # so data is unambiguously the thing that is missing. game_state.unlocked = ["shipyards"] var requires: String = String(game_state.skill_node(doctrine).get("requires", "")) if requires != "" and not game_state.is_unlocked(requires): game_state.unlocked.append(requires) game_state.research_points = 0 var mothership_id: int = game_state.add_building( GameState.BUILDING_MOTHERSHIP, Vector2.ZERO) controller.skill_menu.building_id = mothership_id if popup.visible: return _fail("the explainer was open before anything was refused") controller._on_unlock_requested(doctrine) if not popup.visible: return _fail("a doctrine costing data was refused with no explanation") if game_state.is_unlocked(doctrine): return _fail("the doctrine was researched with no data at all") var said: String = popup.have_label.text if not said.contains("YOU HAVE 0"): return _fail("the explainer does not say what the player holds: '%s'" % said) if not popup.detail_label.text.to_lower().contains("scanner"): return _fail("the explainer never mentions how to find a lab world") print("skill tree: refused and explained - '%s'" % said) popup.close_menu() # --- C. with the data, it goes through, quietly --------------- game_state.research_points = 20 controller._on_unlock_requested(doctrine) if not game_state.is_unlocked(doctrine): return _fail("the doctrine was refused with data in hand") if popup.visible: return _fail("the explainer opened on a research that succeeded") print("with data in hand it goes through, and says nothing") elapsed = 0.0 stage = 1 return false 1: # --- B. the lab explains too, rather than blaming the queue --- game_state.research_points = 0 var lab_id: int = game_state.add_building(GameState.BUILDING_SHIP_LAB, Vector2(600.0, 0.0)) controller.lab_building_id = lab_id controller.lab_vessel = GameState.VESSEL_INTERCEPTOR controller.lab_choice = 0 var options: Array = controller._lab_options() if options.is_empty(): return _fail("the lab offers nothing, so this proves nothing") # Pick an option that actually costs data. for i in range(options.size()): if game_state.research_point_cost(String(options[i].id)) > 0: controller.lab_choice = i break if game_state.research_point_cost(String(options[controller.lab_choice].id)) <= 0: return _fail("no lab option costs research data, so this proves nothing") # The lab panel is open when the player presses buy - that is what # puts the explainer on top of another dialog, which is the whole # point of case D below. controller.lab_menu.open_menu() controller._on_lab_buy() if not popup.visible: return _fail("the lab refused for want of data with no explanation") if controller.lab_menu.status.text.contains("already queued"): return _fail("the lab still blames the queue: '%s'" % controller.lab_menu.status.text) print("ship lab: refused and explained, not blamed on the queue") # --- D. and the player can get back out of it ---------------- # The explainer is the only dialog that opens ON TOP of another # one, and IconButton refuses input to any button sitting behind # an open panel. That used to include this popup's own close # button, so the dialog could not be dismissed at all. Case B # above left the ship lab open underneath, which is the real # situation - assert that rather than arranging it. if not controller.lab_menu.visible: return _fail("the lab is not open underneath, so this proves nothing") var close: Control = popup.get_node("CloseButton") if close._blocked_by_modal(): return _fail("the explainer close button is blocked by the lab behind it - the dialog is inescapable") close.pressed.emit() if popup.visible: return _fail("pressing close left the explainer open") # The reverse must still hold: the lab's own buttons stay dead # while a dialog covers them. popup.open_for_shortfall(10, "anything") var lab_close: Control = controller.lab_menu.get_node("CloseButton") if not lab_close._blocked_by_modal(): return _fail("the lab buttons are live underneath an open dialog") popup.close_menu() print("close button works over the lab, and the lab stays blocked underneath") print("TEST_RESEARCH_POINTS_EXPLAINER: PASS") quit(0) return true return false func _fail(message: String) -> bool: print("TEST_RESEARCH_POINTS_EXPLAINER: FAIL (%s)" % message) quit(1) return true