extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_research_double_tap.gd # Forgejo #83: the research tree's separate buy button is gone. Tapping a node # selects it; tapping the SAME node again buys it. # # Covers: # A. the buy button really is gone from the scene, not merely bypassed - a # leftover would still be tappable and would still be the thing a player # reaches for # B. the first tap selects and does NOT buy, so browsing the tree is free # C. a second tap on the same node buys it # D. moving to a DIFFERENT node resets that - two taps on two nodes must # never add up to a purchase # E. tapping an unaffordable node twice buys nothing and says why var game_state: Node var main: Node var tree_view: Node var controller: Node var stage: int = 0 var elapsed: float = 0.0 const NODE_ID := "shipyards" func _initialize() -> void: game_state = root.get_node("GameState") game_state.buildings.clear() game_state.extractors.clear() game_state.reset_progression() main = load("res://world/Main.tscn").instantiate() root.add_child(main) current_scene = main controller = main.get_node("BuildController") tree_view = main.get_node("UI/SkillMenu/Tree") func _tap(id: String) -> void: # Straight at the node's own position in the tree's local space, so this # exercises the same hit test a finger does. var node: Dictionary = game_state.skill_node(id) var event := InputEventScreenTouch.new() event.index = 1 event.pressed = true event.position = tree_view.global_position + tree_view.node_screen_pos(node) tree_view.handle_touch(event) var release := InputEventScreenTouch.new() release.index = 1 release.pressed = false release.position = event.position tree_view.handle_touch(release) func _process(delta: float) -> bool: elapsed += delta if stage != 0: return false if elapsed < 0.3: return false stage = 1 # --- A. the button is gone --------------------------------------------- if main.get_node_or_null("UI/SkillMenu/UnlockButton") != null: return _fail("the research panel still has an UnlockButton node") print("the separate buy button is gone from the panel") main.get_node("UI/SkillMenu").visible = true tree_view.visible = true tree_view.reset_scroll() game_state.unlocked.clear() game_state.research_points = 99 game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 500) game_state.fleet_inventory.set_amount(GameState.RESOURCE_ORE, 0) var cost: int = int(game_state.skill_node(NODE_ID).cost) var before: int = game_state.available_ore() # --- B. the first tap only selects ------------------------------------- _tap(NODE_ID) if tree_view.selected_id != NODE_ID: return _fail("the first tap did not select the node") if game_state.is_unlocked(NODE_ID): return _fail("the first tap bought the research - browsing the tree must be free") if game_state.available_ore() != before: return _fail("the first tap spent %d" % (before - game_state.available_ore())) print("first tap selects and spends nothing") # --- C. the second tap buys -------------------------------------------- _tap(NODE_ID) if not game_state.is_unlocked(NODE_ID): return _fail("a second tap on the same node did not buy it") if game_state.available_ore() != before - cost: return _fail("bought for %d, expected %d" % [before - game_state.available_ore(), cost]) print("second tap on the same node buys it (%d ore)" % cost) # --- D. two taps on two nodes are not a purchase ----------------------- var first: String = "" var second: String = "" for node in GameState.SKILL_TREE: var id: String = String(node.id) if game_state.is_unlocked(id) or not game_state.prerequisite_met(id): continue if first == "": first = id elif second == "": second = id break if second == "": return _fail("could not find two unlocked-but-available nodes for the cross-node case") var ore_before: int = game_state.available_ore() _tap(first) _tap(second) if game_state.is_unlocked(first) or game_state.is_unlocked(second): return _fail("tapping two different nodes bought one of them") if game_state.available_ore() != ore_before: return _fail("tapping two different nodes spent %d" % (ore_before - game_state.available_ore())) print("one tap each on two nodes buys neither") # --- E. unaffordable stays unaffordable -------------------------------- game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 0) game_state.fleet_inventory.set_amount(GameState.RESOURCE_ORE, 0) _tap(second) _tap(second) if game_state.is_unlocked(second): return _fail("a node was bought with no ore at all") controller._refresh_skill_menu(second) if controller.skill_status.text == "": return _fail("an unaffordable node was refused silently") print("unaffordable node refused, and says so: '%s'" % controller.skill_status.text) print("TEST_RESEARCH_DOUBLE_TAP: PASS") quit(0) return true func _fail(message: String) -> bool: print("TEST_RESEARCH_DOUBLE_TAP: FAIL (%s)" % message) quit(1) return true