Prevent shoe buffs while crawling (#39648)

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
Princess Cheeseballs
2025-08-20 04:08:31 -07:00
committed by GitHub
parent 95b0df9a89
commit 47dd036ef2
8 changed files with 80 additions and 27 deletions

View File

@@ -15,6 +15,13 @@ public sealed partial class ClothingSpeedModifierComponent : Component
[DataField] [DataField]
public float SprintModifier = 1.0f; public float SprintModifier = 1.0f;
/// <summary>
/// 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.
/// </summary>
[DataField]
public bool? Standing;
} }
[Serializable, NetSerializable] [Serializable, NetSerializable]

View File

@@ -3,6 +3,7 @@ using Content.Shared.Inventory;
using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle;
using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Content.Shared.Standing;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
@@ -12,10 +13,11 @@ namespace Content.Shared.Clothing;
public sealed class ClothingSpeedModifierSystem : EntitySystem public sealed class ClothingSpeedModifierSystem : EntitySystem
{ {
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
[Dependency] private readonly ItemToggleSystem _toggle = 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() public override void Initialize()
{ {
@@ -54,7 +56,12 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem
private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent> args) private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent> args)
{ {
if (_toggle.IsActivated(uid)) if (!_toggle.IsActivated(uid))
return;
if (component.Standing != null && !_standing.IsMatchingState(args.Owner, component.Standing.Value))
return;
args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier);
} }

View File

@@ -1,23 +1,33 @@
using Content.Shared.Clothing.Components; using Content.Shared.Clothing.Components;
using Content.Shared.Gravity; using Content.Shared.Gravity;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Standing;
namespace Content.Shared.Clothing.EntitySystems; namespace Content.Shared.Clothing.EntitySystems;
/// <remarks>
/// 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.
/// </remarks>
public sealed class AntiGravityClothingSystem : EntitySystem public sealed class AntiGravityClothingSystem : EntitySystem
{ {
[Dependency] SharedGravitySystem _gravity = default!; [Dependency] private readonly StandingStateSystem _standing = default!;
[Dependency] private readonly SharedGravitySystem _gravity = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
SubscribeLocalEvent<AntiGravityClothingComponent, InventoryRelayedEvent<IsWeightlessEvent>>(OnIsWeightless); SubscribeLocalEvent<AntiGravityClothingComponent, InventoryRelayedEvent<IsWeightlessEvent>>(OnIsWeightless);
SubscribeLocalEvent<AntiGravityClothingComponent, ClothingGotEquippedEvent>(OnEquipped); SubscribeLocalEvent<AntiGravityClothingComponent, ClothingGotEquippedEvent>(OnEquipped);
SubscribeLocalEvent<AntiGravityClothingComponent, ClothingGotUnequippedEvent>(OnUnequipped); SubscribeLocalEvent<AntiGravityClothingComponent, ClothingGotUnequippedEvent>(OnUnequipped);
SubscribeLocalEvent<AntiGravityClothingComponent, InventoryRelayedEvent<DownedEvent>>(OnDowned);
SubscribeLocalEvent<AntiGravityClothingComponent, InventoryRelayedEvent<StoodEvent>>(OnStood);
} }
private void OnIsWeightless(Entity<AntiGravityClothingComponent> ent, ref InventoryRelayedEvent<IsWeightlessEvent> args) private void OnIsWeightless(Entity<AntiGravityClothingComponent> ent, ref InventoryRelayedEvent<IsWeightlessEvent> args)
{ {
if (args.Args.Handled) if (args.Args.Handled || _standing.IsDown(args.Owner))
return; return;
args.Args.Handled = true; args.Args.Handled = true;
@@ -26,11 +36,29 @@ public sealed class AntiGravityClothingSystem : EntitySystem
private void OnEquipped(Entity<AntiGravityClothingComponent> entity, ref ClothingGotEquippedEvent args) private void OnEquipped(Entity<AntiGravityClothingComponent> 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); _gravity.RefreshWeightless(args.Wearer, true);
} }
private void OnUnequipped(Entity<AntiGravityClothingComponent> entity, ref ClothingGotUnequippedEvent args) private void OnUnequipped(Entity<AntiGravityClothingComponent> 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); _gravity.RefreshWeightless(args.Wearer, false);
} }
private void OnDowned(Entity<AntiGravityClothingComponent> entity, ref InventoryRelayedEvent<DownedEvent> args)
{
_gravity.RefreshWeightless(args.Owner, false);
}
private void OnStood(Entity<AntiGravityClothingComponent> entity, ref InventoryRelayedEvent<StoodEvent> args)
{
_gravity.RefreshWeightless(args.Owner, true);
}
} }

View File

@@ -24,6 +24,7 @@ using Content.Shared.Overlays;
using Content.Shared.Projectiles; using Content.Shared.Projectiles;
using Content.Shared.Radio; using Content.Shared.Radio;
using Content.Shared.Slippery; using Content.Shared.Slippery;
using Content.Shared.Standing;
using Content.Shared.Strip.Components; using Content.Shared.Strip.Components;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Content.Shared.Verbs; using Content.Shared.Verbs;
@@ -57,6 +58,8 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, IsUnequippingTargetAttemptEvent>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, IsUnequippingTargetAttemptEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, ChameleonControllerOutfitSelectedEvent>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, ChameleonControllerOutfitSelectedEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, BeforeEmoteEvent>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, BeforeEmoteEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, StoodEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, DownedEvent>(RelayInventoryEvent);
// by-ref events // by-ref events
SubscribeLocalEvent<InventoryComponent, RefreshFrictionModifiersEvent>(RefRelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, RefreshFrictionModifiersEvent>(RefRelayInventoryEvent);
@@ -114,7 +117,7 @@ public partial class InventorySystem
return; return;
// this copies the by-ref event if it is a struct // this copies the by-ref event if it is a struct
var ev = new InventoryRelayedEvent<T>(args); var ev = new InventoryRelayedEvent<T>(args, inventory.Owner);
var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots); var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots);
while (enumerator.NextItem(out var item)) while (enumerator.NextItem(out var item))
{ {
@@ -130,7 +133,7 @@ public partial class InventorySystem
if (args.TargetSlots == SlotFlags.NONE) if (args.TargetSlots == SlotFlags.NONE)
return; return;
var ev = new InventoryRelayedEvent<T>(args); var ev = new InventoryRelayedEvent<T>(args, inventory.Owner);
var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots); var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots);
while (enumerator.NextItem(out var item)) while (enumerator.NextItem(out var item))
{ {
@@ -141,7 +144,7 @@ public partial class InventorySystem
private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent<EquipmentVerb> args) private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent<EquipmentVerb> args)
{ {
// Automatically relay stripping related verbs to all equipped clothing. // Automatically relay stripping related verbs to all equipped clothing.
var ev = new InventoryRelayedEvent<GetVerbsEvent<EquipmentVerb>>(args); var ev = new InventoryRelayedEvent<GetVerbsEvent<EquipmentVerb>>(args, uid);
var enumerator = new InventorySlotEnumerator(component); var enumerator = new InventorySlotEnumerator(component);
while (enumerator.NextItem(out var item, out var slotDef)) 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<InnateVerb> args) private void OnGetInnateVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent<InnateVerb> args)
{ {
// Automatically relay stripping related verbs to all equipped clothing. // Automatically relay stripping related verbs to all equipped clothing.
var ev = new InventoryRelayedEvent<GetVerbsEvent<InnateVerb>>(args); var ev = new InventoryRelayedEvent<GetVerbsEvent<InnateVerb>>(args, uid);
var enumerator = new InventorySlotEnumerator(component, SlotFlags.WITHOUT_POCKET); var enumerator = new InventorySlotEnumerator(component, SlotFlags.WITHOUT_POCKET);
while (enumerator.NextItem(out var item)) while (enumerator.NextItem(out var item))
{ {
@@ -176,9 +179,12 @@ public sealed class InventoryRelayedEvent<TEvent> : EntityEventArgs
{ {
public TEvent Args; public TEvent Args;
public InventoryRelayedEvent(TEvent args) public EntityUid Owner;
public InventoryRelayedEvent(TEvent args, EntityUid owner)
{ {
Args = args; Args = args;
Owner = owner;
} }
} }

View File

@@ -21,7 +21,7 @@ public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem
{ {
// Explicitly check for standing state component, as entities without it will return false for IsDown() // 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) // which prevents inserting any kind of non-mobs into this container (which is unintended)
if (TryComp<StandingStateComponent>(ent, out var standingState) && !_standing.IsDown(ent, standingState)) if (TryComp<StandingStateComponent>(ent, out var standingState) && !_standing.IsDown((ent, standingState)))
args.Contents.Remove(ent); args.Contents.Remove(ent);
} }
} }

View File

@@ -1,5 +1,6 @@
using Content.Shared.Climbing.Events; using Content.Shared.Climbing.Events;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Inventory;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Content.Shared.Physics; using Content.Shared.Physics;
@@ -69,12 +70,17 @@ public sealed class StandingStateSystem : EntitySystem
ChangeLayers(entity); ChangeLayers(entity);
} }
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) public bool IsMatchingState(Entity<StandingStateComponent?> entity, bool standing)
{ {
if (!Resolve(uid, ref standingState, false)) return standing != IsDown(entity);
}
public bool IsDown(Entity<StandingStateComponent?> entity)
{
if (!Resolve(entity, ref entity.Comp, false))
return false; return false;
return !standingState.Standing; return !entity.Comp.Standing;
} }
public bool Down(EntityUid uid, public bool Down(EntityUid uid,
@@ -213,29 +219,27 @@ public record struct DropHandItemsEvent();
/// <summary> /// <summary>
/// Subscribe if you can potentially block a down attempt. /// Subscribe if you can potentially block a down attempt.
/// </summary> /// </summary>
public sealed class DownAttemptEvent : CancellableEntityEventArgs public sealed class DownAttemptEvent : CancellableEntityEventArgs;
{
}
/// <summary> /// <summary>
/// Subscribe if you can potentially block a stand attempt. /// Subscribe if you can potentially block a stand attempt.
/// </summary> /// </summary>
public sealed class StandAttemptEvent : CancellableEntityEventArgs public sealed class StandAttemptEvent : CancellableEntityEventArgs;
{
}
/// <summary> /// <summary>
/// Raised when an entity becomes standing /// Raised when an entity becomes standing
/// </summary> /// </summary>
public sealed class StoodEvent : EntityEventArgs public sealed class StoodEvent : EntityEventArgs, IInventoryRelayEvent
{ {
} public SlotFlags TargetSlots { get; } = SlotFlags.FEET;
};
/// <summary> /// <summary>
/// Raised when an entity is not standing /// Raised when an entity is not standing
/// </summary> /// </summary>
public sealed class DownedEvent : EntityEventArgs public sealed class DownedEvent : EntityEventArgs, IInventoryRelayEvent
{ {
public SlotFlags TargetSlots { get; } = SlotFlags.FEET;
} }
/// <summary> /// <summary>

View File

@@ -501,7 +501,7 @@ public abstract partial class SharedStunSystem
// Targeted moth attack // Targeted moth attack
CancelKnockdownDoAfter((entity, entity.Comp)); CancelKnockdownDoAfter((entity, entity.Comp));
RemComp<KnockedDownComponent>(entity); RemCompDeferred<KnockedDownComponent>(entity);
} }
private void OnKnockdownAttempt(Entity<GravityAffectedComponent> entity, ref KnockDownAttemptEvent args) private void OnKnockdownAttempt(Entity<GravityAffectedComponent> entity, ref KnockDownAttemptEvent args)

View File

@@ -135,6 +135,7 @@
- type: ClothingSpeedModifier - type: ClothingSpeedModifier
walkModifier: 1.5 walkModifier: 1.5
sprintModifier: 1.5 sprintModifier: 1.5
standing: true
- type: Appearance - type: Appearance
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals: