Add new test file for phase 19 scenery with unique identifier
This commit is contained in:
@@ -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
|
||||
@@ -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)
|
||||
@@ -0,0 +1 @@
|
||||
uid://d1551tbed0cpw
|
||||
@@ -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")
|
||||
|
||||
@@ -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
@@ -0,0 +1 @@
|
||||
uid://crpxum1tvnphv
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user