102 lines
4.2 KiB
GDScript
102 lines
4.2 KiB
GDScript
extends SceneTree
|
|
|
|
const RIG_SCENE_PATH := "res://scenes/characters/stickman/stickman_rig.tscn"
|
|
const EXPECTED_UID := "uid://dr5ef3l2var2s"
|
|
|
|
var _checks := 0
|
|
var _failures := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
var scene_source := FileAccess.get_file_as_string(RIG_SCENE_PATH)
|
|
_check(scene_source.contains('uid="%s"' % EXPECTED_UID), "relocated scene retains its UID")
|
|
var packed := load(RIG_SCENE_PATH) as PackedScene
|
|
_check(packed != null, "relocated rig scene loads")
|
|
if packed == null:
|
|
_finish()
|
|
return
|
|
var rig := packed.instantiate() as StickmanRig
|
|
_check(rig != null, "scene root remains StickmanRig")
|
|
root.add_child(rig)
|
|
await process_frame
|
|
await physics_frame
|
|
var locomotion := rig.get_node_or_null("RigComponents/Locomotion") as StickmanLocomotion
|
|
var ragdoll := rig.get_node_or_null("RigComponents/Ragdoll") as StickmanRagdoll
|
|
var runner := rig.get_node_or_null("RigComponents/ActionRunner") as StickmanActionRunner
|
|
_check(locomotion != null, "typed Locomotion component resolves")
|
|
_check(ragdoll != null, "typed Ragdoll component resolves")
|
|
_check(runner != null, "typed ActionRunner component resolves")
|
|
_check(rig.get_locomotion() == locomotion, "root locomotion facade exposes authored component")
|
|
|
|
var head_look_at := rig._head_look_at as SkeletonModification2DLookAt
|
|
var head_bone := rig.get_node("Skeleton2D/Torso/Head") as Bone2D
|
|
var head_target := rig.get_node("IK_Targets/Head") as Marker2D
|
|
_check(head_look_at != null and head_look_at.enabled,
|
|
"FORWARD profile enables head marker tracking")
|
|
var head_rotation_before := head_bone.rotation
|
|
head_target.position += Vector2(-400.0, 180.0)
|
|
for frame in 4:
|
|
await process_frame
|
|
_check(absf(wrapf(head_bone.rotation - head_rotation_before, -PI, PI)) > 0.1,
|
|
"head bone follows a moved Head marker (before %.3f after %.3f target %s)"
|
|
% [head_rotation_before, head_bone.rotation, str(head_target.global_position)])
|
|
rig.set_facing_profile(StickmanRig.FacingProfile.LEFT)
|
|
_check(not head_look_at.enabled, "LEFT mirror disables non-mirror-invariant head LookAt")
|
|
rig.set_facing_profile(StickmanRig.FacingProfile.RIGHT)
|
|
_check(head_look_at.enabled, "RIGHT profile re-enables head marker tracking")
|
|
|
|
var left_bands: Dictionary = StickmanRagdoll.JOINT_BAND_LIMITS[StickmanRig.FacingProfile.LEFT]
|
|
var right_bands: Dictionary = StickmanRagdoll.JOINT_BAND_LIMITS[StickmanRig.FacingProfile.RIGHT]
|
|
for leg_key: String in ["left_lower_leg", "right_lower_leg"]:
|
|
var left_band: Dictionary = left_bands[leg_key]
|
|
var right_band: Dictionary = right_bands[leg_key]
|
|
_check(float(left_band["lower"]) < 0.0 and float(left_band["upper"]) >= 0.0,
|
|
"LEFT %s hinge includes straight and bends backward" % leg_key)
|
|
_check(float(right_band["lower"]) <= 0.0 and float(right_band["upper"]) > 0.0,
|
|
"RIGHT %s hinge includes straight and bends backward" % leg_key)
|
|
|
|
var assigned: Array[Dictionary] = [{"type": "wait", "duration": 0.25}]
|
|
rig.action_queue = assigned
|
|
_check(runner.action_queue.size() == 1, "direct root action_queue assignment delegates")
|
|
_check(rig.get_queue() == assigned, "root queue query preserves compatibility")
|
|
rig.queue_action({"type": "speak", "text": "modular", "duration": 0.1})
|
|
_check(runner.action_queue.size() == 2, "root queue mutation delegates")
|
|
|
|
rig.set_ragdoll(true)
|
|
await physics_frame
|
|
_check(rig.state == StickmanRig.RigState.RAGDOLL, "root remains ragdoll state authority")
|
|
_check(ragdoll.get_bodies().size() == 10, "ragdoll component owns ten physics bodies")
|
|
rig.snap_to_standing()
|
|
await physics_frame
|
|
_check(rig.state == StickmanRig.RigState.ANIMATED, "snap facade returns to ANIMATED")
|
|
_check(ragdoll.get_bodies().is_empty(), "snap destroys component-owned ragdoll bodies")
|
|
|
|
rig.queue_free()
|
|
await process_frame
|
|
await process_frame
|
|
var orphan := root.find_child("RagdollBodyContainer", true, false)
|
|
_check(orphan == null, "freeing rig leaves no ragdoll container orphan")
|
|
_finish()
|
|
|
|
|
|
func _check(condition: bool, label: String) -> void:
|
|
_checks += 1
|
|
if condition:
|
|
print("PASS: %s" % label)
|
|
else:
|
|
_failures += 1
|
|
push_error("FAIL: %s" % label)
|
|
|
|
|
|
func _finish() -> void:
|
|
if _failures == 0:
|
|
print("RESULT: ALL PASSED (%d assertions)" % _checks)
|
|
quit(0)
|
|
else:
|
|
print("RESULT: %d FAILURE(S) out of %d assertions" % [_failures, _checks])
|
|
quit(1)
|