Files
Bullosseum/tests/ragdoll_throw_test.gd
T
richard 7b6a141767 Reorganize the codebase into role-based folders (snake_case)
Move the 40-odd root-level scripts and scenes into a tree that groups by role,
per the approved roadmap (WS2/WS3): actors/{player,matador,bear}, systems/{combat,
fx,input,debug,render,crowd,config}, ui/, props/ (was tscn_s/). Rename the shell
scene.tscn -> levels/game_shell.tscn and every PascalCase scene to snake_case
(Player.tscn -> player.tscn, MainMenu.tscn -> main_menu.tscn, etc.) so the
case-insensitive web/macOS filesystems can't alias two files together.

Done as a scripted move: each file travels with its .uid; every quoted res:// path
across scenes, scripts, .tres, and project.godot's 9 autoloads is rewritten; the
.godot uid/class cache is rebuilt from scratch. Scenes still resolve by uid, so the
rewrite is belt-and-suspenders. Lint globs (run_tests.sh + both CI workflows) now
walk the new dirs; an orphan animation_tree.gd.uid and 7 pre-existing trailing-
whitespace lints (barrel/torch, never covered by the old root-only glob) are cleaned.

Full suite green on both sides. Asset consolidation (Assets/Animations/fonts ->
art/) is deferred — moving imported .glb/.png/.ttf is safer done in the editor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017KoYBYQgRyY1zDHXMsXY3V
2026-09-06 00:04:41 +03:00

84 lines
2.4 KiB
GDScript

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://levels/game_shell.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