ProtectedRosterPreview.cs
using Sandbox;
namespace PaintballBaddies;
/// <summary>Isolated native preview of the protected roster candidate.</summary>
public sealed class ProtectedRosterPreview : Component
{
[Property] public string ModelPath { get; set; } = "models/characters/roxie_protected/roxie_protected.vmdl";
[Property] public string Motion { get; set; } = "run";
[Property] public Vector3 MarkerOffset { get; set; } = new( 5, 0, 0 );
[Property] public bool HandCloseUp { get; set; }
[Property] public bool ShowMarker { get; set; } = true;
[Property] public bool ShowBeltGear { get; set; }
[Property] public bool ShowContactFloor { get; set; }
[Property] public float ViewYaw { get; set; }
[Property] public float MotionPhase { get; set; } = -1;
[Property] public bool LogPoseReview { get; set; }
private bool poseLogged;
[Property] public bool HeadCloseUp { get; set; }
[Property] public float GripAmount { get; set; } = 1;
[Property] public float ThumbAmount { get; set; } = 1;
[Property] public float AimPitch { get; set; }
[Property] public bool ReviewAimTransition { get; set; }
private float appliedPitch;
private float aimReviewTime;
[Property] public string MarkerModelPath { get; set; } = "models/weapons/vx9_full_frame/vx9_full_frame.vmdl";
[Property] public Vector3 CloseUpFocusOffset { get; set; } = new( 5, 0, 0 );
[Property] public Vector3 CloseUpCameraOffset { get; set; } = new( 30, -35, 15 );
[Property] public float ReloadPhase { get; set; } = .55f;
[Property] public string ReloadTrackPath { get; set; } = "";
private ReloadPresentationTrack reloadTrack;
private GameObject subject;
private GameObject marker;
private SkinnedModelRenderer body;
protected override void OnStart()
{
var player = Scene.GetAllComponents<PlayerController>().First();
player.UseCameraControls = false;
player.UseInputControls = false;
player.UseLookControls = false;
subject = new GameObject { Name = "Protected Roxie review" };
subject.WorldPosition = new Vector3( 0, 0, 410 );
if ( ShowContactFloor )
TrainingRange.Box( subject, "Sole contact reference", new Vector3( 0, 0, -1 ),
new Vector3( 160, 160, 2 ), new Color( .55f, .58f, .60f ) );
body = subject.Components.Create<SkinnedModelRenderer>();
body.Model = Model.Load( ModelPath );
marker = new GameObject { Name = "Protected marker fit review" };
marker.Components.Create<ModelRenderer>().Model = Model.Load( MarkerModelPath );
marker.Enabled = ShowMarker;
body.UseAnimGraph = false;
body.Sequence.Name = Motion;
body.Sequence.Looping = true;
if ( Motion == "reload_standing" )
{
body.Sequence.Looping = false;
body.PlaybackRate = 0;
reloadTrack = subject.Components.Create<ReloadPresentationTrack>();
reloadTrack.Body = body;
if ( ModelPath.Contains( "/mei_protected" ) ) reloadTrack.TrackPath = "animations/mei_reload_presentation.json";
if ( !string.IsNullOrWhiteSpace( ReloadTrackPath ) ) reloadTrack.TrackPath = ReloadTrackPath;
reloadTrack.Phase = ReloadPhase;
reloadTrack.ApplyFrame();
}
else if ( ShowBeltGear && CoveredRosterAssets.ReloadTrack( ModelPath ) is { } trackPath )
{
reloadTrack = subject.Components.Create<ReloadPresentationTrack>();
reloadTrack.Body = body; reloadTrack.TrackPath = trackPath; reloadTrack.Docked = true;
}
if ( Motion != "reload_standing" && MotionPhase >= 0 ) body.PlaybackRate = 0;
Log.Info( $"PB_PROTECTED model={body.Model.IsValid()} clip={body.Sequence.Name}" );
Log.Info( $"PB_PROTECTED_MORPHS {string.Join(",", body.Morphs.Names)}" );
}
protected override void OnUpdate()
{
aimReviewTime += Time.Delta;
if ( Motion != "reload_standing" && MotionPhase >= 0 ) body.Sequence.TimeNormalized = MotionPhase.Clamp( 0, 1 );
var targetPitch = ReviewAimTransition ? (aimReviewTime < 1 ? -30f : aimReviewTime < 2 ? 30f : 0f) : AimPitch;
appliedPitch += (targetPitch.Clamp( -45, 45 ) - appliedPitch) * (1 - System.MathF.Exp( -12 * Time.Delta ));
if ( reloadTrack is not null && !reloadTrack.Docked )
{
body.Sequence.TimeNormalized = ReloadPhase;
reloadTrack.Phase = ReloadPhase;
reloadTrack.ApplyFrame();
}
else foreach ( var name in body.Morphs.Names )
if ( name == "LeftGrip" || name == "RightGrip" || name == "LeftThumb" || name == "RightThumb" )
body.Morphs.Set( name, name.Contains("Thumb") ? ThumbAmount : GripAmount, 0 );
if(body.Model.Morphs.GetIndex("WristVolume")>=0)
body.Morphs.Set("WristVolume",Motion=="run" ? 1 : 0,0);
if ( body.TryGetBoneTransform( "RightHand", out var hand ) )
{
marker.WorldPosition = hand.Position + subject.WorldRotation * MarkerOffset;
marker.WorldRotation = subject.WorldRotation;
}
var camera = Components.Get<CameraComponent>();
camera.WorldPosition = new Vector3( 0, 0, 445 ) + Rotation.FromYaw( ViewYaw ) * new Vector3( 145, -95, 13 );
camera.WorldRotation = Rotation.LookAt( new Vector3(0,0,445) - camera.WorldPosition );
camera.FieldOfView = 40;
if ( HeadCloseUp )
{
var target = body.TryGetBoneTransform( "Head", out var head )
? head.Position + Vector3.Up * 5
: subject.WorldPosition + Vector3.Up * 62;
camera.WorldPosition = target + new Vector3( 25, -15, 3 );
camera.WorldRotation = Rotation.LookAt( target - camera.WorldPosition );
camera.FieldOfView = 32;
}
if ( HandCloseUp && body.TryGetBoneTransform( "RightHand", out var focus ) )
{
var target = focus.Position + CloseUpFocusOffset;
camera.WorldPosition = target + Rotation.FromYaw( ViewYaw ) * CloseUpCameraOffset;
camera.WorldRotation = Rotation.LookAt( target - camera.WorldPosition );
camera.FieldOfView = 32;
}
}
protected override void OnDestroy() { subject?.Destroy(); marker?.Destroy(); }
protected override void OnPreRender()
{
if ( !body.IsValid() ) return;
if(LogPoseReview && !poseLogged && aimReviewTime>.3f)
{
foreach(var name in new[]{"Hips","Spine","RightArm","RightForeArm","LeftArm","LeftForeArm","RightHand","LeftHand","LeftFoot","RightFoot"})
if(body.TryGetBoneTransform(name,out var bone))
Log.Info($"STRAFE_POSE {name} local={body.WorldTransform.PointToLocal(bone.Position)} phase={body.Sequence.TimeNormalized} time={body.Sequence.Time}");
poseLogged=true;
}
CoveredSuitMotion.Apply( body );
if ( CoveredAimPose.Apply( body, appliedPitch, MarkerOffset, out var pose ) )
{
marker.WorldPosition = pose.Position;
marker.WorldRotation = pose.Rotation;
}
}
}