feat(engine): add defeat-screen proof test suite

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-10 23:02:35 -07:00
parent 87b5bc287a
commit 8017152ccf
4 changed files with 211 additions and 0 deletions

View file

@ -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))

View file

@ -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")

View file

@ -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))

View file

@ -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")