CharacterVoice.cs
using Sandbox;
namespace PaintballBaddies;
/// <summary>Occasional user-selected D/E efforts, shared by player and AI.</summary>
public sealed class CharacterVoice : Component
{
public const string EffortSound="sounds/voices/occasional_effort.sound";
public const string KnockdownSound="sounds/voices/knockdown_effort.sound";
private float cooldown=2;
private int previousHits;
private bool previousTraversal;
public float SinceLastVoice { get; private set; }=100;
public int Plays { get; private set; }
public int KnockdownPlays { get; private set; }
protected override void OnUpdate()
{
if(!MultiplayerSession.Authority)return;
cooldown=System.MathF.Max(0,cooldown-Time.Delta);
SinceLastVoice+=Time.Delta;
var actor=Components.Get<PaintballCombatant>();
if(!actor.IsValid())return;
bool traversal=Components.Get<VaultController>()?.IsVaulting==true || Components.Get<CoverController>()?.Sliding==true;
bool hit=actor.PaintHits>previousHits;
bool effort=traversal&&!previousTraversal;
previousHits=actor.PaintHits;previousTraversal=traversal;
if((hit || effort) && Game.Random.Float(0,1)<(effort ? .45f : .28f))TryPlay();
}
public bool TryPlay()
{
if(!MultiplayerSession.Authority)return false;
if(cooldown>0 || Scene.Camera is not {} camera || (camera.WorldPosition-WorldPosition).Length>650)return false;
if(Scene.GetAllComponents<CharacterVoice>().Any(x=>x.SinceLastVoice<4))return false;
PlayEvent(EffortSound);return true;
}
/// <summary>Confirmed knockdowns always speak, regardless of chatter cooldown or host camera distance.</summary>
public bool PlayKnockdown()
{
if(!MultiplayerSession.Authority)return false;
PlayEvent(KnockdownSound);KnockdownPlays++;return true;
}
private void PlayEvent(string sound)
{
// Let each listener's native spatial mixer attenuate the event. In a
// network game the host camera must not decide what a client can hear.
if(MultiplayerSession.Online)NetworkEffects.Sound(sound,WorldPosition+Vector3.Up*48,System.Guid.Empty);
else Sound.Play(sound,WorldPosition+Vector3.Up*48);
SinceLastVoice=0;cooldown=Game.Random.Float(12,20);Plays++;
}
}