Files
sbox-public/engine/Sandbox.Engine/Scene/Components/Game/RadiusDamage.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

154 lines
4.0 KiB
C#

namespace Sandbox;
/// <summary>
/// Applies damage in a radius, with physics force, and optional occlusion
/// </summary>
[Category( "Game" ), Icon( "flare" ), EditorHandle( Icon = "💥" )]
public sealed class RadiusDamage : Component
{
/// <summary>
/// Should the world shield victims from damage?
/// </summary>
[Property]
public float Radius { get; set; } = 512;
/// <summary>
/// How much physics force should be applied on explosion?
/// </summary>
[Property]
public float PhysicsForceScale { get; set; } = 1;
/// <summary>
/// If enabled we'll apply damage once as soon as enabled
/// </summary>
[Property]
public bool DamageOnEnabled { get; set; } = true;
/// <summary>
/// Should the world shield victims from damage?
/// </summary>
[Property]
public bool Occlusion { get; set; } = true;
/// <summary>
/// The amount of damage inflicted
/// </summary>
[Property]
public float DamageAmount { get; set; } = 100;
/// <summary>
/// Tags to apply to the damage
/// </summary>
[Property]
public TagSet DamageTags { get; set; } = new TagSet();
/// <summary>
/// Who should we credit with this attack?
/// </summary>
[Property]
public GameObject Attacker { get; set; }
protected override void OnEnabled()
{
base.OnEnabled();
if ( DamageOnEnabled )
{
Apply();
}
}
protected override void DrawGizmos()
{
if ( !Gizmo.IsSelected )
return;
Gizmo.Draw.LineSphere( new Sphere( 0, Radius ), 16 );
}
/// <summary>
/// Apply the damage now
/// </summary>
public void Apply()
{
var sphere = new Sphere( WorldPosition, Radius );
var dmg = new DamageInfo();
dmg.Weapon = GameObject;
dmg.Damage = DamageAmount;
dmg.Tags.Add( DamageTags );
dmg.Attacker = Attacker;
ApplyDamage( sphere, dmg, PhysicsForceScale );
}
public static void ApplyDamage( Sphere sphere, DamageInfo damage, float physicsForce = 1, GameObject ignore = null )
{
var scene = Game.ActiveScene;
if ( !scene.IsValid() ) return;
var point = sphere.Center;
var damageAmount = damage.Damage;
var objectsInArea = scene.FindInPhysics( sphere );
var losTrace = scene.Trace.WithTag( "map" ).WithoutTags( "trigger", "gib", "debris", "player" );
foreach ( var rb in objectsInArea.SelectMany( x => x.GetComponents<Rigidbody>() ).Distinct() )
{
if ( rb.IsProxy ) continue;
if ( !rb.MotionEnabled ) continue;
if ( ignore.IsValid() && ignore.IsDescendant( rb.GameObject ) )
continue;
// If the object isn't in line of sight, fuck it off
var tr = losTrace.Ray( point, rb.WorldPosition ).Run();
if ( tr.Hit && tr.GameObject.IsValid() )
{
if ( !rb.GameObject.Root.IsDescendant( tr.GameObject ) )
continue;
}
var dir = (rb.WorldPosition - point).Normal;
var distance = rb.WorldPosition.Distance( sphere.Center );
var forceMagnitude = Math.Clamp( 10000000000f / (distance * distance + 1), 0, 10000000000f );
forceMagnitude += physicsForce * (1 - (distance / sphere.Radius));
rb.ApplyForceAt( point, dir * forceMagnitude );
}
foreach ( var damageable in objectsInArea.SelectMany( x => x.GetComponentsInParent<Component.IDamageable>().Distinct() ) )
{
// no proxy checks needed, it's up to the OnDamage call to filter
var target = damageable as Component;
if ( ignore.IsValid() && ignore.IsDescendant( target.GameObject ) )
continue;
// If the object isn't in line of sight, fuck it off
var tr = losTrace.Ray( point, target.WorldPosition ).Run();
if ( tr.Hit && tr.GameObject.IsValid() )
{
if ( !target.GameObject.Root.IsDescendant( tr.GameObject ) )
continue;
}
var distance = target.WorldPosition.Distance( point );
var distanceLinear = distance / sphere.Radius;
distanceLinear = distanceLinear.Clamp( 0, 1 );
damage.Damage = damageAmount * distanceLinear;
var direction = (target.WorldPosition - point).Normal;
var force = direction * distance * 50f;
damage.Origin = sphere.Center;
damage.Position = tr.HitPosition;
damageable.OnDamage( damage );
}
damage.Damage = damageAmount;
}
}