CharacterPaintReview.cs
using Sandbox;
namespace PaintballBaddies;
/// <summary>Opt-in physical front/back/side impacts above the arena; never takes player input.</summary>
public sealed class CharacterPaintReview : Component
{
private GameObject actorObject,shooterObject;
private CitizenOpponentPresentation presentation;
private ArenaOpponent bot;
private PaintballCombatant actor,attacker;
private float elapsed,next=1;
private int shot;
public bool Done { get; private set; }
public List<string> Results { get; }=new();
public bool Crouch { get; set; }
protected override void OnPreRender()
{
if(presentation.IsValid() && presentation.IsReady && !actor.Components.Get<CloseCombat>().Incapacitated)
presentation.FootstepRenderer.Components.Get<CitizenLocomotion>().Animation.DuckLevel=Crouch ? 1 : 0;
}
protected override void OnStart()
{
actorObject=new GameObject(GameObject){Name="Clothing hit review",WorldPosition=new Vector3(0,0,1500)};
presentation=actorObject.Components.Create<CitizenOpponentPresentation>();
bot=actorObject.Components.Create<ArenaOpponent>();bot.Character="viper";bot.CombatEnabled=false;
actor=actorObject.Components.Create<PaintballCombatant>();actor.Team=91;actor.StayInMatch=true;
var box=actorObject.Components.Create<BoxCollider>();box.Scale=new(28,28,66);box.Center=new(0,0,33);
shooterObject=new GameObject(GameObject){Name="Clothing review shooter",WorldPosition=new Vector3(120,0,1500)};
attacker=shooterObject.Components.Create<PaintballCombatant>();attacker.Team=92;attacker.StayInMatch=true;
}
protected override void OnUpdate()
{
elapsed+=Time.Delta;
if(!presentation.IsReady)return;
bot.Enabled=false;
bot.ReviewCrouch=Crouch;
var agent=actorObject.Components.Get<NavMeshAgent>();if(agent.IsValid()){agent.Stop();agent.UpdatePosition=false;}
actorObject.WorldPosition=new(0,0,1500);actorObject.WorldRotation=Rotation.Identity;
var body=presentation.FootstepRenderer;
body.Components.Get<CitizenLocomotion>().UpdateMotion(Vector3.Zero,Vector3.Zero,true,Crouch ? 1 : 0,0,Rotation.Identity,0,false,Time.Delta);
if(Done || elapsed<next)return;
next=elapsed+.45f;
if(shot>0)
{
var paint=actor.Components.Get<CharacterPaint>();
bool pass=actor.PaintHits==shot && attacker.HitsLanded==shot && paint?.Count==System.Math.Min(shot,CharacterPaint.Capacity)
&& !paint.LastBone.Contains("IK") && !paint.LastBone.Contains("ikrule");
var result=$"{(pass ? "PASS" : "FAIL")} shot={shot} hits={actor.PaintHits} score={attacker.HitsLanded} paint={paint?.Count} bone={paint?.LastBone}";
Results.Add(result);Log.Info("CHARACTER_PAINT "+result);
}
if(shot>=12){Done=true;return;}
var direction=new[]{Vector3.Forward,Vector3.Backward,Vector3.Left,Vector3.Right}[shot%4];
var height=new[]{34f,47f,61f}[shot/4];
var target=actorObject.WorldPosition+Vector3.Up*height;
var start=target+direction*120;
var ray=PaintballFlight.Trace(Scene,start,target-direction*12,shooterObject);
Log.Info($"CHARACTER_PAINT_TRACE shot={shot+1} hit={ray.GameObject?.Name} hitbox={ray.Hitbox is not null} position={ray.HitPosition}");
var ballObject=new GameObject(GameObject){Name="Clothing test paintball",WorldPosition=start};
var ball=ballObject.Components.Create<OpponentPaintball>();ball.Shooter=shooterObject;ball.Team=92;ball.Velocity=PaintballFlight.LaunchVelocity(start,target);
shot++;
}
}