From 3297d82f88b9a6631a6d9dd545c058e901e767ec Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 7 Apr 2026 17:50:32 -0700 Subject: [PATCH] =?UTF-8?q?test(scenes):=20=E2=9C=85=20Add=20test=20scene?= =?UTF-8?q?=20and=20script=20to=20validate=20crafting=20completion=20proof?= =?UTF-8?q?=20in=20game=20engine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../scenes/tests/crafting_complete_proof.gd | 121 ++++++++++++++++++ .../scenes/tests/crafting_complete_proof.tscn | 6 + 2 files changed, 127 insertions(+) create mode 100644 src/game/engine/scenes/tests/crafting_complete_proof.gd create mode 100644 src/game/engine/scenes/tests/crafting_complete_proof.tscn diff --git a/src/game/engine/scenes/tests/crafting_complete_proof.gd b/src/game/engine/scenes/tests/crafting_complete_proof.gd new file mode 100644 index 00000000..086587e3 --- /dev/null +++ b/src/game/engine/scenes/tests/crafting_complete_proof.gd @@ -0,0 +1,121 @@ +extends Node +## Crafting complete modal + unit panel item slot proof — Task 5.2. +## Mounts the unit_panel and crafting_complete_modal, seeds a stub Dwarven +## Warrior squad with iron_axe already equipped, selects it (unit_panel shows +## the item slot), then emits EventBus.item_crafted to pop the modal with +## dwarven_plate and the eligible squad in the list. Captures + quits. + +const UnitScript: GDScript = preload("res://engine/src/entities/unit.gd") +const UnitPanelScene: PackedScene = preload( + "res://engine/scenes/hud/unit_panel.tscn" +) +const CraftingModalScene: PackedScene = preload( + "res://engine/scenes/hud/crafting_complete_modal.tscn" +) + +const OUTPUT_DIR: String = "user://screenshots" +const SCREENSHOT_NAME: String = "items_crafting_proof" +const CAPTURE_DELAY: float = 0.7 + + +## Extends Unit so crafting_complete_modal's `is UnitScript` check passes. +## Implements the getters/fields unit_panel.gd reads when it takes the +## UnitScript branch. Unit.gd itself is a 2-line stub in this repo, so +## the proof scene must supply everything the UI reads. +class UnitStub: + extends Unit + var type_id: String = "dwarven_warrior" + var combat_type: String = "melee" + var hp: int = 10 + var max_hp: int = 10 + var movement_remaining: int = 2 + var position: Vector2i = Vector2i(3, 5) + var tier: int = 2 + var equipped_items: Array = [] + func get_attack() -> int: + return 4 + func get_defense() -> int: + return 3 + func get_movement() -> int: + return 2 + func is_civilian() -> bool: + return false + func has_keyword(_keyword: String) -> bool: + return false + func get_combat_type() -> String: + return "melee" + func get_name() -> String: + return "Dwarven Warrior" + + +## Minimal player stub for the modal's `_populate_squads` lookup. +class PlayerStub: + extends RefCounted + var index: int = 0 + var units: Array = [] + + +var _unit_panel: PanelContainer = null +var _crafting_modal: CanvasLayer = null +var _player: RefCounted = null +var _squad: UnitScript = null +var _captured: bool = false + + +func _ready() -> void: + ThemeVocabulary.load_vocabulary("age-of-dwarves") + DataLoader.load_theme("age-of-dwarves") + _seed() + _mount_ui() + EventBus.unit_selected.emit(_squad) + await get_tree().create_timer(0.1).timeout + EventBus.item_crafted.emit("dwarven_plate", null, _player) + await get_tree().create_timer(CAPTURE_DELAY).timeout + _capture_and_quit() + + +func _seed() -> void: + var squad: UnitStub = UnitStub.new() + squad.equipped_items.append( + {"item_id": "iron_axe", "charges_remaining": -1} + ) + _squad = squad + + var player: PlayerStub = PlayerStub.new() + player.units.append(squad) + _player = player + + +func _mount_ui() -> void: + _unit_panel = UnitPanelScene.instantiate() as PanelContainer + # unit_panel expects a parent CanvasLayer anchor; wrap it for visibility. + var layer: CanvasLayer = CanvasLayer.new() + layer.layer = 3 + add_child(layer) + layer.add_child(_unit_panel) + _unit_panel.position = Vector2(24, 420) + _unit_panel.custom_minimum_size = Vector2(320, 260) + + _crafting_modal = CraftingModalScene.instantiate() as CanvasLayer + add_child(_crafting_modal) + + +func _capture_and_quit() -> void: + if _captured: + return + _captured = true + + DirAccess.make_dir_recursive_absolute(OUTPUT_DIR) + var viewport: Viewport = get_viewport() + var tex: ViewportTexture = viewport.get_texture() + if tex == null: + push_error("[crafting_proof] viewport texture null (headless?)") + get_tree().quit() + return + var image: Image = tex.get_image() + var path: String = "%s/%s.png" % [OUTPUT_DIR, SCREENSHOT_NAME] + image.save_png(path) + print("[crafting_proof] screenshot saved: ", path) + + await get_tree().create_timer(0.1).timeout + get_tree().quit() diff --git a/src/game/engine/scenes/tests/crafting_complete_proof.tscn b/src/game/engine/scenes/tests/crafting_complete_proof.tscn new file mode 100644 index 00000000..1692c794 --- /dev/null +++ b/src/game/engine/scenes/tests/crafting_complete_proof.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://craftingproof001"] + +[ext_resource type="Script" path="res://engine/scenes/tests/crafting_complete_proof.gd" id="1"] + +[node name="CraftingCompleteProof" type="Node"] +script = ExtResource("1")