PopRiotBurst.cs
using Sandbox;
using System;
namespace PaintballBaddies;
/// <summary>Reuses the bank reward's native ring and particles; bounded independently on each peer.</summary>
public sealed class PopRiotBurst : Component
{
private float age,radius;
private Color tint;
private LineRenderer ring;
public static int SoundsPlayed {get;private set;}
private static float nextPopSound;
public static void Spawn(Scene scene,Vector3 position,float radius,Color tint,bool giant,bool chained)
{
if(!scene.IsValid())return;
var existing=scene.GetAllComponents<PopRiotBurst>().OrderByDescending(x=>x.age).ToArray();
foreach(var old in existing.Take(Math.Max(0,existing.Length-9)))old.GameObject.Destroy();
var go=new GameObject(false,"Paint pop shower"){WorldPosition=position,NetworkMode=NetworkMode.Never};
go.Tags.Add("paintball_debris");var effect=go.Components.Create<PopRiotBurst>();effect.radius=radius;effect.tint=tint;go.Enabled=true;
if(RealTime.Now>=nextPopSound || giant)
{
nextPopSound=RealTime.Now+.055f;
var cue=PopRiotAudio.Get(giant ? "bank" : "collect");
var handle=Sound.Play(cue,scene.Camera?.WorldPosition ?? position);
if(handle?.IsPlaying==true)SoundsPlayed++;
}
if(giant && scene.Camera.IsValid())scene.Camera.AddShake(.55f,22,.3f);
}
protected override void OnStart()
{
ring=ArenaGlow.Ring(GameObject,"Pop ripple",radius,2,tint*1.8f,32);
// Orient the quick halo to the camera instead of flashing across the whole screen.
ring.LocalRotation=Rotation.FromPitch(90);
var particles=Components.Create<ParticleEffect>();particles.MaxParticles=radius>70 ? 80 : 22;particles.Lifetime=.85f;
particles.LocalSpace=0;particles.ApplyAlpha=false;particles.ApplyColor=false;particles.ApplyRotation=false;particles.ApplyShape=false;
particles.Force=true;particles.ForceDirection=Vector3.Down*170;particles.ForceScale=1;particles.ForceSpace=ParticleEffect.SimulationSpace.World;particles.Collision=false;
var renderer=Components.Create<ParticleSpriteRenderer>();
renderer.Sprite=new Sprite{Animations=[new Sprite.Animation{Name="Default",Frames=[new Sprite.Frame{Texture=PaintImpactSystem.SplashTexture}]}]};
renderer.Scale=1;renderer.Additive=false;renderer.Lighting=false;renderer.Shadows=false;
particles.OnStep=(p,dt)=>p.Alpha=((.85f-p.Age)/.4f).Clamp(0,1);
int count=radius>70 ? 72 : 18;
for(int i=0;i<count;i++)
{
var direction=new Vector3(MathF.Cos(i*2.399f),MathF.Sin(i*2.399f),.3f+(i%5)*.25f).Normal;
var p=particles.Emit(WorldPosition+direction*radius*.7f,0);
p.Size=new Vector3(radius>70 ? 13 : 7);p.Color=i%3==0 ? Color.Lerp(tint,Color.White,.4f) : tint;
p.Velocity=direction*(radius>70 ? 240 : 125);
}
}
protected override void OnUpdate()
{
age+=Time.Delta;if(age>=.9f){GameObject.Destroy();return;}
ring.Enabled=age<.28f;
ring.LocalScale=Vector3.One*(1+age*2);
var color=(tint*1.6f).WithAlpha((1-age/.28f).Clamp(0,1));ring.Color=Gradient.FromColors(color,color);
}
}