YolkBurst.cs
using Sandbox;
using System;
namespace PaintballBaddies;
/// <summary>Reuses the existing splatter mask and native shard particles for eggs.</summary>
public sealed class YolkBurst : Component
{
public static readonly Color Yellow=new(1,.61f,.035f);
public static int Bursts {get;private set;}
private float age;
public static void Spawn(Vector3 point)
{
var go=new GameObject(true,"Broken egg and yolk"){NetworkMode=NetworkMode.Never,WorldPosition=point};
go.Tags.Add("paintball_debris");go.Components.Create<YolkBurst>();Bursts++;
}
protected override void OnStart()
{
foreach(var old in Scene.GetAllComponents<YolkBurst>().OrderByDescending(x=>x.age).SkipLast(11).ToArray())old.GameObject.Destroy();
var p=Components.Create<ParticleEffect>();p.MaxParticles=14;p.Lifetime=1.25f;p.LocalSpace=0;
p.ApplyShape=p.ApplyRotation=p.ApplyColor=p.ApplyAlpha=false;p.Force=true;p.ForceDirection=Vector3.Down*450;p.ForceScale=1;p.ForceSpace=ParticleEffect.SimulationSpace.World;
p.Collision=true;p.CollisionRadius=.4f;p.Bounce=.12f;p.PushStrength=0;
var render=Components.Create<ParticleModelRenderer>();render.Choices=[new(){Model=Model.Load("models/gear/clear_shield/fracture/piece_03.vmdl")}];
render.MaterialOverride=Material.Load("materials/dev/primary_white.vmat");render.CastShadows=false;render.RotateWithGameObject=false;
p.OnStep=(part,dt)=>{part.Angles+=new Angles(210,350,130)*dt;part.Alpha=((1.25f-part.Age)/.25f).Clamp(0,1);};
for(int i=0;i<14;i++){var part=p.Emit(WorldPosition,0);part.Size=new Vector3(i<8 ? .045f : .065f);part.Color=i<8 ? new Color(.96f,.91f,.8f) : Yellow;part.Velocity=Rotation.FromYaw(i*137).Forward*(40+i*3)+Vector3.Up*(45+i*4);}
// Near surfaces receive yolk; an air interception also drops a splash
// onto the floor below it. No full-screen obstruction or paintball FX.
foreach(var direction in new[]{Vector3.Down,Vector3.Up,Vector3.Forward,Vector3.Backward,Vector3.Left,Vector3.Right})
{
var hit=Scene.Trace.Ray(WorldPosition,WorldPosition+direction*(direction==Vector3.Down ? 180 : 24)).WithSurfaceMeshes().WithoutTags("paintball_debris").Run();
if(!hit.Hit)continue;
var mark=new GameObject(GameObject,true,"Yolk splatter"){NetworkMode=NetworkMode.Never,WorldPosition=hit.HitPosition+hit.Normal*(hit.Normal.z>.75f ? .03f : .3f),WorldRotation=Rotation.LookAt(-hit.Normal)};
var enemy=OverdriveEnemy.Find(hit.GameObject);var rig=enemy?.Components.Get<OverdriveRobotRig>();
if(rig?.Body.IsValid()==true)
{
string bone=enemy.Zone(hit.HitPosition) switch {1=>"head",8=>"leg_lower_L",16=>"leg_lower_R",_=>"spine_1"};
var parent=rig.Body.GetBoneObject(bone);if(parent.IsValid())mark.Parent=parent;
mark.Components.Create<YolkLifetime>();
}
var decal=mark.Components.Create<Decal>();decal.Decals=[new(){ColorTexture=PaintImpactSystem.SplashTexture,Width=1,Height=1,ColorMix=1}];
decal.SortLayer=101;decal.Parallax=0;
decal.Size=new Vector2(11,11);decal.Depth=hit.Normal.z>.75f ? .12f : 6;decal.ColorTint=Yellow;decal.Rotation=Game.Random.Float(0,360);decal.LifeTime=0;decal.Transient=false;decal.AttenuationAngle=.35f;
}
}
protected override void OnUpdate(){age+=Time.Delta;if(age>18)GameObject.Destroy();}
}
public sealed class YolkLifetime : Component
{
private float age;
protected override void OnUpdate(){age+=Time.Delta;if(age>18)GameObject.Destroy();}
}