class_name MatadorRagdoll extends RefCounted ## Builds the matador's physical-bone ragdoll. Body joints are cone-twist with ## limited swing/twist (DP-tunable) so the corpse keeps its silhouette instead of ## folding into a flat "deflated" pile; the cape stays free (PIN) so it drapes. const PhysicsLayers = preload("res://physics_layers.gd") # [bone, radius] → sphere collider; [bone, radius, height] → capsule. const _BONES: Array = [ ["matador", 0.18], ["COG", 0.12], ["chest", 0.13, 0.30], ["head", 0.14], ["collarbone_L", 0.06, 0.18], ["collarbone_R", 0.06, 0.18], ["arm_L", 0.07, 0.22], ["arm_R", 0.07, 0.22], ["forearm_L", 0.06, 0.22], ["forearm_R", 0.06, 0.22], ["hand_L", 0.05, 0.12], ["hand_R", 0.05, 0.12], ["leg_L", 0.10, 0.32], ["leg_R", 0.10, 0.32], ["shin_L", 0.08, 0.28], ["shin_R", 0.08, 0.28], ["foot_L", 0.07, 0.18], ["foot_R", 0.07, 0.18], ["Bone", 0.04, 0.30], ["Bone.001", 0.04, 0.34], ["Bone.002", 0.04, 0.30], ] # Reuse an existing simulator if the rig already has one, else build the bodies. static func build(skeleton: Skeleton3D) -> PhysicalBoneSimulator3D: for child: Node in skeleton.get_children(): if child is PhysicalBoneSimulator3D: (child as PhysicalBoneSimulator3D).active = false return child as PhysicalBoneSimulator3D var sim := PhysicalBoneSimulator3D.new() sim.active = false skeleton.add_child(sim) var swing := DP.f("mat_joint_swing") var twist := DP.f("mat_joint_twist") var damp := DP.f("mat_ragdoll_damp") for entry: Array in _BONES: var bname: String = entry[0] if skeleton.find_bone(bname) == -1: continue var pb := PhysicalBone3D.new() pb.bone_name = bname pb.mass = 0.4 pb.linear_damp = 0.1 # The ragdoll only simulates once the matador is dead, so it lives on the CORPSE # layer and masks to CORPSE alone: it rests on the ground/walls (which also carry # CORPSE) but the bull — which collides only with WORLD — passes straight through # the bodies instead of tripping over them. pb.collision_layer = PhysicsLayers.CORPSE pb.collision_mask = PhysicsLayers.CORPSE if bname.begins_with("Bone"): # Cape: floppy cloth — free swing, light, draggy, one-way collision (layer 0 # so nothing collides with it; it still drapes on the CORPSE-masked ground). pb.joint_type = PhysicalBone3D.JOINT_TYPE_PIN pb.mass = 0.15 pb.linear_damp = 0.5 pb.angular_damp = 0.7 pb.collision_layer = 0 else: # Body: cone-twist joints cap how far each bone bends from its rest # pose, so the silhouette holds; extra angular damp settles the flail. pb.joint_type = PhysicalBone3D.JOINT_TYPE_CONE pb.angular_damp = damp pb.set(&"joint_constraints/swing_span", swing) pb.set(&"joint_constraints/twist_span", twist) var cs := CollisionShape3D.new() if entry.size() >= 3: var cap := CapsuleShape3D.new() cap.radius = entry[1] cap.height = entry[2] cs.shape = cap else: var sph := SphereShape3D.new() sph.radius = entry[1] cs.shape = sph pb.add_child(cs) sim.add_child(pb) return sim