Files
modbison/scripts/smoke_test_object_stacking.gd
T

156 lines
6.3 KiB
GDScript

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)