HelmetFitPreview.cs
using Sandbox;
using System.Linq;
namespace PaintballBaddies;

/// <summary>Opt-in accessory fitting preview. Does not modify the character source mesh.</summary>
public sealed class HelmetFitPreview : Component
{
    [Property] public string ModelVariant { get; set; } = "";
    [Property] public bool CloseUp { get; set; }
    [Property] public int CharacterIndex { get; set; } = 4;
    [Property] public bool UseHairVariant { get; set; } = true;
    [Property] public float ViewYaw { get; set; }
    [Property] public string Motion { get; set; } = "";
    [Property] public string HelmetAsset { get; set; } = "helmet_freya_hq";
    [Property] public float HelmetScale { get; set; } = 1.12f;
    [Property] public Vector3 HeadOffset { get; set; } = new(1.3f,0,-3.4f);
    private PlayerController player;
    private GameObject helmet;
    private GameObject neckSeal;
    private bool fitted;
    [Property] public bool FullBody { get; set; }
    protected override void OnStart()
    {
        player=Scene.GetAllComponents<PlayerController>().First();
        helmet=new GameObject { Name="Freya enclosed helmet fitting preview" };
        helmet.Components.Create<ModelRenderer>().Model=Model.Load($"models/gear/{HelmetAsset}/{HelmetAsset}.vmdl");
        neckSeal=new GameObject { Name="Helmet fabric neck seal fitting preview" };
        var sealAsset=CharacterIndex==4 ? "helmet_neck_seal" : $"helmet_neck_seal_{RosterSelection.Names[CharacterIndex].ToLowerInvariant()}";
        if (!string.IsNullOrEmpty(ModelVariant)) sealAsset=$"helmet_neck_seal_{ModelVariant}";
        neckSeal.Components.Create<SkinnedModelRenderer>().Model=Model.Load($"models/gear/{sealAsset}/{sealAsset}.vmdl");
    }
    protected override void OnUpdate()
    {
        var roster=player.Components.Get<RosterSelection>();
        if (roster is null) return;
        if (roster.Selected != CharacterIndex && !roster.Select(CharacterIndex)) return;
        var renderer=player.Renderer;
        if (!fitted)
        {
            if (UseHairVariant)
            {
                var slug=RosterSelection.Names[CharacterIndex].ToLowerInvariant();
                renderer.Model=string.IsNullOrEmpty(ModelVariant) ? Model.Load($"models/characters/{slug}/{slug}_helmet_hair_preview.vmdl") : Model.Load($"models/characters/{ModelVariant}/{ModelVariant}_combat_preview.vmdl");
            }
            renderer.UseAnimGraph=false;
            renderer.Sequence.Name="idle";
            renderer.Sequence.Looping=true;
            fitted=true;
        }
        if (!string.IsNullOrEmpty(Motion))
        {
            player.Components.Get<ViperAvatar>().PreviewSequence=Motion;
        }
        if (!renderer.TryGetBoneTransform("Head",out var head)) return;
        var bind=renderer.Model.GetBoneTransform("Head");
        var rotation=head.Rotation*bind.Rotation.Inverse;
        helmet.WorldRotation=rotation;
        helmet.WorldScale=Vector3.One*HelmetScale;
        helmet.WorldPosition=head.Position+rotation*HeadOffset;
        neckSeal.WorldTransform=renderer.WorldTransform;
        neckSeal.Enabled=true;
        if (neckSeal.Enabled) neckSeal.Components.Get<SkinnedModelRenderer>().BoneMergeTarget=renderer;
        if (CloseUp)
        {
            player.UseCameraControls=false;
            player.UseInputControls=false;
            player.UseLookControls=false;
            var camera=Components.Get<CameraComponent>();
            var target=FullBody ? renderer.WorldPosition+Vector3.Up*40 : head.Position+Vector3.Up*2;
            camera.WorldPosition=target+(renderer.WorldRotation*Rotation.FromYaw(ViewYaw)).Forward*(FullBody ? 160 : 32)+Vector3.Up*3;
            camera.WorldRotation=Rotation.LookAt(target-camera.WorldPosition);
            camera.FieldOfView=45;
        }
    }
    protected override void OnDestroy() { helmet?.Destroy(); neckSeal?.Destroy(); }
}