PaintballFlightReview.cs
using Sandbox;
namespace PaintballBaddies;
/// <summary>Opt-in unobstructed flight check for both production projectile paths.</summary>
public sealed class PaintballFlightReview : Component
{
private PlayerController player;
private PaintballMarker weapon;
private GameObject opponentBall;
private Vector3 origin;
private float elapsed, shotTime;
private int stage;
protected override void OnUpdate()
{
elapsed+=Time.Delta;
if(stage==0 && elapsed>1)
{
player=Scene.GetAllComponents<PlayerController>().First();
weapon=player.Components.Get<PaintballMarker>();
player.UseInputControls=false;weapon.AcceptInput=false;
player.WorldPosition=new Vector3(0,0,5000);stage=1;
}
if(stage>0)player.WorldPosition=new Vector3(0,0,5000);
if(stage==1 && elapsed>2)
{
origin=weapon.Muzzle;
var fired=weapon.FireAt(origin+Vector3.Up*10000);
opponentBall=new GameObject(true,"Flight review AI ball");
opponentBall.WorldPosition=origin+Vector3.Right*100;
var ball=opponentBall.Components.Create<OpponentPaintball>();
ball.Shooter=player.GameObject;ball.Velocity=Vector3.Up*PaintballFlight.Speed;
shotTime=elapsed;stage=2;
Log.Info($"FLIGHT_REVIEW fired={fired}");
}
if(stage==2 && elapsed-shotTime>2)
{
var ball=Scene.GetAllComponents<TrailRenderer>().FirstOrDefault(x=>x.GameObject.Parent?.Name=="Paintball");
Log.Info("FLIGHT_REVIEW "+Json.Serialize(new{age=elapsed-shotTime,
player_alive=weapon.ActivePaintballs==1,player_distance=ball.IsValid() ? (ball.WorldPosition-origin).Length : 0,
ai_alive=opponentBall.IsValid(),ai_distance=opponentBall.IsValid() ? (opponentBall.WorldPosition-origin).Length : 0,
player_trail=ball.IsValid(),ai_trail=opponentBall.IsValid() && opponentBall.Components.Get<TrailRenderer>(FindMode.EverythingInSelfAndDescendants).IsValid()}));
stage=3;
}
if(stage==3 && elapsed-shotTime>3.2f)
{
Log.Info("FLIGHT_REVIEW "+Json.Serialize(new{expired_player=weapon.ActivePaintballs==0,expired_ai=!opponentBall.IsValid()}));stage=4;
}
}
}