diff --git a/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs b/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs index 866ce38a57..8dc496ec25 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs @@ -15,6 +15,13 @@ public sealed partial class ClothingSpeedModifierComponent : Component [DataField] public float SprintModifier = 1.0f; + + /// + /// An optional required standing state. + /// Set to true if you need to be standing, false if you need to not be standing, null if you don't care. + /// + [DataField] + public bool? Standing; } [Serializable, NetSerializable] diff --git a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs index 56758654ed..29f063c7fa 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Inventory; using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Movement.Systems; +using Content.Shared.Standing; using Content.Shared.Verbs; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -12,10 +13,11 @@ namespace Content.Shared.Clothing; public sealed class ClothingSpeedModifierSystem : EntitySystem { - [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; - [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; public override void Initialize() { @@ -54,8 +56,13 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent args) { - if (_toggle.IsActivated(uid)) - args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); + if (!_toggle.IsActivated(uid)) + return; + + if (component.Standing != null && !_standing.IsMatchingState(args.Owner, component.Standing.Value)) + return; + + args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); } private void OnClothingVerbExamine(EntityUid uid, ClothingSpeedModifierComponent component, GetVerbsEvent args) diff --git a/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs index 636a21533e..554db51a23 100644 --- a/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs @@ -1,23 +1,33 @@ using Content.Shared.Clothing.Components; using Content.Shared.Gravity; using Content.Shared.Inventory; +using Content.Shared.Standing; namespace Content.Shared.Clothing.EntitySystems; +/// +/// We check standing state on all clothing because we don't want you to have anti-gravity unless you're standing. +/// This is for balance reasons as it prevents you from wearing anti-grav clothing to cheese being stun cuffed, as +/// well as other worse things. +/// public sealed class AntiGravityClothingSystem : EntitySystem { - [Dependency] SharedGravitySystem _gravity = default!; + [Dependency] private readonly StandingStateSystem _standing = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + /// public override void Initialize() { SubscribeLocalEvent>(OnIsWeightless); SubscribeLocalEvent(OnEquipped); SubscribeLocalEvent(OnUnequipped); + SubscribeLocalEvent>(OnDowned); + SubscribeLocalEvent>(OnStood); } private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) { - if (args.Args.Handled) + if (args.Args.Handled || _standing.IsDown(args.Owner)) return; args.Args.Handled = true; @@ -26,11 +36,29 @@ public sealed class AntiGravityClothingSystem : EntitySystem private void OnEquipped(Entity entity, ref ClothingGotEquippedEvent args) { + // This clothing item does nothing if we're not standing + if (_standing.IsDown(args.Wearer)) + return; + _gravity.RefreshWeightless(args.Wearer, true); } private void OnUnequipped(Entity entity, ref ClothingGotUnequippedEvent args) { + // This clothing item does nothing if we're not standing + if (_standing.IsDown(args.Wearer)) + return; + _gravity.RefreshWeightless(args.Wearer, false); } + + private void OnDowned(Entity entity, ref InventoryRelayedEvent args) + { + _gravity.RefreshWeightless(args.Owner, false); + } + + private void OnStood(Entity entity, ref InventoryRelayedEvent args) + { + _gravity.RefreshWeightless(args.Owner, true); + } } diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index f4a0ccb5de..5109930a2d 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -24,6 +24,7 @@ using Content.Shared.Overlays; using Content.Shared.Projectiles; using Content.Shared.Radio; using Content.Shared.Slippery; +using Content.Shared.Standing; using Content.Shared.Strip.Components; using Content.Shared.Temperature; using Content.Shared.Verbs; @@ -57,6 +58,8 @@ public partial class InventorySystem SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent(RelayInventoryEvent); // by-ref events SubscribeLocalEvent(RefRelayInventoryEvent); @@ -114,7 +117,7 @@ public partial class InventorySystem return; // this copies the by-ref event if it is a struct - var ev = new InventoryRelayedEvent(args); + var ev = new InventoryRelayedEvent(args, inventory.Owner); var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots); while (enumerator.NextItem(out var item)) { @@ -130,7 +133,7 @@ public partial class InventorySystem if (args.TargetSlots == SlotFlags.NONE) return; - var ev = new InventoryRelayedEvent(args); + var ev = new InventoryRelayedEvent(args, inventory.Owner); var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots); while (enumerator.NextItem(out var item)) { @@ -141,7 +144,7 @@ public partial class InventorySystem private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent args) { // Automatically relay stripping related verbs to all equipped clothing. - var ev = new InventoryRelayedEvent>(args); + var ev = new InventoryRelayedEvent>(args, uid); var enumerator = new InventorySlotEnumerator(component); while (enumerator.NextItem(out var item, out var slotDef)) { @@ -153,7 +156,7 @@ public partial class InventorySystem private void OnGetInnateVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent args) { // Automatically relay stripping related verbs to all equipped clothing. - var ev = new InventoryRelayedEvent>(args); + var ev = new InventoryRelayedEvent>(args, uid); var enumerator = new InventorySlotEnumerator(component, SlotFlags.WITHOUT_POCKET); while (enumerator.NextItem(out var item)) { @@ -176,9 +179,12 @@ public sealed class InventoryRelayedEvent : EntityEventArgs { public TEvent Args; - public InventoryRelayedEvent(TEvent args) + public EntityUid Owner; + + public InventoryRelayedEvent(TEvent args, EntityUid owner) { Args = args; + Owner = owner; } } diff --git a/Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs b/Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs index 630135f36a..d11695321b 100644 --- a/Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs +++ b/Content.Shared/Morgue/EntityStorageLayingDownOverrideSystem.cs @@ -21,7 +21,7 @@ public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem { // Explicitly check for standing state component, as entities without it will return false for IsDown() // which prevents inserting any kind of non-mobs into this container (which is unintended) - if (TryComp(ent, out var standingState) && !_standing.IsDown(ent, standingState)) + if (TryComp(ent, out var standingState) && !_standing.IsDown((ent, standingState))) args.Contents.Remove(ent); } } diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index 7f3b8c9aac..ffe8f2c156 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Climbing.Events; using Content.Shared.Hands.Components; +using Content.Shared.Inventory; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; using Content.Shared.Physics; @@ -69,12 +70,17 @@ public sealed class StandingStateSystem : EntitySystem ChangeLayers(entity); } - public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) + public bool IsMatchingState(Entity entity, bool standing) { - if (!Resolve(uid, ref standingState, false)) + return standing != IsDown(entity); + } + + public bool IsDown(Entity entity) + { + if (!Resolve(entity, ref entity.Comp, false)) return false; - return !standingState.Standing; + return !entity.Comp.Standing; } public bool Down(EntityUid uid, @@ -213,29 +219,27 @@ public record struct DropHandItemsEvent(); /// /// Subscribe if you can potentially block a down attempt. /// -public sealed class DownAttemptEvent : CancellableEntityEventArgs -{ -} +public sealed class DownAttemptEvent : CancellableEntityEventArgs; /// /// Subscribe if you can potentially block a stand attempt. /// -public sealed class StandAttemptEvent : CancellableEntityEventArgs -{ -} +public sealed class StandAttemptEvent : CancellableEntityEventArgs; /// /// Raised when an entity becomes standing /// -public sealed class StoodEvent : EntityEventArgs +public sealed class StoodEvent : EntityEventArgs, IInventoryRelayEvent { -} + public SlotFlags TargetSlots { get; } = SlotFlags.FEET; +}; /// /// Raised when an entity is not standing /// -public sealed class DownedEvent : EntityEventArgs +public sealed class DownedEvent : EntityEventArgs, IInventoryRelayEvent { + public SlotFlags TargetSlots { get; } = SlotFlags.FEET; } /// diff --git a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs index 098e3176d9..1a7bc88ec3 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.Knockdown.cs @@ -501,7 +501,7 @@ public abstract partial class SharedStunSystem // Targeted moth attack CancelKnockdownDoAfter((entity, entity.Comp)); - RemComp(entity); + RemCompDeferred(entity); } private void OnKnockdownAttempt(Entity entity, ref KnockDownAttemptEvent args) diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml index 26fbc0c7ae..69c167051b 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml @@ -135,6 +135,7 @@ - type: ClothingSpeedModifier walkModifier: 1.5 sprintModifier: 1.5 + standing: true - type: Appearance - type: GenericVisualizer visuals: @@ -182,7 +183,7 @@ price: 75 - type: Tag tags: [ ] - + - type: entity parent: ClothingShoesBase id: ClothingShoesBootsJump @@ -200,7 +201,7 @@ jumpSound: /Audio/Effects/stealthoff.ogg - type: ActionGrant actions: - - ActionGravityJump + - ActionGravityJump - type: ItemActionGrant actions: - ActionGravityJump