CounterDebris.cs
using Sandbox;
using System;
namespace PaintballBaddies;
/// <summary>Bounded native model particles for counter props without authored gibs.</summary>
public sealed class CounterDebris : Component
{
public static int BreakBursts {get;private set;}
public static int ChipBursts {get;private set;}
private string kind;
private int round;
private bool broken;
private float age;
private const int MaximumBursts=8;
public static void Spawn(Scene scene,Vector3 point,string kind,int round,bool broken)
{
foreach(var old in scene.GetAllComponents<CounterDebris>().OrderByDescending(x=>x.age).SkipLast(MaximumBursts-1).ToArray())old.GameObject.Destroy();
var go=new GameObject(false,broken ? "Counter prop fragments" : "Counter impact chips"){NetworkMode=NetworkMode.Never,WorldPosition=point};
go.Tags.Add("paintball_debris");var fx=go.Components.Create<CounterDebris>();fx.kind=kind;fx.round=round;fx.broken=broken;go.Enabled=true;
if(broken)BreakBursts++;else ChipBursts++;
}
protected override void OnStart()
{
bool metal=kind=="counter_metal",glass=kind=="counter_glass",food=kind=="counter_food";
var particles=Components.Create<ParticleEffect>();particles.MaxParticles=broken ? 10 : 2;particles.Lifetime=2.2f;particles.LocalSpace=0;
particles.ApplyShape=particles.ApplyRotation=particles.ApplyColor=particles.ApplyAlpha=false;
particles.Force=true;particles.ForceDirection=Vector3.Down*500;particles.ForceScale=1;particles.ForceSpace=ParticleEffect.SimulationSpace.World;
particles.Collision=true;particles.CollisionRadius=.6f;particles.Bounce=.25f;particles.Friction=.6f;particles.PushStrength=0;
particles.CollisionIgnore=new TagSet();particles.CollisionIgnore.Add("paintball_actor");particles.CollisionIgnore.Add("overdrive_enemy");particles.CollisionIgnore.Add("paintball_debris");
var renderer=Components.Create<ParticleModelRenderer>();renderer.Scale=1;renderer.CastShadows=false;renderer.RotateWithGameObject=false;
if(food)renderer.Choices=new(){new(){Model=Model.Load("models/restaurant/breakfast/potato_crumb.vmdl")},new(){Model=Model.Load("models/restaurant/breakfast/egg_piece.vmdl")}};
else if(glass)
{
var layout=Json.Deserialize<ShieldBreakEffect.Layout>(FileSystem.Mounted.ReadAllText("models/gear/clear_shield/fracture/layout.json"));
renderer.Choices=layout.Pieces.Take(6).Select(x=>new ParticleModelRenderer.ModelEntry{Model=Model.Load(x.Model)}).ToList();
}
else renderer.Choices=Enumerable.Range(1,4).Select(i=>new ParticleModelRenderer.ModelEntry
{Model=Model.Load(metal ? $"models/citizen_props/oldoven/oldovengib{i:00}_lod01.vmdl" : $"models/citizen_props/coffeemug01/coffeemug01gib{i:00}_lod01.vmdl")}).ToList();
particles.OnStep=(p,dt)=>{p.Angles+=new Angles(210,320,170)*dt;p.Alpha=((2.2f-p.Age)/.5f).Clamp(0,1);};
for(int i=0;i<particles.MaxParticles;i++)
{
var direction=Rotation.FromYaw(i*137.5f).Forward;
var p=particles.Emit(WorldPosition+direction*2+Vector3.Up*2,0);
p.Size=new Vector3((food ? 1.4f : metal ? .13f : glass ? .13f : .65f)*(broken ? 1 : .4f));
p.Color=glass ? new Color(.46f,.28f,.12f) : Color.White;
p.Velocity=direction*Game.Random.Float(60,130)+Vector3.Up*Game.Random.Float(90,170);
}
if(food)KitchenCombatAudio.Play("egg",WorldPosition);
else if(broken && !metal)SausageKitchenAudio.Play(true,WorldPosition);
else Sound.Play(new SoundEvent{Sounds=[SoundFile.Load(metal ? $"sounds/physics/phys-impact-metal-hollow-{Game.Random.Int(1,3)}.vsnd" : $"sounds/impacts/bullets/glass_bullet_impact-{Game.Random.Int(1,3)}.vsnd")],Volume=broken ? .7f : .32f,Pitch=1.1f,Distance=850},WorldPosition);
}
protected override void OnUpdate()
{
age+=Time.Delta;
if(age>2.4f || MultiplayerSession.Find(Scene)?.RoundId!=round)GameObject.Destroy();
}
}