A Grenade projectile class. It counts up AliveTime, explodes after ExplosionFuse or on collision, applies direct impact damage to a hit player then simulates an explosion trace to apply area damage to nearby damage components, and destroys its GameObject.
using Sandbox.Events;
using System;
namespace KOTH;
public sealed class Grenade : Projectile, IGameEventHandler<ProjectileCollideEvent>
{
[Property, Group("Explosion")] public float ExplosionFuse { get; set; } = 3f;
[Property, Group("Explosion")] public GameObject ExplosionPrefab { get; set; }
private TimeSince AliveTime = new();
protected override void OnStart()
{
base.OnStart();
AliveTime = 0;
}
protected override void OnUpdate()
{
base.OnUpdate();
// explode on our own accord //
if (AliveTime >= ExplosionFuse)
{
FProjectileCollision ProjectileCollision;
SimulateExplode(out ProjectileCollision, WorldPosition);
foreach (var DamageComponent in ProjectileCollision.TracedDamageComponents)
{
if (!DamageComponent.IsValid())
{
continue;
}
FDamageRequest DamageRequest = new()
{
TargetDamageComponent = DamageComponent,
AttackerPlayerPawn = OwnerPlayerPawn,
DamageOrigin = ProjectileCollision.HitLocation,
TargetOrigin = DamageComponent.WorldPosition,
BaseDamage = (BaseDamage * .33f).FloorToInt(),
BaseKnockbackStrength = BaseKnockbackStrength,
DamageType = EDamageType.Projectile,
DamageFalloffType = EDamageFalloffType.Falloff,
MaxDamageImpactDistance = ExplosionRadius,
};
if (DamageComponent.GameObject.GetComponent<PlayerPawn>() is { } PlayerPawn)
{
DamageRequest.TargetPlayerPawn = PlayerPawn;
DamageRequest.TargetOrigin = PlayerPawn.CenterPosition;
}
Scene.Dispatch(new DamageRequestEvent(DamageRequest));
}
GameObject.Root.Destroy();
}
}
public void OnGameEvent(ProjectileCollideEvent EventArgs)
{
var Collision = EventArgs.ProjectileCollision;
// the guy we hit ////////////////////////////////////////////////////////
var HitPlayerPawn = Collision.HitObject.Root.Components.Get<PlayerPawn>();
if (!HitPlayerPawn.IsValid())
{
return;
}
FDamageRequest DirectDamageRequest = new()
{
TargetDamageComponent = HitPlayerPawn.DamageComponent,
AttackerPlayerPawn = OwnerPlayerPawn,
TargetPlayerPawn = HitPlayerPawn,
DamageOrigin = Collision.HitLocation,
TargetOrigin = HitPlayerPawn.CenterPosition,
BaseDamage = BaseDamage,
BaseKnockbackStrength = BaseKnockbackStrength,
DirectImpact = true,
DamageType = EDamageType.Projectile,
DamageFalloffType = EDamageFalloffType.None,
MaxDamageImpactDistance = ExplosionRadius,
};
Scene.Dispatch(new DamageRequestEvent(DirectDamageRequest));
// explode on them //////////////////////
FProjectileCollision ProjectileCollision;
SimulateExplode(out ProjectileCollision, Collision.HitLocation);
foreach (var DamageComponent in ProjectileCollision.TracedDamageComponents)
{
if (!DamageComponent.IsValid() || HitPlayerPawn.DamageComponent == DamageComponent)
{
continue;
}
FDamageRequest DamageRequest = new()
{
TargetDamageComponent = DamageComponent,
AttackerPlayerPawn = OwnerPlayerPawn,
DamageOrigin = ProjectileCollision.HitLocation,
TargetOrigin = DamageComponent.WorldPosition,
BaseDamage = (BaseDamage * .66f).FloorToInt(),
BaseKnockbackStrength = BaseKnockbackStrength,
DamageType = EDamageType.Projectile,
DamageFalloffType = EDamageFalloffType.Falloff,
MaxDamageImpactDistance = ExplosionRadius,
};
if (DamageComponent.GameObject.GetComponent<PlayerPawn>() is { } PlayerPawn)
{
DamageRequest.TargetPlayerPawn = PlayerPawn;
DamageRequest.TargetOrigin = PlayerPawn.CenterPosition;
}
Scene.Dispatch(new DamageRequestEvent(DamageRequest));
}
GameObject.Root.Destroy();
}
}