extends SceneTree ## Headless smoke test for item stacking, surface alignment, elevation helpers, ## and hierarchical parenting persistence. var _failures: int = 0 var _checks: int = 0 func _init() -> void: print("--- Running Smoke Test: Object Stacking & Placement ---") test_stack_manager_math() test_editor_core_stacking() test_hierarchical_movement() test_elevation_and_drop_to_surface() test_chunk_codec_persistence() if _failures == 0: print("--- PASS: All %d stacking checks passed! ---" % _checks) quit(0) else: printerr("--- FAIL: %d failures out of %d checks ---" % [_failures, _checks]) quit(1) func _check(desc: String, cond: bool) -> void: _checks += 1 if cond: print(" ✓ %s" % desc) else: _failures += 1 printerr(" ✗ FAIL: %s" % desc) func _make_test_core() -> EditorCore: var payload := ChunkDataDecoder.ChunkPayload.new() var layer := ChunkDataDecoder.TileLayerData.new() layer.name = "ground" layer.layer_id = 0 layer.width = 16 layer.height = 16 layer.depth = 1 layer.tiles = PackedInt32Array() layer.tiles.resize(16 * 16) payload.tile_layers.append(layer) var model := ChunkEditorModel.new(payload) var catalog := EditorObjectCatalog.new() catalog.load_catalog() return EditorCore.new(model, catalog) const PlacementStackManagerClass = preload("res://scripts/editor/placement_stack_manager.gd") func test_stack_manager_math() -> void: print("Testing PlacementStackManager calculations...") var h_crate: float = PlacementStackManagerClass.get_height("crate_small") _check("Crate height is 1.0", is_equal_approx(h_crate, 1.0)) var h_chest: float = PlacementStackManagerClass.get_height("chest_01") _check("Chest height is 0.8", is_equal_approx(h_chest, 0.8)) var target := ChunkDataDecoder.WorldObjectData.new() target.id = "base_table" target.position = Vector3(2.0, 2.0, 0.0) target.properties = {"catalog_id": "table_wooden"} var stack_info: Dictionary = PlacementStackManagerClass.calculate_stack_transform( "chest_01", target, Vector3(2.0, 2.0, 0.0), Vector3(0, 0, 1), 0.0 ) _check("Stack transform is marked as stacked", stack_info["is_stacked"]) _check("Stack transform parent_id matches target", stack_info["parent_id"] == "base_table") _check("Stack transform Z elevation is 0.9 (table top)", is_equal_approx(stack_info["position"].z, 0.9)) func test_editor_core_stacking() -> void: print("Testing EditorCore.place_object_stacked...") var core := _make_test_core() var base_id := core.place_object("crate_small", Vector3(3.0, 3.0, 0.0)) _check("Placed base crate", base_id != "") var top_crate_id := core.place_object_stacked("crate_small", base_id, Vector3(3.0, 3.0, 0.0)) _check("Placed stacked crate", top_crate_id != "") var top_obj := core.model.find_object(top_crate_id) _check("Top crate found in model", top_obj != null) _check("Top crate Z position is 1.0", is_equal_approx(top_obj.position.z, 1.0)) _check("Top crate has parent_id set to base crate", top_obj.properties.get("parent_id") == base_id) var chest_id := core.place_object_stacked("chest_01", top_crate_id, Vector3(3.0, 3.0, 0.0)) _check("Placed stacked chest", chest_id != "") var chest_obj := core.model.find_object(chest_id) _check("Chest Z position is 2.0", is_equal_approx(chest_obj.position.z, 2.0)) _check("Chest parent_id set to top crate", chest_obj.properties.get("parent_id") == top_crate_id) func test_hierarchical_movement() -> void: print("Testing Hierarchical Movement Propagation...") var core := _make_test_core() var base_id := core.place_object("crate_small", Vector3(2.0, 2.0, 0.0)) var top_id := core.place_object_stacked("crate_small", base_id, Vector3(2.0, 2.0, 0.0)) # Move base crate by (+4, +3, 0) var ok_move := core.move_object(base_id, Vector3(6.0, 5.0, 0.0)) _check("Base crate move succeeded", ok_move) var base_obj := core.model.find_object(base_id) var top_obj := core.model.find_object(top_id) _check("Base crate moved to (6, 5, 0)", base_obj.position.is_equal_approx(Vector3(6.0, 5.0, 0.0))) _check("Top crate automatically moved with base to (6, 5, 1)", top_obj.position.is_equal_approx(Vector3(6.0, 5.0, 1.0))) # Test undo restores both core.undo() _check("Undo restored base crate to (2, 2, 0)", base_obj.position.is_equal_approx(Vector3(2.0, 2.0, 0.0))) _check("Undo restored top crate to (2, 2, 1)", top_obj.position.is_equal_approx(Vector3(2.0, 2.0, 1.0))) func test_elevation_and_drop_to_surface() -> void: print("Testing Elevation adjustments and Drop to Surface...") var core := _make_test_core() var base_id := core.place_object("crate_small", Vector3(4.0, 4.0, 0.0)) var floating_id := core.place_object("chest_01", Vector3(4.0, 4.0, 5.0)) # Floating high in the air var drop_ok := core.drop_object_to_surface(floating_id) _check("Drop to surface succeeded", drop_ok) var floating_obj := core.model.find_object(floating_id) _check("Dropped chest landed at Z=1.0 (on crate)", is_equal_approx(floating_obj.position.z, 1.0)) _check("Dropped chest acquired parent_id of base crate", floating_obj.properties.get("parent_id") == base_id) # Nudge elevation with adjust_object_elevation core.adjust_object_elevation(floating_id, 0.5) _check("Elevation nudged to Z=1.5", is_equal_approx(floating_obj.position.z, 1.5)) func test_chunk_codec_persistence() -> void: print("Testing Stacking & Parent ID Chunk Serialization...") var core := _make_test_core() var b_id := core.place_object("crate_small", Vector3(1.0, 1.0, 0.0)) var t_id := core.place_object_stacked("chest_01", b_id, Vector3(1.0, 1.0, 0.0)) # Encode chunk to binary var bytes := core.model.encode_binary() _check("Encoded chunk to bytes", bytes.size() > 0) # Decode chunk var decoded_payload: ChunkDataDecoder.ChunkPayload = ChunkDataDecoder.decode_binary(bytes) _check("Decoded chunk successfully", decoded_payload != null) _check("Decoded 2 objects", decoded_payload.objects.size() == 2) var dec_base: ChunkDataDecoder.WorldObjectData = null var dec_top: ChunkDataDecoder.WorldObjectData = null for obj in decoded_payload.objects: if obj.id == b_id: dec_base = obj elif obj.id == t_id: dec_top = obj _check("Base object restored with Z=0", dec_base != null and is_equal_approx(dec_base.position.z, 0.0)) _check("Top object restored with Z=1.0", dec_top != null and is_equal_approx(dec_top.position.z, 1.0)) _check("Top object restored with parent_id", dec_top != null and dec_top.properties.get("parent_id") == b_id)