From d0c5110b7b1b66545193fd2b15ee965cfdaed130 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Mon, 9 Sep 2024 00:14:44 -0400 Subject: [PATCH] Step Trigger Minor Refactor (#884) # Description I made this PR in response to a bug report from Floof, where it was discovered that a "Minimum Size Felinid or Harpy" is instantaneously killed by mouse traps. Which confused me, because Felinids and Harpies are intended to be immune to floor traps like landmines, glass, and mouse traps. Then I discovered that mouse traps overwrite the step trigger cancellation, meaning that mouse traps will just completely ignore a Felinid/Harpy canceling the step trigger. Additionally, to my endless frustration, the Felinid/Harpy floor trap immunity is handled by a Tag and not a Component, which isn't really acceptable in this day and age. I decided to take a little bit of a different approach to this problem, first by doing the usual code cleanup to EE standards. Then by adding a new StepTriggerImmuneComponent. This component acts as an early-exit for the entire StepTriggerSystem, immediately at the initial entrypoint, during the CanTrigger bool. This component is given to Felinids and Harpies by default, representing their "Extremely low density bodies" having too much surface area and not enough mass to trigger floor traps. Effectively, they are now working as originally intended, by having immunity to setting off landmines. Because we have a trait point system, and this is coincidentally also a trait requested by Nuclear14, I have gone ahead and created a Trait that gives this component to anyone willing to pay the points for it. # Changelog :cl: - fix: Felinids and Harpies will now correctly never set off floor traps, such as landmines and mouse traps. - add: Trap Avoider has been added as a new trait, allowing characters to buy the innate ability to avoid floor traps. I would have named this "Light Step", after the trait from Fallout that shares its name and effect, but someone already gave that name to a different trait entirely. --------- Signed-off-by: VMSolidus Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> --- Content.Server/Mousetrap/MousetrapSystem.cs | 25 +++---- .../Components/StepTriggerImmuneComponent.cs | 15 ++++ .../StepTrigger/Systems/StepTriggerSystem.cs | 74 ++++++------------- Resources/Locale/en-US/traits/traits.ftl | 5 ++ .../Entities/Mobs/Species/harpy.yml | 1 + .../Entities/Mobs/Species/felinid.yml | 1 + Resources/Prototypes/Traits/skills.yml | 13 ++++ 7 files changed, 66 insertions(+), 68 deletions(-) create mode 100644 Content.Shared/StepTrigger/Components/StepTriggerImmuneComponent.cs diff --git a/Content.Server/Mousetrap/MousetrapSystem.cs b/Content.Server/Mousetrap/MousetrapSystem.cs index e3aaab364dd..2d4fed27992 100644 --- a/Content.Server/Mousetrap/MousetrapSystem.cs +++ b/Content.Server/Mousetrap/MousetrapSystem.cs @@ -2,13 +2,9 @@ using Content.Server.Explosion.EntitySystems; using Content.Server.Popups; using Content.Shared.Interaction.Events; -using Content.Shared.Inventory; using Content.Shared.Mousetrap; -using Content.Shared.StepTrigger; using Content.Shared.StepTrigger.Systems; -using Robust.Server.GameObjects; using Robust.Shared.Physics.Components; -using Robust.Shared.Player; namespace Content.Server.Mousetrap; @@ -44,15 +40,16 @@ private void OnStepTriggerAttempt(EntityUid uid, MousetrapComponent component, r private void BeforeDamageOnTrigger(EntityUid uid, MousetrapComponent component, BeforeDamageUserOnTriggerEvent args) { - if (TryComp(args.Tripper, out PhysicsComponent? physics) && physics.Mass != 0) - { - // The idea here is inverse, - // Small - big damage, - // Large - small damage - // yes i punched numbers into a calculator until the graph looked right - var scaledDamage = -50 * Math.Atan(physics.Mass - component.MassBalance) + (25 * Math.PI); - args.Damage *= scaledDamage; - } + if (!TryComp(args.Tripper, out var physics) + || physics.Mass is 0) + return; + + // The idea here is inverse, + // Small - big damage, + // Large - small damage + // Yes, I punched numbers into a calculator until the graph looked right + var scaledDamage = -50 * MathF.Atan(physics.Mass - component.MassBalance) + 25 * MathF.PI; + args.Damage *= scaledDamage; } private void OnTrigger(EntityUid uid, MousetrapComponent component, TriggerEvent args) @@ -64,9 +61,7 @@ private void OnTrigger(EntityUid uid, MousetrapComponent component, TriggerEvent private void UpdateVisuals(EntityUid uid, MousetrapComponent? mousetrap = null, AppearanceComponent? appearance = null) { if (!Resolve(uid, ref mousetrap, ref appearance, false)) - { return; - } _appearance.SetData(uid, MousetrapVisuals.Visual, mousetrap.IsActive ? MousetrapVisuals.Armed : MousetrapVisuals.Unarmed, appearance); diff --git a/Content.Shared/StepTrigger/Components/StepTriggerImmuneComponent.cs b/Content.Shared/StepTrigger/Components/StepTriggerImmuneComponent.cs new file mode 100644 index 00000000000..4321334a3ae --- /dev/null +++ b/Content.Shared/StepTrigger/Components/StepTriggerImmuneComponent.cs @@ -0,0 +1,15 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.StepTrigger.Components; + +/// +/// This component marks an entity as being immune to all step triggers. +/// For example, a Felinid or Harpy being so low density, that they don't set off landmines. +/// +/// +/// This is the "Earliest Possible Exit" method, and therefore isn't possible to un-cancel. +/// It will prevent ALL step trigger events from firing. Therefore there may sometimes be unintended consequences to this. +/// Consider using a subscription to StepTriggerAttemptEvent if you wish to be more selective. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class StepTriggerImmuneComponent : Component { } diff --git a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs index d81ad754d1e..89655afac23 100644 --- a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs +++ b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs @@ -41,9 +41,7 @@ public override void Update(float frameTime) while (enumerator.MoveNext(out var uid, out var active, out var trigger, out var transform)) { if (!Update(uid, trigger, transform, query)) - { continue; - } RemCompDeferred(uid, active); } @@ -51,11 +49,8 @@ public override void Update(float frameTime) private bool Update(EntityUid uid, StepTriggerComponent component, TransformComponent transform, EntityQuery query) { - if (!component.Active || - component.Colliding.Count == 0) - { + if (!component.Active || component.Colliding.Count == 0) return true; - } if (component.Blacklist != null && TryComp(transform.GridUid, out var grid)) { @@ -67,17 +62,13 @@ private bool Update(EntityUid uid, StepTriggerComponent component, TransformComp if (ent == uid) continue; - if (component.Blacklist.IsValid(ent.Value, EntityManager) == true) - { + if (component.Blacklist.IsValid(ent.Value, EntityManager)) return false; - } } } foreach (var otherUid in component.Colliding) - { UpdateColliding(uid, component, transform, otherUid, query); - } return false; } @@ -95,9 +86,8 @@ private void UpdateColliding(EntityUid uid, StepTriggerComponent component, Tran if (!ourAabb.Intersects(otherAabb)) { if (component.CurrentlySteppedOn.Remove(otherUid)) - { Dirty(uid, component); - } + return; } @@ -109,9 +99,7 @@ private void UpdateColliding(EntityUid uid, StepTriggerComponent component, Tran || component.CurrentlySteppedOn.Contains(otherUid) || ratio < component.IntersectRatio || !CanTrigger(uid, otherUid, component)) - { return; - } if (component.StepOn) { @@ -130,7 +118,9 @@ private void UpdateColliding(EntityUid uid, StepTriggerComponent component, Tran private bool CanTrigger(EntityUid uid, EntityUid otherUid, StepTriggerComponent component) { - if (!component.Active || component.CurrentlySteppedOn.Contains(otherUid)) + if (HasComp(otherUid) + || !component.Active + || component.CurrentlySteppedOn.Contains(otherUid)) return false; // Can't trigger if we don't ignore weightless entities @@ -141,7 +131,6 @@ private bool CanTrigger(EntityUid uid, EntityUid otherUid, StepTriggerComponent return false; var msg = new StepTriggerAttemptEvent { Source = uid, Tripper = otherUid }; - RaiseLocalEvent(uid, ref msg); return msg.Continue && !msg.Cancelled; @@ -151,18 +140,14 @@ private void OnStartCollide(EntityUid uid, StepTriggerComponent component, ref S { var otherUid = args.OtherEntity; - if (!args.OtherFixture.Hard) - return; - - if (!CanTrigger(uid, otherUid, component)) + if (!args.OtherFixture.Hard + || !CanTrigger(uid, otherUid, component)) return; EnsureComp(uid); if (component.Colliding.Add(otherUid)) - { Dirty(uid, component); - } } private void OnEndCollide(EntityUid uid, StepTriggerComponent component, ref EndCollideEvent args) @@ -182,29 +167,21 @@ private void OnEndCollide(EntityUid uid, StepTriggerComponent component, ref End } if (component.Colliding.Count == 0) - { RemCompDeferred(uid); - } } private void TriggerHandleState(EntityUid uid, StepTriggerComponent component, ref AfterAutoHandleStateEvent args) { if (component.Colliding.Count > 0) - { EnsureComp(uid); - } else - { RemCompDeferred(uid); - } } public void SetIntersectRatio(EntityUid uid, float ratio, StepTriggerComponent? component = null) { - if (!Resolve(uid, ref component)) - return; - - if (MathHelper.CloseToPercent(component.IntersectRatio, ratio)) + if (!Resolve(uid, ref component) + || MathHelper.CloseToPercent(component.IntersectRatio, ratio)) return; component.IntersectRatio = ratio; @@ -213,10 +190,8 @@ public void SetIntersectRatio(EntityUid uid, float ratio, StepTriggerComponent? public void SetRequiredTriggerSpeed(EntityUid uid, float speed, StepTriggerComponent? component = null) { - if (!Resolve(uid, ref component)) - return; - - if (MathHelper.CloseToPercent(component.RequiredTriggeredSpeed, speed)) + if (!Resolve(uid, ref component) + || MathHelper.CloseToPercent(component.RequiredTriggeredSpeed, speed)) return; component.RequiredTriggeredSpeed = speed; @@ -225,10 +200,8 @@ public void SetRequiredTriggerSpeed(EntityUid uid, float speed, StepTriggerCompo public void SetActive(EntityUid uid, bool active, StepTriggerComponent? component = null) { - if (!Resolve(uid, ref component)) - return; - - if (active == component.Active) + if (!Resolve(uid, ref component) + || active == component.Active) return; component.Active = active; @@ -236,26 +209,21 @@ public void SetActive(EntityUid uid, bool active, StepTriggerComponent? componen } } +/// +/// Raised at the beginning of a step trigger, and before entering the checks. +/// Allows for entities to end the steptrigger early via args.Cancelled. +/// [ByRefEvent] -public struct StepTriggerAttemptEvent -{ - public EntityUid Source; - public EntityUid Tripper; - public bool Continue; - /// - /// Set by systems which wish to cancel the step trigger event, regardless of event ordering. - /// - public bool Cancelled; -} +public record struct StepTriggerAttemptEvent(EntityUid Source, EntityUid Tripper, bool Continue, bool Cancelled); /// -/// Raised when an entity stands on a steptrigger initially (assuming it has both on and off states). +/// Raised when an entity stands on a steptrigger initially (assuming it has both on and off states). /// [ByRefEvent] public readonly record struct StepTriggeredOnEvent(EntityUid Source, EntityUid Tripper); /// -/// Raised when an entity leaves a steptrigger if it has on and off states OR when an entity intersects a steptrigger. +/// Raised when an entity leaves a steptrigger if it has on and off states OR when an entity intersects a steptrigger. /// [ByRefEvent] public readonly record struct StepTriggeredOffEvent(EntityUid Source, EntityUid Tripper); diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 219d725c17a..e049af3c62b 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -217,6 +217,11 @@ trait-description-NaturalTelepath = drawbacks of Latent Psychic, except that you are guaranteed to start with full Telepathy. You may still gain powers as normal for a Latent Psychic. +trait-name-TrapAvoider = Trap Avoider +trait-description-TrapAvoider = + You possess a preturnatural sense of traps, and will unconsciously avoid them. You will never trigger + floor traps, such as land mines, tripwires, mouse traps(If you're small enough), etc. + trait-name-AnomalousPositronics = Anomalous Positronics trait-description-AnomalousPositronics = Whether by intentional design from the manufacturer, black market modifications, or accidental omission, diff --git a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml index f4055170b4c..788b21eafb0 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml @@ -118,6 +118,7 @@ understands: - GalacticCommon - SolCommon + - type: StepTriggerImmune - type: entity save: false diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml index 285c7340b21..a1ca357b080 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml @@ -79,6 +79,7 @@ stealth: Subtle stripTimeReduction: 0 stripTimeMultiplier: 0.667 + - type: StepTriggerImmune - type: entity save: false diff --git a/Resources/Prototypes/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index 0f0d9f8e990..d757cbcd564 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -264,6 +264,19 @@ traits: - AnomalousPositronics +- type: trait + id: TrapAvoider + category: Physical + points: -3 + components: + - type: StepTriggerImmune + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Felinid + - Harpy + - type: trait id: AnomalousPositronics category: Mental