ExplosiveBarrel.cs

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.

Native Interop
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();
        }
    }
}