using Content.Server._NF.Roadkill.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.Item;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Content.Shared.Projectiles;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Physics.Events;
using Robust.Shared.Prototypes;
namespace Content.Server._NF.Roadkill.Systems;
///
/// Kills and/or gibs entities (useful for space mobs) when they collide with a quickly moving grid.
///
public sealed class RoadkillSystem : EntitySystem
{
[Dependency] private readonly PhysicsSystem _physics = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
private readonly ProtoId _bluntDamageType = "Blunt";
private readonly FixedPoint2 _extraDamage = 20;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnRoadkillCollide);
}
private void OnRoadkillCollide(Entity ent, ref StartCollideEvent args)
{
var ourXform = Transform(ent);
var otherXform = Transform(args.OtherEntity);
// Roadkill collision: roadkillable thing might not be on a grid (e.g. it flew in onto a lattice grid but slams into a wall at high speed)
// but the thing it collides with should be on a grid (not space) and not be an item
if (ourXform.MapUid == null
|| ourXform.MapUid != otherXform.MapUid
|| otherXform.GridUid == null
|| HasComp(args.OtherEntity)
|| HasComp(args.OtherEntity))
return;
var ourVelocity = _physics.GetMapLinearVelocity(ent, args.OurBody, ourXform);
var otherVelocity = _physics.GetMapLinearVelocity(args.OtherEntity, args.OtherBody, otherXform);
var jungleDiff = (ourVelocity - otherVelocity).Length();
if (jungleDiff >= ent.Comp.DestroySpeed)
{
// Play audio following the colliding entity (presumably more stable for doppler than a static position)
if (ent.Comp.DestroySound != null)
_audio.PlayPvs(_audio.ResolveSound(ent.Comp.DestroySound), args.OtherEntity);
QueueDel(ent);
}
else if (jungleDiff >= ent.Comp.KillSpeed)
{
if (_mobState.IsDead(ent))
return;
// Try to apply damage if this thing can take damage.
if (_mobThreshold.TryGetThresholdForState(ent, MobState.Dead, out var threshold) &&
TryComp(ent, out var damageableComponent) &&
damageableComponent.TotalDamage < threshold)
{
var damage = new DamageSpecifier();
damage.DamageDict[_bluntDamageType] = threshold.Value - damageableComponent.TotalDamage + _extraDamage;
_damageable.TryChangeDamage(ent, damage, ignoreResistances: true);
}
_mobState.ChangeMobState(ent, MobState.Dead);
}
}
}