using Content.Shared.Clothing; using Content.Shared.Damage.Components; using Content.Shared.Examine; using Content.Shared.FixedPoint; using Content.Shared.Inventory; using Content.Shared.Movement.Systems; namespace Content.Shared.Damage { public sealed class SlowOnDamageSystem : EntitySystem { [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnDamageChanged); SubscribeLocalEvent(OnRefreshMovespeed); SubscribeLocalEvent>(OnModifySpeed); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnGotEquipped); SubscribeLocalEvent(OnGotUnequipped); SubscribeLocalEvent(OnIgnoreStartup); SubscribeLocalEvent(OnIgnoreShutdown); SubscribeLocalEvent(OnIgnoreModifySpeed); } private void OnRefreshMovespeed(EntityUid uid, SlowOnDamageComponent component, RefreshMovementSpeedModifiersEvent args) { if (!EntityManager.TryGetComponent(uid, out var damage)) return; if (damage.TotalDamage == FixedPoint2.Zero) return; // Get closest threshold FixedPoint2 closest = FixedPoint2.Zero; var total = damage.TotalDamage; foreach (var thres in component.SpeedModifierThresholds) { if (total >= thres.Key && thres.Key > closest) closest = thres.Key; } if (closest != FixedPoint2.Zero) { var speed = component.SpeedModifierThresholds[closest]; var ev = new ModifySlowOnDamageSpeedEvent(speed); RaiseLocalEvent(uid, ref ev); args.ModifySpeed(ev.Speed, ev.Speed); } } private void OnDamageChanged(EntityUid uid, SlowOnDamageComponent component, DamageChangedEvent args) { // We -could- only refresh if it crossed a threshold but that would kind of be a lot of duplicated // code and this isn't a super hot path anyway since basically only humans have this _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(uid); } private void OnModifySpeed(Entity ent, ref InventoryRelayedEvent args) { var dif = 1 - args.Args.Speed; if (dif <= 0) return; // reduces the slowness modifier by the given coefficient args.Args.Speed += dif * ent.Comp.Modifier; } private void OnExamined(Entity ent, ref ExaminedEvent args) { var msg = Loc.GetString("slow-on-damage-modifier-examine", ("mod", (1 - ent.Comp.Modifier) * 100)); args.PushMarkup(msg); } private void OnGotEquipped(Entity ent, ref ClothingGotEquippedEvent args) { _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(args.Wearer); } private void OnGotUnequipped(Entity ent, ref ClothingGotUnequippedEvent args) { _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(args.Wearer); } private void OnIgnoreStartup(Entity ent, ref ComponentStartup args) { _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(ent); } private void OnIgnoreShutdown(Entity ent, ref ComponentShutdown args) { _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(ent); } private void OnIgnoreModifySpeed(Entity ent, ref ModifySlowOnDamageSpeedEvent args) { args.Speed = 1f; } } [ByRefEvent] public record struct ModifySlowOnDamageSpeedEvent(float Speed) : IInventoryRelayEvent { public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET; } }