Implement Phase 19 updates and user/editor verification

- Updated architectural specifications for Phase 19 scenery, backgrounds, and surface finishes to reflect implementation status and user verification results.
- Revised UI/UX specifications for background ground and wall color, plain background, and starter scene composition to indicate successful implementation and verification.
- Enhanced scenery controller and sandbox stage scripts to improve eraser functionality and user interactions.
- Adjusted test cases to align with the latest changes in scenery and background implementations, ensuring all checks pass successfully.
- Added new buttons for wall area erasure in the scenery capture bar and updated layout handling for better user experience.
This commit is contained in:
2026-10-22 17:02:39 -04:00
parent 59f1817063
commit c6f3930dcb
21 changed files with 179 additions and 183 deletions
+20 -12
View File
@@ -116,8 +116,8 @@ func _run() -> void:
_expect(String(_stage.get("_scene_name")) == "Town Street", "choosing Town Street loads it as a named, unsaved scene")
_expect(String(_stage.get("_sky_id")) == "sky_cloudy_day", "Town Street starter selects the approved cloudy sky")
var world: Node2D = _stage.get_node("World") as Node2D
var sidewalk_bounds: Rect2 = _test_starter_surface_row(world, &"finish_ground_sidewalk", -16.0, "Town Street sidewalk")
var road_bounds: Rect2 = _test_starter_surface_row(world, &"finish_ground_asphalt", 16.0, "Town Street asphalt", 27, 2592.0)
var sidewalk_bounds: Rect2 = _test_starter_surface_row(world, &"finish_ground_sidewalk", -16.0, "Town Street sidewalk", 41, 3936.0)
var road_bounds: Rect2 = _test_starter_surface_row(world, &"finish_ground_asphalt", 16.0, "Town Street asphalt", 41, 3936.0)
_expect(is_equal_approx(road_bounds.position.y, sidewalk_bounds.end.y), "Town Street asphalt row meets the sidewalk row with no vertical gap")
_test_saved_starter_camera("res://content/stages/starters/town_street.scene", "Town Street")
await physics_frame
@@ -205,15 +205,18 @@ func _run() -> void:
selector.call("_set_gallery_tab", "backgrounds")
await process_frame
var background_grid: GridContainer = selector.find_child("GridContainer", true, false) as GridContainer
var surface_cards_row: HBoxContainer = selector.find_child("SurfaceCardsRow", true, false) as HBoxContainer
var background_preview_count: int = 0
var cloudy_card: AssetCard = _find_card_by_id(background_grid, "sky_cloudy_day")
if background_grid != null:
for card_node: Node in background_grid.get_children():
for card_container: Control in [background_grid, surface_cards_row]:
if card_container == null:
continue
for card_node: Node in card_container.get_children():
for card_content: Node in card_node.get_children():
for preview_node: Node in card_content.get_children():
if preview_node.get_script() == BACKGROUND_CARD_PREVIEW:
background_preview_count += 1
_expect(background_grid != null and background_grid.get_child_count() == 7 and background_preview_count == 7, "Plain Background, four Sky looks, and both surface setup cards render production-style previews")
_expect(background_grid != null and background_grid.get_child_count() == 5 and surface_cards_row != null and surface_cards_row.get_child_count() == 2 and background_preview_count == 7, "Plain Background, four Sky looks, and both surface setup cards render production-style previews")
var cloudy_status: Label = cloudy_card.find_child("StatusLabel", true, false) as Label if cloudy_card != null else null
_expect(cloudy_card != null and cloudy_card.button_pressed and cloudy_status != null and cloudy_status.text == "Current background", "gallery marks the loaded Town Street Sky as the current background")
var clear_sky_card: AssetCard = _find_card_by_id(background_grid, "sky_clear_day")
@@ -557,7 +560,9 @@ func _expect(condition: bool, description: String) -> void:
func _test_scene_color_compatibility() -> void:
var wall_document: Dictionary = {
"version": STAGE_SCENE_IO.FILE_VERSION,
"background_kind": "sky",
"sky_id": "sky_clear_day",
"plain_color": STAGE_SCENE_IO.DEFAULT_PLAIN_COLOR,
"objects": [{
"file_id": "wall_panel_1",
"type": "wall_panel",
@@ -787,8 +792,8 @@ func _test_wall_eraser(world: Node2D, controller: StageSceneryController) -> voi
cancel_press.pressed = true
controller.handle_mouse_button(cancel_press, Vector2(19850.0, 19950.0), false)
controller.handle_mouse_motion(Vector2(19950.0, 20050.0))
controller.cancel_active_gesture()
_expect(_count_walls_with_origin(world, Vector2(20000.0, 20000.0)) == 4 and document_changes[0] == 1, "Cancel discards the unfinished eraser rectangle without changing earlier openings")
controller.cancel_active_tool()
_expect(not controller.is_active() and _count_walls_with_origin(world, Vector2(20000.0, 20000.0)) == 4 and document_changes[0] == 1, "Cancel discards the unfinished eraser rectangle without changing earlier openings")
controller.exit_tool()
controller.begin_wall_eraser(Vector2(19850.0, 19950.0))
controller.handle_mouse_button(cancel_press, Vector2(19850.0, 19950.0), false)
@@ -865,6 +870,9 @@ func _is_grid_aligned(position: Vector2, grid_size: float) -> bool:
func _test_group_preview_save_gate(world: Node2D) -> void:
_stage.set_mode(SandboxStage.StageMode.PLAY)
await physics_frame
_stage.set_mode(SandboxStage.StageMode.EDIT)
var members: Array[Node2D] = []
for child: Node in world.get_children():
if child.get_script() == STAGE_SCENERY_ARTWORK:
@@ -972,8 +980,8 @@ func _test_missing_artwork_repair(world: Node2D, browser: StageAssetBrowser) ->
for _refresh_index: int in 3:
_stage.call("_refresh_scenery_capture_bar")
await process_frame
var action_buttons: Array[Node] = capture_bar.find_children("Action_*", "Button", true, false)
_expect(action_buttons.size() == 4, "repeated selection refresh keeps one capture button per action")
var erase_wall_buttons: Array[Node] = capture_bar.find_children("Action_erase_wall_area", "Button", true, false)
_expect(erase_wall_buttons.size() == 1, "repeated selection refresh keeps one wall-area erase action control")
var capture_rect: Rect2 = capture_bar.get_global_rect()
var repair_rect: Rect2 = repair_card.get_global_rect()
_expect(capture_bar.visible and capture_rect.end.x <= repair_rect.position.x, "1280x720 scenery capture bar reserves a clear lane beside the repair inspector (bar=%s inspector=%s viewport=%s)" % [capture_rect, repair_rect, get_root().size])
@@ -983,7 +991,7 @@ func _test_missing_artwork_repair(world: Node2D, browser: StageAssetBrowser) ->
await process_frame
capture_rect = capture_bar.get_global_rect()
repair_rect = repair_card.get_global_rect()
_expect(capture_bar.visible and repair_card.visible and capture_rect.end.x <= repair_rect.position.x, "narrow viewport keeps the stacked capture actions separate from the repair inspector")
_expect(capture_bar.visible and repair_card.visible and capture_rect.end.x <= repair_rect.position.x, "narrow viewport keeps the stacked capture actions separate from the repair inspector (bar=%s inspector=%s viewport=%s)" % [capture_rect, repair_rect, get_root().size])
root.size = Vector2i(1280, 720)
_stage.call("_refresh_scenery_inspector_layout")
await process_frame
@@ -1044,7 +1052,7 @@ func _test_single_scenery_transforms(world: Node2D, artwork_path: String) -> voi
artwork.display_name = "Phase 19 Transform Fixture"
artwork.set_depth(&"near")
world.add_child(artwork)
artwork.global_position = Vector2(2400.0, 1600.0)
artwork.global_position = Vector2(12000.0, 8000.0)
artwork.global_rotation = 0.19
artwork.scale = Vector2(1.25, 0.82)
var selection: StageSelection = _stage.get("_selection") as StageSelection
@@ -1068,7 +1076,7 @@ func _test_single_scenery_transforms(world: Node2D, artwork_path: String) -> voi
var cancel_button: Button = capture_bar.find_child("Action_cancel_single", true, false) as Button
_expect(capture_bar.visible and cancel_button != null and cancel_button.text.contains("Cancel"), "active scenery drag exposes a visible Cancel action")
var scenery_inspector: StageInspectorPanel = _stage.get("_scenery_inspector") as StageInspectorPanel
_expect(capture_bar.get_global_rect().end.x <= scenery_inspector.get_global_rect().position.x, "active scenery Cancel bar stays in the clear lane beside the inspector")
_expect(capture_bar.get_global_rect().end.x <= scenery_inspector.get_global_rect().position.x, "active scenery Cancel bar stays in the clear lane beside the inspector (bar=%s inspector=%s viewport=%s)" % [capture_bar.get_global_rect(), scenery_inspector.get_global_rect(), get_root().size])
_stage.call("_handle_scenery_edit_motion", original_bounds.get_center() + Vector2(37.0, 43.0))
var moved_bounds: Rect2 = artwork.get_visual_world_bounds()
_expect(_is_grid_aligned(moved_bounds.position, 20.0), "snapped scenery move aligns the visible AABB top-left to the grid")