extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_crafter_animation.gd # The ship crafter is drawn as a factory with a hull being assembled on the # line. Verifies: # 1. a crafter spawned the normal way (BuildingField, from GameState) # actually animates - the animation is what tells the player what the # building does. The mothership animates too (reactor core, orbiting # satellites) # 2. the command centre stays static, because per-frame redraws of art # that never changes were a measurable framerate cost earlier on # 3. the hull assembles progressively over the cycle rather than popping # in whole, and every piece is on before the assembly phase ends # 4. the cycle loops without anim_time growing without bound var scene: Node var game_state: Node var field: Node var stage: int = 0 var elapsed: float = 0.0 var crafter: Node2D var time_at_start: float = 0.0 func _initialize() -> void: game_state = root.get_node("GameState") game_state.rocks = 200 game_state.buildings.clear() game_state.extractors.clear() scene = load("res://Main.tscn").instantiate() root.add_child(scene) field = scene.get_node("BuildingField") func _find(type: String) -> Node2D: for b in field.spawned: if is_instance_valid(b) and b.building_type == type: return b return null func _process(delta: float) -> bool: elapsed += delta match stage: 0: scene.get_node("AsteroidField").set_process(false) scene.get_node("EnemySpawner").set_process(false) field.place(GameState.BUILDING_SHIP_CRAFTER, Vector2(500.0, 0.0)) field.place(GameState.BUILDING_MOTHERSHIP, Vector2(-500.0, 0.0)) field.place(GameState.BUILDING_COMMAND_CENTER, Vector2(0.0, 500.0)) elapsed = 0.0 stage = 1 return false 1: # Spawning is deferred, so give the tree a frame to attach them. if elapsed < 0.2: return false var crafter_building: Node2D = _find(GameState.BUILDING_SHIP_CRAFTER) if crafter_building == null: print("TEST_CRAFTER_ANIMATION: FAIL (crafter never spawned)") quit(1) return true crafter = crafter_building.visual var animated: bool = crafter.is_processing() var mothership_building: Node2D = _find(GameState.BUILDING_MOTHERSHIP) var mothership: Node2D = mothership_building.visual if mothership_building != null else null var mothership_animates: bool = mothership != null and mothership.is_processing() var command_building: Node2D = _find(GameState.BUILDING_COMMAND_CENTER) var command: Node2D = command_building.visual if command_building != null else null var command_static: bool = command != null and not command.is_processing() print("crafter animates=%s mothership animates=%s command centre static=%s" % [ animated, mothership_animates, command_static ]) if not (animated and mothership_animates and command_static): print("TEST_CRAFTER_ANIMATION: FAIL (wrong buildings are animating)") quit(1) return true # --- the hull assembles piece by piece ------------------------ var total: int = crafter.SHIP_PARTS.size() var counts: Array = [] for p in [0.0, 0.1, 0.35, 0.55, 0.9, 1.0]: counts.append(crafter.attached_part_count(p)) var progressive: bool = counts[0] == 0 and counts[-1] == total for i in range(1, counts.size()): if counts[i] < counts[i - 1]: progressive = false # Nothing may still be missing when the welding phase ends, or # the hull would launch half-built. var complete_before_launch: bool = crafter.attached_part_count(1.0) == total print("parts attached across the phase: %s (of %d) progressive=%s" % [ counts, total, progressive ]) if not (progressive and complete_before_launch and counts[1] < total): print("TEST_CRAFTER_ANIMATION: FAIL (hull does not build up gradually)") quit(1) return true time_at_start = crafter.anim_time elapsed = 0.0 stage = 2 return false 2: if elapsed < 0.4: return false var advanced: bool = crafter.anim_time != time_at_start var bounded: bool = crafter.anim_time >= 0.0 and crafter.anim_time < crafter.CYCLE print("anim_time %.3f -> %.3f (cycle %.2f) advanced=%s bounded=%s" % [ time_at_start, crafter.anim_time, crafter.CYCLE, advanced, bounded ]) if advanced and bounded: print("TEST_CRAFTER_ANIMATION: PASS") quit(0) else: print("TEST_CRAFTER_ANIMATION: FAIL (animation clock stalled or ran away)") quit(1) return true return false