ShotgunShellEjection.cs
using Sandbox;
using System;
namespace PaintballBaddies;
/// <summary>Small local cosmetic casings; no network bodies or gameplay collision.</summary>
public sealed class ShotgunShellEjection : Component
{
private Vector3 velocity;
private float age;
private int bounces;
public static int Ejected {get;private set;}
public static void Spawn(Scene scene,GameObject shooter,Vector3 muzzle,Vector3 direction)
{
var model=Model.Load(OverdriveAssets.ShellPath);if(model.IsError)return;
foreach(var old in scene.GetAllComponents<ShotgunShellEjection>().OrderByDescending(e=>e.age).SkipLast(23).ToArray())old.GameObject.Destroy();
var anchor=shooter.IsValid() ? shooter.Components.Get<PaintballMarker>()?.PresentationAnchor ?? shooter.Components.Get<ArenaOpponent>()?.PresentationAnchor : null;
var rotation=Rotation.LookAt(direction);
var port=anchor.IsValid() ? anchor.WorldTransform.PointToWorld((shooter.Components.Get<OverdriveLoadout>()?.FittedOffset ?? OverdriveLoadout.ShotgunOffset)+new Vector3(2.5f,-1.4f,4)) : muzzle-direction*17;
var go=new GameObject(false,"Ejected shotgun shell"){NetworkMode=NetworkMode.Never,WorldPosition=port,WorldRotation=rotation,WorldScale=new Vector3(1.25f)};
go.Tags.Add("paintball_debris");go.Components.Create<ModelRenderer>().Model=model;
go.Components.Create<ShotgunShellEjection>().velocity=rotation.Right*95+Vector3.Up*70+direction*12;go.Enabled=true;Ejected++;
}
protected override void OnUpdate()
{
age+=Time.Delta;if(age>2.2f){GameObject.Destroy();return;}
if(bounces>=2)return;
float dt=MathF.Min(Time.Delta,.04f);velocity+=Vector3.Down*600*dt;
var end=WorldPosition+velocity*dt;
var hit=Scene.Trace.Ray(WorldPosition,end).WithoutTags("paintball_actor","overdrive_enemy","paintball_debris").Run();
WorldPosition=hit.Hit ? hit.HitPosition+hit.Normal*.5f : end;
if(hit.Hit){velocity=(velocity-2*Vector3.Dot(velocity,hit.Normal)*hit.Normal)*.32f;bounces++;}
WorldRotation*=Rotation.From(new Angles(440*dt,300*dt,160*dt));
}
}