A game component for an explosive barrel. It handles collision-triggered explosions, spawns particle and sound effects, applies radial impulse to nearby rigidbodies, and can chain-explode other barrels while respecting a ShieldAbility component.
using Sandbox;
using System;
using System.Linq;
public sealed class ExplosiveBarrel : Component, Component.ICollisionListener
{
[Property] public float ExplosionRadius { get; set; } = 450f;
[Property] public float ExplosionForce { get; set; } = 65000f;
[Property] public float ImpactThreshold { get; set; } = 250f;
[Property] public GameObject ExplosionParticle { get; set; }
[Property] public SoundEvent ExplosionSound { get; set; }
private bool _hasExploded = false;
public void Explode()
{
if ( _hasExploded || !GameObject.IsValid() ) return;
_hasExploded = true;
if ( ExplosionParticle.IsValid() )
{
ExplosionParticle.Clone( WorldPosition );
}
if ( ExplosionSound != null )
{
Sound.Play( ExplosionSound, WorldPosition );
}
var hits = Scene.Trace.Sphere( ExplosionRadius, WorldPosition, WorldPosition + Vector3.Up * 2f )
.IgnoreGameObject( GameObject )
.RunAll();
foreach ( var hit in hits )
{
if ( !hit.GameObject.IsValid() ) continue;
var targetObj = hit.GameObject.Root;
var shield = targetObj.Components.Get<ShieldAbility>( true );
if ( shield.IsValid() && shield.IsShieldActive ) continue;
var chainBarrel = targetObj.Components.Get<ExplosiveBarrel>();
if ( chainBarrel.IsValid() && chainBarrel != this )
{
chainBarrel.Explode();
continue;
}
var rb = targetObj.Components.Get<Rigidbody>();
if ( rb.IsValid() )
{
var dir = (hit.GameObject.WorldPosition - WorldPosition).Normal;
dir.z = MathF.Max( dir.z, 0.4f );
rb.ApplyImpulse( dir * ExplosionForce );
}
}
Log.Info( $"[ExplosiveBarrel] Booom! Barrel exploded at {WorldPosition}" );
GameObject.Destroy();
}
public void OnCollisionStart( Collision collision )
{
if ( collision.Contact.Speed.Length > ImpactThreshold )
{
Explode();
}
}
}