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; using Content.Shared.StatusEffectNew; namespace Content.Shared.Damage.Systems; public sealed partial class SlowOnDamageSystem : EntitySystem { [Dependency] private MovementSpeedModifierSystem _movementSpeedModifierSystem = default!; [Dependency] private DamageableSystem _damage = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnDamageChanged); SubscribeLocalEvent(OnRefreshMovespeed); SubscribeLocalEvent>(OnModifySpeed); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnGotEquipped); SubscribeLocalEvent(OnGotUnequipped); SubscribeLocalEvent(OnIgnoreApplied); SubscribeLocalEvent(OnIgnoreRemoved); SubscribeLocalEvent>(OnIgnoreModifySpeed); } private void OnRefreshMovespeed(EntityUid uid, SlowOnDamageComponent component, RefreshMovementSpeedModifiersEvent args) { if (!TryComp(uid, out var damage)) return; var totalDamage = _damage.GetTotalDamage((uid, damage)); if (totalDamage == FixedPoint2.Zero) return; // Get closest threshold FixedPoint2 closest = FixedPoint2.Zero; var total = 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", 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 OnIgnoreApplied(Entity ent, ref StatusEffectAppliedEvent args) { _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(args.Target); } private void OnIgnoreRemoved(Entity ent, ref StatusEffectRemovedEvent args) { _movementSpeedModifierSystem.RefreshMovementSpeedModifiers(args.Target); } private void OnIgnoreModifySpeed(Entity ent, ref StatusEffectRelayedEvent args) { args.Args = args.Args with { Speed = 1f }; } } [ByRefEvent] public record struct ModifySlowOnDamageSpeedEvent(float Speed) : IInventoryRelayEvent { public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET; }