CharacterHitboxes.cs
using Sandbox;
namespace PaintballBaddies;
/// <summary>Native animated hitboxes for the adapted Citizen skeleton.</summary>
public sealed class CharacterHitboxes : Component
{
private SkinnedModelRenderer body;
public GameObject Actor { get; private set; }
public int Count { get; private set; }
private readonly List<GameObject> boxes=new();
public void Configure(SkinnedModelRenderer renderer,GameObject actor){body=renderer;Actor=actor;}
protected override void OnUpdate()
{
if(Count>0 || !body.IsValid() || !body.Model.IsValid())return;
Add("pelvis","spine_0",7.5f);
Add("spine_0","spine_2",6.5f);
Add("head",null,5.4f);
foreach(var side in new[]{"L","R"})
{
Add("arm_upper_"+side,"arm_lower_"+side,2.6f);
Add("arm_lower_"+side,"hand_"+side,2.2f);
Add("hand_"+side,null,2.1f);
Add("leg_upper_"+side,"leg_lower_"+side,4.2f);
Add("leg_lower_"+side,"foot_"+side,3.0f);
Add("foot_"+side,null,3.0f);
}
}
private void Add(string start,string end,float radius)
{
if(!body.TryGetBoneTransform(start,out var from))return;
var to=from;
if(end is not null && !body.TryGetBoneTransform(end,out to))return;
var parent=body.GetBoneObject(start);
if(!parent.IsValid())return;
var go=new GameObject(parent){Name="Paint hitbox "+start};
go.LocalPosition=Vector3.Zero;go.LocalRotation=Rotation.Identity;
var hitbox=go.Components.Create<ManualHitbox>();
hitbox.Shape=end is null ? ManualHitbox.HitboxShape.Sphere : ManualHitbox.HitboxShape.Capsule;
hitbox.CenterA=Vector3.Zero;hitbox.CenterB=from.PointToLocal(to.Position);
hitbox.Radius=radius;hitbox.Target=Actor;hitbox.Rebuild();
boxes.Add(go);Count++;
}
protected override void OnDestroy(){foreach(var go in boxes)if(go.IsValid())go.Destroy();}
}