/extends SceneTree ## Ragdoll throw visual test — triggers ragdoll on the matador and captures a ## frame strip so you can inspect the throw arc and settling. ## ## Run (needs a display for real screenshots): ## godot --script tests/ragdoll_throw_test.gd ## ## Outputs: tests/output/ragdoll_throw/frame_00..09.png ## Each frame is 0.2 s apart → strip covers 2 s of flight and tumble. const OUTPUT_DIR := "res://tests/output/ragdoll_throw" const BULL_SPEED := 22.0 # simulate a solid charge const TRACKED_BONES: Array[StringName] = [&"chest", &"head", &"COG"] func _init() -> void: _run.call_deferred() func _run() -> void: DirAccess.make_dir_recursive_absolute(OUTPUT_DIR) var scene_res := load("res://scene.tscn") if not scene_res: push_error("ragdoll_throw_test: failed to load scene.tscn") quit(1) return var scene: Node = scene_res.instantiate() root.add_child(scene) # Let physics and AI settle await create_timer(0.8).timeout var matadors := get_nodes_in_group(&"matador") if matadors.is_empty(): push_error("ragdoll_throw_test: no matadors in scene") quit(1) return var mat := matadors[0] as Node3D var spawn := mat.global_position print("matador spawn: %s" % spawn) # Throw toward +Z with a realistic charge speed mat.call(&"_enter_ragdoll", Vector3(0.0, 0.0, 1.0), BULL_SPEED) print("ragdoll triggered bull_speed=%.1f" % BULL_SPEED) # Strip: 10 frames every 0.2 s for i: int in range(10): await create_timer(0.2).timeout var t := (i + 1) * 0.2 var img: Image = root.get_viewport().get_texture().get_image() if img: img.save_png(OUTPUT_DIR + "/frame_%02d.png" % i) var sim: Node = _find_class_recursive(mat, "PhysicalBoneSimulator3D") var bone_info := "" if sim: for bone_name: StringName in TRACKED_BONES: for child: Node in sim.get_children(): if child is PhysicalBone3D and \ (child as PhysicalBone3D).bone_name == bone_name: var bp: Vector3 = (child as PhysicalBone3D).global_position var dist: float = bp.distance_to(spawn) bone_info += " %s y=%.2f dist=%.2f" % [bone_name, bp.y, dist] print("t=%.1fs frame %02d%s" % [t, i, bone_info]) print("\nStrip saved → %s" % OUTPUT_DIR) quit(0) func _find_class_recursive(node: Node, class_name_str: String) -> Node: if node.get_class() == class_name_str: return node for child: Node in node.get_children(): var r := _find_class_recursive(child, class_name_str) if r: return r return null