VaultTraversalChecks.cs
using Sandbox;
namespace PaintballBaddies;
public sealed class VaultTraversalChecks : Component
{
public string TestModelPath { get; set; } = "";
private PlayerController player;
private VaultController vault;
private GameObject obstacle;
private float elapsed;
private int phase;
private void Check( string name, bool pass, string detail ) => Log.Info( $"VAULT_MOVE {(pass ? "PASS" : "FAIL")} {name}: {detail}" );
protected override void OnStart()
{
player = Components.Get<PlayerController>(); vault = Components.Get<VaultController>();
player.UseInputControls = player.UseLookControls = player.UseCameraControls = false; Components.Get<PaintballMarker>().AcceptInput = false;
Components.Get<CoverController>().TestInput = true;
WorldPosition = new Vector3( -595, 70, 2 ); player.EyeAngles = new Angles( 0, 0, 0 );
obstacle = TrainingRange.Box( null, "Vault traversal fixture", new Vector3( -550, 70, 24 ), new Vector3( 20, 180, 48 ), Color.Gray, true );
obstacle.Components.Create<CoverSurface>();
}
protected override void OnUpdate()
{
elapsed += Time.Delta;
var camera = Scene.GetAllComponents<CameraComponent>().First();
camera.WorldPosition = new Vector3( -550, -190, 110 );
camera.WorldRotation = Rotation.LookAt( new Vector3( -550, 70, 45 ) - camera.WorldPosition );
if ( phase == 0 && elapsed > 1 )
{
if ( !string.IsNullOrEmpty( TestModelPath ) )
{
var character = CoveredRosterAssets.CharacterIndex( TestModelPath );
if ( character >= 0 ) Components.Get<RosterSelection>().Select( character );
player.Renderer.Model = Model.Load( TestModelPath );
Check( "protected model loaded", player.Renderer.Model.IsValid(), player.Renderer.Model.Name );
}
Check( "rejects vault outside cover", !vault.TryBegin(), "near a valid obstacle but unattached" );
var weapon = Components.Get<PaintballMarker>();
var fired = weapon.FireAt(new Vector3(-400,70,65));
weapon.Reload(); weapon.ReviewAimOverride = true;
Check("reload prepared before vault", fired && weapon.ReloadRemaining > 0 && weapon.Ammo == 39, "one paintball spent, reload active");
Check( "attaches to low cover", Components.Get<CoverController>().TryEnter(), "physical fixture" );
phase++;
}
else if ( phase == 1 && elapsed > 1.3f )
{
Check( "shows available vault prompt", vault.Available && vault.Hint == "V VAULT", vault.Hint );
var cover = Components.Get<CoverController>();
cover.Leave();
Check( "clears prompt immediately after leaving", !vault.Available && vault.Hint == "", "same frame as Leave" );
Check( "rejects stale route after leaving", !vault.TryBegin(), "no intervening route refresh" );
Check( "reattaches before traversal", cover.TryEnter(), "valid low cover" );
Check( "starts valid vault", vault.TryBegin(), vault.Outcome );
var weapon = Components.Get<PaintballMarker>();
Check("vault cancels reload without transferring paint", weapon.ReloadRemaining == 0 && weapon.Ammo == 39 && weapon.Reserve == 160, $"ammo={weapon.Ammo} reserve={weapon.Reserve}");
Check( "shows traversal state", vault.Hint == "VAULTING", vault.Hint );
Check("vault suppresses cover entry and prompt",!cover.CanEnter && cover.Hint=="" && !cover.TryEnter(),"no attachment during kinematic traversal");
phase++;
}
else if ( phase == 2 && elapsed > 1.75f )
{
Check( "body crosses above barrier", vault.IsVaulting && WorldPosition.z > 48 && WorldPosition.x > -590, $"position={WorldPosition}" );
Check( "vault animation active", player.Renderer.Sequence.Name == "vault" && player.Renderer.Sequence.Time > .1f, $"clip={player.Renderer.Sequence.Name} time={player.Renderer.Sequence.Time}" );
Check( "firing blocked during vault", !Components.Get<PaintballMarker>().FireAt( new Vector3( 100, 70, 55 ) ), "physical traversal" );
var weapon = Components.Get<PaintballMarker>();
weapon.Reload();
Check("vault blocks reload and aim", weapon.ReloadRemaining == 0 && !weapon.Aiming && weapon.Ammo == 39 && weapon.Reserve == 160, "held aim and reload request during traversal");
if ( !string.IsNullOrEmpty( TestModelPath ) )
{
var sampled = CoveredVaultGroundProfiles.TrySample( player.Renderer.Model.Name, ((TestModelPath is "models/characters/mei_planted_review/mei_protected.vmdl" or "models/characters/mei_protected_morph/mei_protected.vmdl") ? player.Renderer.Sequence.TimeNormalized : player.Renderer.Sequence.Time / .8f), out var offset );
var actual = vault.VisualBasePosition.z - player.Renderer.LocalPosition.z;
Check( "protected grounding applied", sampled && System.MathF.Abs( actual - offset ) < .05f, $"model={player.Renderer.Model.Name} actual={actual} expected={offset}" );
Check( "integrated helmet used", Components.Get<RosterHelmet>() is { Equipped: false }, "legacy helmet and neck seal removed" );
}
phase++;
}
else if ( phase == 3 && elapsed > 2.6f )
{
Check( "lands on far side", vault.Completed == 1 && !vault.IsVaulting && WorldPosition.x > -520 && WorldPosition.z < 2, $"position={WorldPosition} outcome={vault.Outcome}" );
Check( "physics and input restored", player.Body.MotionEnabled && !player.UseInputControls, $"motion={player.Body.MotionEnabled} input={player.UseInputControls}" );
Check( "visual offset restored", player.Renderer.LocalPosition == vault.VisualBasePosition, $"offset={player.Renderer.LocalPosition}" );
var weapon = Components.Get<PaintballMarker>();
weapon.Reload();
Check("reload and held aim resume after landing", weapon.ReloadRemaining > 0 && weapon.Aiming && weapon.Ammo == 39 && weapon.Reserve == 160, "fresh reload request accepted");
weapon.ReviewAimOverride = null;
obstacle.Destroy(); Components.Get<CoverController>().TestInput = false;
player.UseInputControls = player.UseLookControls = player.UseCameraControls = true; Components.Get<PaintballMarker>().AcceptInput = true;
Log.Info( "VAULT_MOVE COMPLETE" ); Destroy();
}
}
protected override void OnPreRender()
{
if ( TestModelPath != "models/characters/mei_planted_review/mei_protected.vmdl" || elapsed < 1.95f || elapsed > 2.45f ) return;
var body = player.Renderer;
foreach ( var name in new[] { "Hips", "Head", "LeftFoot", "RightFoot" } )
{
if ( !body.TryGetBoneTransform( name, out var bone ) ) continue;
var p = bone.Position - player.WorldPosition;
Log.Info( $"VAULT_LANDING_POSE t={elapsed:F6} dt={Time.Delta:F6} clip={body.Sequence.Name} bone={name} x={p.x:F6} y={p.y:F6} z={p.z:F6} clip_time={body.Sequence.Time:F6} normalized={body.Sequence.TimeNormalized:F6}" );
}
}
}