CitizenMarkerGrip.cs
using Sandbox;
using Sandbox.Citizen;

namespace PaintballBaddies;

/// <summary>Paintball marker carry and support-hand recovery on the native human rig.</summary>
public sealed class CitizenMarkerGrip : Component
{
    // Shared native hold_R fit: gloved index at the VX-9 trigger guard.
    public static Vector3 MarkerOffset => new(0,0,-3f);
    public float Grip { get; private set; } = 1;
    [Property] public float RightGripStrength { get; set; } = 1;
    public Rotation SupportRotation { get; private set; }
    public bool Returning { get; private set; }
    private bool rotationInitialized;
    private Transform returnStart;

    public void UpdateGrip(SkinnedModelRenderer body, float strength, bool release, float delta)
    {
        strength = strength.Clamp(0,1);
        Grip += ((release ? 0 : strength)-Grip)*(1-System.MathF.Exp(-20*delta));
        body.Set("pb_grip_weight", strength * RightGripStrength.Clamp(0,1));
        body.Set("pb_left_grip", Grip);
    }

    public bool Attach(SkinnedModelRenderer body, GameObject marker, Vector3 offset, Angles angles)
    {
        if (!marker.IsValid() || !body.IsValid()) return false;
        // Let the engine carry the marker with the final animated bone, including
        // IK updates after OnUpdate. Copying its world pose here lagged the wrist.
        body.CreateBoneObjects = true;
        var hold = body.GetBoneObject("hold_R");
        if (!hold.IsValid()) return false;
        if (marker.Parent != hold) marker.Parent = hold;
        marker.LocalPosition = offset;
        marker.LocalRotation = angles.ToRotation();
        return true;
    }

    public void UpdateSupport(SkinnedModelRenderer body, CitizenAnimationHelper animation,
        GameObject marker, GameObject target, Vector3 offset, bool release, float recovery)
    {
        if (release) { animation.IkLeftHand = null; return; }
        if (!target.IsValid() || !body.TryGetBoneTransform("hand_L", out var left)) return;
        if (!rotationInitialized)
        {
            SupportRotation = marker.WorldRotation.Inverse*left.Rotation;
            rotationInitialized = true;
        }
        target.WorldPosition = marker.WorldPosition+marker.WorldRotation*offset;
        target.WorldRotation = marker.WorldRotation*SupportRotation;
        if (recovery >= 0)
        {
            if (!Returning) { returnStart = GameObject.WorldTransform.ToLocal(left); Returning = true; }
            var progress = recovery.Clamp(0,1);
            var blend = progress*progress*(3-2*progress);
            target.WorldPosition = Vector3.Lerp(GameObject.WorldTransform.PointToWorld(returnStart.Position), target.WorldPosition, blend);
            target.WorldRotation = Rotation.Slerp(GameObject.WorldRotation*returnStart.Rotation, target.WorldRotation, blend);
        }
        animation.IkLeftHand = target;
    }

    public void ResetRecovery() => Returning = false;
}