From 8017152ccf4eadb2fca3391126e53942aa0230c8 Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 10 May 2026 23:02:35 -0700 Subject: [PATCH] =?UTF-8?q?feat(engine):=20=E2=9C=85=20add=20defeat-screen?= =?UTF-8?q?=20proof=20test=20suite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../scenes/tests/defeat_screen_proof.gd | 102 ++++++++++++++++++ .../scenes/tests/defeat_screen_proof.tscn | 6 ++ .../scenes/tests/victory_screen_proof.gd | 97 +++++++++++++++++ .../scenes/tests/victory_screen_proof.tscn | 6 ++ 4 files changed, 211 insertions(+) create mode 100644 src/game/engine/scenes/tests/defeat_screen_proof.gd create mode 100644 src/game/engine/scenes/tests/defeat_screen_proof.tscn create mode 100644 src/game/engine/scenes/tests/victory_screen_proof.gd create mode 100644 src/game/engine/scenes/tests/victory_screen_proof.tscn diff --git a/src/game/engine/scenes/tests/defeat_screen_proof.gd b/src/game/engine/scenes/tests/defeat_screen_proof.gd new file mode 100644 index 00000000..0f139e35 --- /dev/null +++ b/src/game/engine/scenes/tests/defeat_screen_proof.gd @@ -0,0 +1,102 @@ +extends Node2D +## p2-67 follow-up — Self-capturing defeat-screen proof. Mirrors +## victory_screen_proof but emits the defeat path (player 0 loses while +## player 1 takes the lead). + +const DefeatScreenScene: PackedScene = preload( + "res://engine/scenes/menus/defeat_screen.tscn" +) +const PlayerScript: GDScript = preload("res://engine/src/entities/player.gd") +const CityScript: GDScript = preload("res://engine/src/entities/city.gd") + +const OUTPUT_DIR: String = "user://screenshots" +const VIEWPORT_SIZE: Vector2i = Vector2i(1920, 1080) + +var _captured: bool = false + + +func _ready() -> void: + DisplayServer.window_set_size(VIEWPORT_SIZE) + get_viewport().size = VIEWPORT_SIZE + RenderingServer.set_default_clear_color(Color(0.04, 0.02, 0.02)) + + DataLoader.load_theme("age-of-dwarves") + DataLoader.load_world("earth") + ThemeAssets.set_theme("age-of-dwarves") + ThemeVocabulary.load_vocabulary("age-of-dwarves") + + _seed_players() + GameState.turn_number = 64 + GameState.current_seed = 42 + + var screen: CanvasLayer = DefeatScreenScene.instantiate() as CanvasLayer + add_child(screen) + await get_tree().process_frame + await get_tree().process_frame + + # Defeat screen listens to EventBus.player_eliminated for the human + + # falls back to a direct show() call from VictoryManager. Emit the + # elimination so the screen wakes up. + if EventBus.has_signal("player_eliminated"): + EventBus.player_eliminated.emit(0) + # Also emit victory_achieved for the surviving leader so the screen + # can read the winner banner from GameState. + EventBus.victory_achieved.emit(1, "domination") + + for _i: int in range(12): + await get_tree().process_frame + await get_tree().create_timer(0.8).timeout + RenderingServer.force_draw(false, 0.0) + await get_tree().process_frame + _capture() + get_tree().quit() + + +func _seed_players() -> void: + GameState.players.clear() + for i: int in range(3): + var p: PlayerScript = PlayerScript.new() + p.player_name = ["Karak Ankor", "Goldvein", "Blackhammer"][i] + p.race_id = "dwarf" + p.index = i + p.is_human = (i == 0) + p.color = [ + Color(0.95, 0.65, 0.15), + Color(0.95, 0.85, 0.25), + Color(0.45, 0.25, 0.85), + ][i] + # Player 0 (human) loses all cities; AI players carry the leader recap. + if i > 0: + var city: CityScript = CityScript.new("p%d_capital" % i) + city.owner_index = i + city.position = Vector2i(2 + i * 6, 3) + city.city_name = "%s Capital" % p.player_name + city.set_population(6 + (2 - i) * 2) + p.cities.append(city) + GameState.players.append(p) + + +func _capture() -> void: + if _captured: + return + _captured = true + DirAccess.make_dir_recursive_absolute( + ProjectSettings.globalize_path(OUTPUT_DIR) + ) + var image: Image = get_viewport().get_texture().get_image() + if image == null: + push_error("defeat_screen_proof: viewport image null") + return + var timestamp: String = Time.get_datetime_string_from_system().replace( + ":", "-" + ).replace("T", "_") + var path: String = "%s/defeat_screen_proof_%s.png" % [OUTPUT_DIR, timestamp] + var abs_path: String = ProjectSettings.globalize_path(path) + var err: Error = image.save_png(abs_path) + if err == OK: + print("SCREENSHOT_PATH:%s" % abs_path) + print("defeat_screen_proof: %dx%d saved" % [ + image.get_width(), image.get_height() + ]) + else: + push_error("defeat_screen_proof: save failed: %s" % error_string(err)) diff --git a/src/game/engine/scenes/tests/defeat_screen_proof.tscn b/src/game/engine/scenes/tests/defeat_screen_proof.tscn new file mode 100644 index 00000000..15953082 --- /dev/null +++ b/src/game/engine/scenes/tests/defeat_screen_proof.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://engine/scenes/tests/defeat_screen_proof.gd" id="1"] + +[node name="DefeatScreenProof" type="Node2D"] +script = ExtResource("1") diff --git a/src/game/engine/scenes/tests/victory_screen_proof.gd b/src/game/engine/scenes/tests/victory_screen_proof.gd new file mode 100644 index 00000000..94346cad --- /dev/null +++ b/src/game/engine/scenes/tests/victory_screen_proof.gd @@ -0,0 +1,97 @@ +extends Node2D +## p2-67 follow-up — Self-capturing victory-screen proof. +## +## Seeds enough `GameState` for the screen's per-player recap table, then +## emits `EventBus.victory_achieved(0, "domination")` and captures the +## resulting overlay. Adds the screen as a child (not via change_scene) +## so the framebuffer stays fresh under headless llvmpipe. + +const VictoryScreenScene: PackedScene = preload( + "res://engine/scenes/menus/victory_screen.tscn" +) +const PlayerScript: GDScript = preload("res://engine/src/entities/player.gd") +const CityScript: GDScript = preload("res://engine/src/entities/city.gd") + +const OUTPUT_DIR: String = "user://screenshots" +const VIEWPORT_SIZE: Vector2i = Vector2i(1920, 1080) + +var _captured: bool = false + + +func _ready() -> void: + DisplayServer.window_set_size(VIEWPORT_SIZE) + get_viewport().size = VIEWPORT_SIZE + RenderingServer.set_default_clear_color(Color(0.04, 0.03, 0.02)) + + DataLoader.load_theme("age-of-dwarves") + DataLoader.load_world("earth") + ThemeAssets.set_theme("age-of-dwarves") + ThemeVocabulary.load_vocabulary("age-of-dwarves") + + _seed_players() + GameState.turn_number = 87 + GameState.current_seed = 42 + + var screen: CanvasLayer = VictoryScreenScene.instantiate() as CanvasLayer + add_child(screen) + await get_tree().process_frame + await get_tree().process_frame + + EventBus.victory_achieved.emit(0, "domination") + + for _i: int in range(12): + await get_tree().process_frame + await get_tree().create_timer(0.8).timeout + RenderingServer.force_draw(false, 0.0) + await get_tree().process_frame + _capture() + get_tree().quit() + + +func _seed_players() -> void: + GameState.players.clear() + for i: int in range(3): + var p: PlayerScript = PlayerScript.new() + p.player_name = ["Karak Ankor", "Goldvein", "Blackhammer"][i] + p.race_id = "dwarf" + p.index = i + p.is_human = (i == 0) + p.color = [ + Color(0.95, 0.65, 0.15), + Color(0.95, 0.85, 0.25), + Color(0.45, 0.25, 0.85), + ][i] + # Give each player a dummy capital so the stats grid renders rows. + var city: CityScript = CityScript.new("p%d_capital" % i) + city.owner_index = i + city.position = Vector2i(2 + i * 6, 3) + city.city_name = "%s Capital" % p.player_name + city.set_population(5 + (3 - i) * 2) + p.cities.append(city) + GameState.players.append(p) + + +func _capture() -> void: + if _captured: + return + _captured = true + DirAccess.make_dir_recursive_absolute( + ProjectSettings.globalize_path(OUTPUT_DIR) + ) + var image: Image = get_viewport().get_texture().get_image() + if image == null: + push_error("victory_screen_proof: viewport image null") + return + var timestamp: String = Time.get_datetime_string_from_system().replace( + ":", "-" + ).replace("T", "_") + var path: String = "%s/victory_screen_proof_%s.png" % [OUTPUT_DIR, timestamp] + var abs_path: String = ProjectSettings.globalize_path(path) + var err: Error = image.save_png(abs_path) + if err == OK: + print("SCREENSHOT_PATH:%s" % abs_path) + print("victory_screen_proof: %dx%d saved" % [ + image.get_width(), image.get_height() + ]) + else: + push_error("victory_screen_proof: save failed: %s" % error_string(err)) diff --git a/src/game/engine/scenes/tests/victory_screen_proof.tscn b/src/game/engine/scenes/tests/victory_screen_proof.tscn new file mode 100644 index 00000000..75f60cbf --- /dev/null +++ b/src/game/engine/scenes/tests/victory_screen_proof.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3] + +[ext_resource type="Script" path="res://engine/scenes/tests/victory_screen_proof.gd" id="1"] + +[node name="VictoryScreenProof" type="Node2D"] +script = ExtResource("1")