feat(studio,stacking,animations,collections): M16 ModBison Studio, object stacking, model animations, and poly collections for MVP 2
checkpoint / checkpoint (push) Successful in 6m32s
checkpoint / checkpoint (push) Successful in 6m32s
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user