Add new test file for phase 19 scenery with unique identifier

This commit is contained in:
2026-10-17 12:53:38 -04:00
parent 36032ebb1e
commit a0510e6405
74 changed files with 40948 additions and 149 deletions
@@ -0,0 +1,54 @@
extends SceneTree
const SANDBOX_STAGE: PackedScene = preload("res://scenes/stage/sandbox_stage.tscn")
const STAGE_SCENERY_ARTWORK: Script = preload("res://scripts/stage/scenery/stage_scenery_artwork.gd")
const CAPTURE_PATH: String = "res://tests/phase19_selected_artwork_depth_inspector.png"
func _initialize() -> void:
call_deferred("_capture")
func _capture() -> void:
root.size = Vector2i(1280, 720)
var stage: SandboxStage = SANDBOX_STAGE.instantiate() as SandboxStage
root.add_child(stage)
await _wait_for_render_frames(3)
var artwork: StageSceneryArtwork = STAGE_SCENERY_ARTWORK.new() as StageSceneryArtwork
artwork.display_name = "Round Tree"
artwork.set_depth(&"near")
if not artwork.load_visual("res://content/props/art_tree-v2.prop"):
push_error("Depth inspector visual capture could not load the tree artwork.")
artwork.free()
stage.queue_free()
quit(1)
return
artwork.position = Vector2(-300.0, -120.0)
(stage.get_node("World") as Node2D).add_child(artwork)
var camera: Camera2D = stage.get_node("Camera2D") as Camera2D
camera.position = Vector2.ZERO
camera.zoom = Vector2(0.85, 0.85)
stage.call("_on_grid_toggled", false)
stage.call("_enter_scenery_edit_mode")
(stage.get("_selection") as StageSelection).select_only(artwork)
stage.call("_refresh_scenery_inspector")
await _wait_for_render_frames(6)
var inspector: StageInspectorPanel = stage.get("_scenery_inspector") as StageInspectorPanel
var section: Control = stage.get("_scenery_depth_section") as Control
if inspector == null or not inspector.visible or section == null or not section.visible:
push_error("Depth inspector visual capture did not show its selected-artwork section.")
stage.queue_free()
quit(1)
return
var capture: Image = root.get_texture().get_image()
var error: Error = capture.save_png(ProjectSettings.globalize_path(CAPTURE_PATH))
print("VISUAL selected artwork depth inspector path=%s size=%s save_error=%s" % [CAPTURE_PATH, str(capture.get_size()), str(error)])
stage.queue_free()
await process_frame
quit(0 if error == OK else 1)
func _wait_for_render_frames(count: int) -> void:
for _frame: int in count:
await process_frame
await RenderingServer.frame_post_draw
@@ -0,0 +1 @@
uid://c0sufu217116a
+146
View File
@@ -0,0 +1,146 @@
extends SceneTree
const SANDBOX_STAGE: PackedScene = preload("res://scenes/stage/sandbox_stage.tscn")
const PROP_EDITOR_SCENE: PackedScene = preload("res://scenes/editors/prop/prop_editor.tscn")
const STAGE_WALL_PANEL: Script = preload("res://scripts/stage/scenery/stage_wall_panel.gd")
const STAGE_SCENERY_ARTWORK: Script = preload("res://scripts/stage/scenery/stage_scenery_artwork.gd")
func _initialize() -> void:
call_deferred("_capture_scenes")
func _capture_scenes() -> void:
root.size = Vector2i(1280, 720)
var stage: SandboxStage = SANDBOX_STAGE.instantiate() as SandboxStage
root.add_child(stage)
await _wait_for_render_frames(3)
var loaded: bool = stage.call("_load_scene_from_path", "res://content/stages/starters/town_street.scene", false)
print("VISUAL town_street_loaded=", loaded)
stage.call("_on_grid_toggled", false)
await _wait_for_render_frames(4)
await _save_viewport_image("res://tests/phase19_visual_town_street.png")
loaded = stage.call("_load_scene_from_path", "res://content/stages/starters/living_room.scene", false)
print("VISUAL living_room_loaded=", loaded)
stage.call("_on_grid_toggled", false)
await _wait_for_render_frames(4)
await _save_viewport_image("res://tests/phase19_visual_living_room.png")
loaded = stage.call("_load_scene_from_path", "res://content/stages/starters/blank.scene", false)
print("VISUAL blank_loaded=", loaded)
stage.call("_on_grid_toggled", false)
await _wait_for_render_frames(2)
var world: Node2D = stage.get_node("World") as Node2D
var camera: Camera2D = stage.get_node("Camera2D") as Camera2D
camera.position = Vector2(0.0, -180.0)
camera.zoom = Vector2(0.72, 0.72)
var sky_view: StageSkyView = stage.get("_sky_view") as StageSkyView
sky_view.set_sky(&"sky_sunset")
var brick_wall: StageWallPanel = STAGE_WALL_PANEL.new() as StageWallPanel
brick_wall.position = Vector2(-430.0, -220.0)
brick_wall.configure(Vector2(390.0, 290.0), &"finish_wall_brick", Color.WHITE)
world.add_child(brick_wall)
var spawner: StageSpawner = stage.get("_spawner") as StageSpawner
var grass: TerrainBlock = spawner.spawn_terrain_asset("ground", Vector2(120.0, 18.0))
grass.set_finish(&"finish_ground_grass")
var tree: StageSceneryArtwork = STAGE_SCENERY_ARTWORK.new() as StageSceneryArtwork
tree.name = "ApprovedTreeArtwork"
tree.position = Vector2(-140.0, -150.0)
tree.display_name = "Tree"
tree.set_depth(&"near")
var tree_loaded: bool = tree.load_visual("res://content/props/art_tree-v2.prop")
world.add_child(tree)
var missing: StageSceneryArtwork = STAGE_SCENERY_ARTWORK.new() as StageSceneryArtwork
missing.name = "MissingArtworkExample"
missing.position = Vector2(100.0, -145.0)
missing.configure_missing("res://content/props/phase19_missing_art_demo.prop", "Tree", Rect2(Vector2(-52.0, -72.0), Vector2(104.0, 144.0)), &"near")
world.add_child(missing)
print("VISUAL approved_tree_loaded=", tree_loaded, " missing_marker=", missing.missing)
stage.call("_enter_scenery_edit_mode")
(stage.get("_selection") as StageSelection).select_only(missing)
stage.call("_refresh_scenery_inspector")
await _wait_for_render_frames(5)
await _save_viewport_image("res://tests/phase19_visual_finishes_and_repair.png")
var scenery_controller: StageSceneryController = stage.get("_scenery_controller") as StageSceneryController
if scenery_controller.begin_single_transform(missing, &"move", missing.global_position):
stage.call("_refresh_scenery_capture_bar")
await _wait_for_render_frames(3)
await _save_viewport_image("res://tests/phase19_visual_scenery_drag_cancel.png")
scenery_controller.cancel_single_transform()
stage.call("_refresh_scenery_capture_bar")
missing.queue_free()
camera.position = Vector2(0.0, -270.0)
tree.position = Vector2(-140.0, -95.0)
(stage.get("_selection") as StageSelection).select_only(tree)
stage.call("_refresh_scenery_inspector")
await _wait_for_render_frames(4)
await _save_viewport_image("res://tests/phase19_visual_scenery_selected_size.png")
var tree_bounds: Rect2 = tree.get_visual_world_bounds()
if scenery_controller.begin_single_transform(tree, &"resize", tree_bounds.position, tree_bounds.position):
stage.call("_refresh_scenery_capture_bar")
await _wait_for_render_frames(3)
await _save_viewport_image("res://tests/phase19_visual_scenery_resize_cancel.png")
scenery_controller.cancel_single_transform()
stage.call("_refresh_scenery_capture_bar")
var browser: StageAssetBrowser = stage.get("_asset_browser") as StageAssetBrowser
stage.call("_load_scene_from_path", "res://content/stages/starters/blank.scene", false)
(stage.get("_selection") as StageSelection).clear_selection()
stage.call("_exit_scenery_edit_mode")
stage.call("_on_grid_toggled", true)
scenery_controller.begin_wall(&"finish_wall_brick", Vector2(-180.0, -100.0))
await _wait_for_render_frames(3)
await _save_viewport_image("res://tests/phase19_visual_wall_panel_preview.png")
scenery_controller.exit_tool()
stage.call("_on_grid_toggled", false)
await _wait_for_render_frames(3)
var background_entries: Array[Dictionary] = browser.call("_scenery_background_entries") as Array[Dictionary]
var selector: AssetSelector = browser.get_selector()
selector.open("scenery", background_entries, "sky_sunset")
selector.call("_set_gallery_tab", "backgrounds")
await _wait_for_render_frames(4)
await _save_viewport_image("res://tests/phase19_visual_background_gallery.png")
stage.queue_free()
await process_frame
await process_frame
var prop_editor: PropEditor = PROP_EDITOR_SCENE.instantiate() as PropEditor
root.add_child(prop_editor)
await _wait_for_render_frames(3)
var prop_canvas: PropCanvas = prop_editor.get("_canvas") as PropCanvas
var fireplace_file: FileAccess = FileAccess.open("res://content/props/art_fireplace_mantel-v2.prop", FileAccess.READ)
if fireplace_file != null:
prop_editor.call("_apply_json_data", fireplace_file.get_as_text())
await _wait_for_render_frames(3)
prop_editor.call("_on_size_for_stage_pressed")
await _wait_for_render_frames(3)
await _save_viewport_image("res://tests/phase19_visual_size_for_stage_entry.png")
prop_editor.call("_finish_stage_size_preview", false)
prop_canvas.set_shape_data([{
"shape_type": "polygon",
"points": PackedVector2Array([Vector2(-50.0, -200.0), Vector2(50.0, -200.0), Vector2(50.0, 0.0), Vector2(-50.0, 0.0)]),
"fill_color": "#9D6A4A",
"outline_color": "#2D2532",
"outline_width": 4.0,
"closed": true,
}])
prop_editor.call("_on_size_for_stage_pressed")
prop_editor.call("_choose_stage_size", 1.0)
await _wait_for_render_frames(4)
await _save_viewport_image("res://tests/phase19_visual_size_for_stage.png")
prop_editor.queue_free()
await process_frame
quit(0)
func _wait_for_render_frames(count: int) -> void:
for _frame: int in count:
await process_frame
await RenderingServer.frame_post_draw
func _save_viewport_image(path: String) -> void:
await RenderingServer.frame_post_draw
var image: Image = root.get_texture().get_image()
var error: Error = image.save_png(ProjectSettings.globalize_path(path))
print("VISUAL capture path=", path, " size=", image.get_size(), " error=", error)
+1
View File
@@ -0,0 +1 @@
uid://d1551tbed0cpw
+5 -1
View File
@@ -22,7 +22,11 @@ func _run() -> void:
var builtins: Array[Dictionary] = PROP_LIBRARY.get_entries()
_check(builtins.size() == 4, "built-in templates remain four entries")
var entries: Array[Dictionary] = PROP_LIBRARY.scan()
_check(entries.size() == 8, "scan returns built-ins and checked-in custom props")
var scanned_briefcase: bool = false
for scanned_entry: Dictionary in entries:
if String(scanned_entry.get("path", "")) == "res://content/props/briefcase.prop":
scanned_briefcase = true
_check(entries.size() >= builtins.size() + 1 and scanned_briefcase, "scan returns built-ins and checked-in custom props")
var entry: Dictionary = PROP_LIBRARY.get_entry("res://content/props/briefcase.prop")
_check(not entry.is_empty() and bool(entry.get("is_custom", false)), "custom path resolves without selector scan")
_check((entry.get("shapes", []) as Array).size() > 1, "custom entry preserves the full shape stack")
+1 -1
View File
@@ -76,7 +76,7 @@ func _test_legacy_migration() -> void:
var migrated: Dictionary = STAGE_SCENE_IO._migrate_to_current(legacy)
var prop_entry: Dictionary = migrated["objects"][1]
var roles: Dictionary = prop_entry["roles"]
_expect(migrated["version"] == "1.3", "old stage version migrates to 1.3")
_expect(migrated["version"] == STAGE_SCENE_IO.FILE_VERSION, "old stage version migrates to the current schema")
_expect(prop_entry["stage_object_id"] == "prop_a", "1.0 prop IDs migrate from file IDs")
_expect(String(roles["start_motion"]) == PROP_STAGE_ROLES.START_PHYSICS, "legacy prop keeps dynamic start behavior")
_expect(bool(roles["blocks_stickmen"]) and bool(roles["legacy_draw_order"]), "legacy prop keeps collisions and draw order")
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
uid://crpxum1tvnphv
+11 -5
View File
@@ -10,7 +10,7 @@ func _init() -> void:
var world: Node2D = Node2D.new()
var spawner: StageSpawner = SPAWNER.new(world) as StageSpawner
var entries: Array[Dictionary] = spawner.get_palette_entries()
_check(entries.size() == 6, "six registry cards")
_check(entries.size() == 7, "six established cards plus the Phase 19 Scenery card")
_check(String(entries[0].get("placement_id", "")) == "ground", "registry id retained")
var card: AssetCard = CARD.new() as AssetCard
card.configure(entries[0])
@@ -20,7 +20,7 @@ func _init() -> void:
chrome.palette_activated.connect(_on_palette_activated)
chrome.build(entries)
var palette_buttons: Dictionary = chrome.get_palette_buttons()
_check(palette_buttons.size() == 6, "six stage palette cards")
_check(palette_buttons.size() == 7, "seven stage palette cards")
for entry: Dictionary in entries:
var placement_id: String = String(entry.get("placement_id", ""))
var asset_card: AssetCard = palette_buttons.get(placement_id) as AssetCard
@@ -28,6 +28,15 @@ func _init() -> void:
if asset_card == null:
continue
var icon: TextureRect = asset_card.thumbnail_rect()
var card_box: VBoxContainer = asset_card.get_child(0) as VBoxContainer
var glyph: Label = card_box.get_child(1) as Label
var title: Label = card_box.get_child(2) as Label
if placement_id == "scenery":
_check(not icon.visible and icon.texture == null, "Scenery retains its themed 🌄 fallback without a dedicated image asset")
_check(glyph.visible and glyph.text == "🌄", "Scenery fallback is kid-readable")
_check(title.text == String(entry.get("label", "")), "Scenery text label remains")
_check(asset_card.tooltip_text == String(entry.get("label", "")), "Scenery tooltip remains")
continue
_check(icon.visible, "%s image is visible" % placement_id)
var icon_texture: Texture2D = icon.texture as Texture2D
_check(icon_texture != null, "%s image texture is assigned" % placement_id)
@@ -37,9 +46,6 @@ func _init() -> void:
_check(icon_texture.get_width() <= 256 and icon_texture.get_height() <= 256, "%s import is capped at 256 px" % placement_id)
_check(icon.custom_minimum_size == Vector2(64.0, 64.0), "%s image area stays 64 px" % placement_id)
_check(icon.stretch_mode == TextureRect.STRETCH_KEEP_ASPECT_CENTERED, "%s image keeps aspect ratio" % placement_id)
var card_box: VBoxContainer = asset_card.get_child(0) as VBoxContainer
var glyph: Label = card_box.get_child(1) as Label
var title: Label = card_box.get_child(2) as Label
_check(not glyph.visible, "%s emoji fallback is hidden" % placement_id)
_check(title.text == String(entry.get("label", "")), "%s text label remains" % placement_id)
_check(asset_card.tooltip_text == String(entry.get("label", "")), "%s tooltip remains" % placement_id)