PaintballCombatant.cs
using Sandbox;
namespace PaintballBaddies;
public sealed class PaintballCombatant : Component
{
protected override void OnStart()
{
GameObject.Tags.Add("paintball_actor");
if(Components.Get<CharacterVoice>() is null)Components.Create<CharacterVoice>();
if(Components.Get<CloseCombat>() is null)Components.Create<CloseCombat>();
}
[Sync(SyncFlags.FromHost)] public int Team { get; set; }
[Sync(SyncFlags.FromHost)] public int PaintHits { get; private set; }
[Sync(SyncFlags.FromHost)] public int HitsLanded { get; private set; }
[Sync(SyncFlags.FromHost)] public bool StayInMatch { get; set; }
[Sync(SyncFlags.FromHost)] public bool AcceptHits { get; set; } = true;
public Vector3 EyePosition => Components.Get<PlayerController>()?.EyePosition
?? WorldPosition+Vector3.Up*(Components.Get<ArenaOpponent>()?.CoverCrouched == true ? 35 : 60);
public int HitLimit { get; set; } = 3;
public bool Eliminated => !StayInMatch && PaintHits >= HitLimit;
public float HitFeedbackRemaining { get; private set; }
[Sync(SyncFlags.FromHost)] public Vector3 LastHitDirection { get; private set; }
public Vector3 LastHitOrigin { get; private set; }
public PaintballCombatant LastAttacker { get; private set; }
private int observedHits;
protected override void OnUpdate()
{
if(!MultiplayerSession.Authority && PaintHits>observedHits)HitFeedbackRemaining=.45f;
observedHits=PaintHits;
HitFeedbackRemaining=System.MathF.Max(0,HitFeedbackRemaining-Time.Delta);
if(Components.Get<CharacterVoice>() is null)Components.Create<CharacterVoice>();
if(Components.Get<CloseCombat>() is null)Components.Create<CloseCombat>();
}
public bool RegisterHit( int attackingTeam, Vector3? sourcePosition = null, PaintballCombatant attacker = null, Vector3? observedOrigin = null )
{
if(!MultiplayerSession.Authority)return false;
if ( !AcceptHits || attackingTeam == Team || Eliminated ) return false;
PaintHits++;
LastAttacker=attacker;
LastHitOrigin=observedOrigin ?? sourcePosition ?? (attacker.IsValid() ? attacker.WorldPosition : WorldPosition);
if(attacker.IsValid() && attacker!=this)
{
attacker.HitsLanded++;
if(MultiplayerSession.Online)attacker.Components.Get<NetworkPawn>()?.ConfirmHit();
}
LastHitDirection = sourcePosition.HasValue ? (sourcePosition.Value-WorldPosition).WithZ(0).Normal : Vector3.Zero;
HitFeedbackRemaining = 0.45f;
return true;
}
public string HitDirectionLabel(Rotation view)
{
if(HitFeedbackRemaining<=0 || LastHitDirection.Length<.01f)return "";
var forward=Vector3.Dot(LastHitDirection,view.Forward.WithZ(0).Normal);
var right=Vector3.Dot(LastHitDirection,view.Right.WithZ(0).Normal);
return System.MathF.Abs(right)>System.MathF.Abs(forward) ? (right>0 ? "RIGHT" : "LEFT") : (forward>0 ? "AHEAD" : "BEHIND");
}
public void ResetPaintHits()
{
if(!MultiplayerSession.Authority)return;
Components.Get<CloseCombat>()?.ResetState();
Components.Get<CharacterPaint>()?.Clear();
PaintHits = 0;
HitsLanded = 0;
AcceptHits = true;
HitFeedbackRemaining = 0;
LastHitDirection = Vector3.Zero;
}
public static PaintballCombatant Find( GameObject go )
{
while ( go is not null )
{
var actor = go.Components.Get<PaintballCombatant>();
if ( actor is not null ) return actor;
go = go.Parent;
}
return null;
}
}