Unveränderter Ausgangsstand inklusive Masterprompt und Konzeptbildern unter assets/. .godot/ und docs/screenshots/ bleiben laut .gitignore unversioniert; letztere sind unter ../backups gesichert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
3.2 KiB
GDScript
60 lines
3.2 KiB
GDScript
extends Node
|
|
var checks: int = 0
|
|
var failures: Array[String] = []
|
|
func check(ok: bool, label: String) -> void:
|
|
checks += 1
|
|
if not ok:
|
|
failures.append(label)
|
|
printerr("RIG_FAIL: ",label)
|
|
func _ready() -> void:
|
|
call_deferred("run")
|
|
func run() -> void:
|
|
var a: Node3D = AssetLibrary.instantiate_fighter("w04")
|
|
var b: Node3D = AssetLibrary.instantiate_fighter("w04")
|
|
add_child(a)
|
|
add_child(b)
|
|
check(a.skeleton.get_bone_count()==27,"27 imported bones")
|
|
check(a.player!=b.player and a.skeleton!=b.skeleton,"independent skeleton and playback")
|
|
for clip: String in ["idle","start","walk","stop","attack_a","attack_b","hit","ability","death"]:
|
|
check(a.clips.has(clip),"imported clip "+clip)
|
|
check(a.clip_length(clip)>.3,"clip duration "+clip)
|
|
var left: int = a.skeleton.find_bone("Thigh.L")
|
|
var other: Quaternion = b.skeleton.get_bone_pose_rotation(left)
|
|
a.preview("walk",.35)
|
|
check(a.skeleton.get_bone_pose_rotation(left).angle_to(other)>.05,"walking deforms weighted leg")
|
|
check(b.skeleton.get_bone_pose_rotation(left).is_equal_approx(other),"second instance keeps pose")
|
|
a.preview("attack_a",.9)
|
|
var finger: int = a.skeleton.find_bone("Finger0.L")
|
|
a.preview("idle",0)
|
|
check(a.skeleton.get_bone_pose_rotation(finger).is_equal_approx(b.skeleton.get_bone_pose_rotation(finger)),"idle restores fingers after attack")
|
|
a.update_combat(1.0/60,0,true,.6,0,0,0,true)
|
|
check(a.current_clip=="attack_a" and absf(a.player.current_animation_position-.54)<.001,"first attack prepares before damage")
|
|
a.update_combat(1.0/60,0,true,0,1,0,0,true)
|
|
check(absf(a.player.current_animation_position-.9)<.001,"primary damage frame matches authored contact")
|
|
a.update_combat(1.0/60,0,true,.15,1,0,0,true)
|
|
check(a.player.current_animation_position>.9,"attack follow-through after damage")
|
|
a.update_combat(1.0/60,0,true,.65,1,0,0,true)
|
|
check(a.current_clip=="attack_b","alternating right attack")
|
|
var clock: float = a.player.current_animation_position
|
|
a.update_combat(0,0,true,.65,1,0,0,true)
|
|
check(is_equal_approx(a.player.current_animation_position,clock),"zero battle delta does not advance playback")
|
|
a.update_combat(1.0/60,0,true,.66,1,.5,.18,true)
|
|
check(a.current_clip=="attack_b","reactions preserve attack contact clock")
|
|
a.update_combat(1.0/60,0,false,0,1,0,0,false)
|
|
check(a.dead and a.current_clip=="death","death uses imported clip")
|
|
for i in range(180):a.update_combat(1.0/60,0,false,0,1,0,0,false)
|
|
check(absf(a.player.current_animation_position-a.clip_length("death"))<.001,"death holds final pose without looping")
|
|
var unit: Node3D = preload("res://scripts/combat/unit.gd").new()
|
|
add_child(unit)
|
|
unit.setup(DataCatalog.get_unit("w04"),1,0,false,Vector2i.ZERO,0,true)
|
|
unit.receive_damage(100000,0)
|
|
unit.refresh_visuals(.1)
|
|
check(not unit.alive and unit.visible and unit._model.dead,"dead unit remains visible for its death animation")
|
|
check(not unit._bars.visible and not unit._selection.visible,"dead health and selection are hidden")
|
|
unit.free()
|
|
a.free()
|
|
b.free()
|
|
var f := FileAccess.open("res://docs/phase11-rig-verification.json",FileAccess.WRITE)
|
|
f.store_string(JSON.stringify({"checks":checks,"failures":failures},"\t"));f.close()
|
|
print("RIG_PASS: ",checks," checks; ",failures.size()," failures")
|
|
await Soundscape.quit_game(0 if failures.is_empty() else 1)
|