]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
EVENT BASED WEIGHTLESSNESS (#37971)
authorPrincess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com>
Tue, 19 Aug 2025 18:35:09 +0000 (11:35 -0700)
committerGitHub <noreply@github.com>
Tue, 19 Aug 2025 18:35:09 +0000 (14:35 -0400)
* Init Commit

* Typos

* Commit 2

* Save Interaction Test Mob from failing

* ssss

* Confident I've gotten all the correct prototypes

* Whoops forgot to edit those

* aaaaa

* Better solution

* Test fail fixes

* Yaml fix

* THE FINAL TEST FIX

* Final fix(?)

* whoops

* Added a WeightlessnessChangedEvent

* Check out this diff

* Wait I'm dumb

* Final optimization and don't duplicate code

* Death to IsWeightless

* File scoped namespaces

* REVIEW

* Fix test fails

* FIX TEST FAILS REAL

* A

* Commit of doom

* borgar

* We don't need to specify on map init apparently

* Fuck it

* LOAD BEARING COMMENT

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
31 files changed:
Content.IntegrationTests/Tests/Doors/AirlockTest.cs
Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs
Content.IntegrationTests/Tests/GravityGridTest.cs
Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
Content.Server/Fluids/EntitySystems/SpraySystem.cs
Content.Server/Gravity/GravitySystem.cs
Content.Shared/Clothing/EntitySystems/AntiGravityClothingSystem.cs
Content.Shared/Clothing/MagbootsComponent.cs
Content.Shared/Clothing/MagbootsSystem.cs
Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs
Content.Shared/Friction/TileFrictionController.cs
Content.Shared/Gravity/FloatingVisualsComponent.cs
Content.Shared/Gravity/GravityAffectedComponent.cs [new file with mode: 0644]
Content.Shared/Gravity/GravityComponent.cs
Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs
Content.Shared/Gravity/SharedGravitySystem.cs
Content.Shared/Movement/Components/MovementIgnoreGravityComponent.cs
Content.Shared/Movement/Systems/FrictionContactsSystem.cs
Content.Shared/Movement/Systems/MovementIgnoreGravitySystem.cs
Content.Shared/Movement/Systems/SharedMoverController.cs
Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs
Content.Shared/Physics/Controllers/SharedConveyorController.cs
Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs
Content.Shared/Throwing/ThrowingSystem.cs
Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs
Resources/Prototypes/Entities/Mobs/base.yml
Resources/Prototypes/Entities/Objects/Fun/immovable_rod.yml
Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml
Resources/Prototypes/Entities/Objects/base_item.yml
Resources/Prototypes/Entities/Structures/base_structure.yml

index e47c73611a4145adbc32ca94da8a7d498d54a2cb..69fe66039b8cd0caf422b811d0c9572ba3b2eae8 100644 (file)
@@ -21,6 +21,7 @@ namespace Content.IntegrationTests.Tests.Doors
   components:
   - type: Physics
     bodyType: Dynamic
+  - type: GravityAffected
   - type: Fixtures
     fixtures:
       fix1:
index 6aa2763888d4dab40e65b2cd135a4ad137c38370..0951e7e260876c0cf763cf3c1244f1b66cf4e16f 100644 (file)
@@ -19,6 +19,7 @@ namespace Content.IntegrationTests.Tests.Gravity
   - type: Alerts
   - type: Physics
     bodyType: Dynamic
+  - type: GravityAffected
 
 - type: entity
   name: WeightlessGravityGeneratorDummy
index b32d6c2b8d8c28112a65a36bf585896986b4b15c..8257035de66c00480835ab4c34d6934a66f4a42e 100644 (file)
@@ -76,8 +76,8 @@ namespace Content.IntegrationTests.Tests
                 Assert.Multiple(() =>
                 {
                     Assert.That(generatorComponent.GravityActive, Is.True);
-                    Assert.That(!entityMan.GetComponent<GravityComponent>(grid1).EnabledVV);
-                    Assert.That(entityMan.GetComponent<GravityComponent>(grid2).EnabledVV);
+                    Assert.That(!entityMan.GetComponent<GravityComponent>(grid1).Enabled);
+                    Assert.That(entityMan.GetComponent<GravityComponent>(grid2).Enabled);
                 });
 
                 // Re-enable needs power so it turns off again.
@@ -94,7 +94,7 @@ namespace Content.IntegrationTests.Tests
                 Assert.Multiple(() =>
                 {
                     Assert.That(generatorComponent.GravityActive, Is.False);
-                    Assert.That(entityMan.GetComponent<GravityComponent>(grid2).EnabledVV, Is.False);
+                    Assert.That(entityMan.GetComponent<GravityComponent>(grid2).Enabled, Is.False);
                 });
             });
 
index 79756ea5b419445f128f2042c519f87d45ff82ed..0ed42d34764fe9c52a0a46c75ea295b8b32eb227 100644 (file)
@@ -144,6 +144,7 @@ public abstract partial class InteractionTest
   - type: Stripping
   - type: Puller
   - type: Physics
+  - type: GravityAffected
   - type: Tag
     tags:
     - CanPilot
index 1bc4b659998da49dcd17e4bdf1e796b20385e8b3..3703c8c1ac35ceeafeed22ea6ff38c4f6b1aea27 100644 (file)
@@ -29,6 +29,7 @@ using Content.Shared.Damage;
 using Content.Shared.Damage.Systems;
 using Content.Shared.Database;
 using Content.Shared.Electrocution;
+using Content.Shared.Gravity;
 using Content.Shared.Interaction.Components;
 using Content.Shared.Inventory;
 using Content.Shared.Mobs;
@@ -675,6 +676,11 @@ public sealed partial class AdminVerbSystem
                 grav.Weightless = true;
 
                 Dirty(args.Target, grav);
+
+                EnsureComp<GravityAffectedComponent>(args.Target, out var weightless);
+                weightless.Weightless = true;
+
+                Dirty(args.Target, weightless);
             },
             Impact = LogImpact.Extreme,
             Message = string.Join(": ", noGravityName, Loc.GetString("admin-smite-remove-gravity-description"))
index d8da0cde3de96bc133701266cf14c081340758b4..2a6b0644be32ee680e9da43c877d11d707ccbf9b 100644 (file)
@@ -166,7 +166,7 @@ public sealed class SpraySystem : EntitySystem
 
             if (TryComp<PhysicsComponent>(user, out var body))
             {
-                if (_gravity.IsWeightless(user, body))
+                if (_gravity.IsWeightless(user))
                 {
                     // push back the player
                     _physics.ApplyLinearImpulse(user, -impulseDirection * entity.Comp.PushbackAmount, body: body);
index 6807b9df4a4e7ea5fe2e447f4af525cf05dbb6d9..7958071a3126840b6d460d0882825cdbc086a622 100644 (file)
@@ -18,7 +18,7 @@ namespace Content.Server.Gravity
         /// </summary>
         public void RefreshGravity(EntityUid uid, GravityComponent? gravity = null)
         {
-            if (!Resolve(uid, ref gravity))
+            if (!GravityQuery.Resolve(uid, ref gravity))
                 return;
 
             if (gravity.Inherent)
@@ -61,7 +61,7 @@ namespace Content.Server.Gravity
         /// </summary>
         public void EnableGravity(EntityUid uid, GravityComponent? gravity = null)
         {
-            if (!Resolve(uid, ref gravity))
+            if (!GravityQuery.Resolve(uid, ref gravity))
                 return;
 
             if (gravity.Enabled || gravity.Inherent)
index c5b2ee3dfc8b3630d5ea3a2a4301624944d84108..636a21533eb028e87b1f69154ff744971f6267a8 100644 (file)
@@ -6,10 +6,13 @@ namespace Content.Shared.Clothing.EntitySystems;
 
 public sealed class AntiGravityClothingSystem : EntitySystem
 {
+    [Dependency] SharedGravitySystem _gravity = default!;
     /// <inheritdoc/>
     public override void Initialize()
     {
         SubscribeLocalEvent<AntiGravityClothingComponent, InventoryRelayedEvent<IsWeightlessEvent>>(OnIsWeightless);
+        SubscribeLocalEvent<AntiGravityClothingComponent, ClothingGotEquippedEvent>(OnEquipped);
+        SubscribeLocalEvent<AntiGravityClothingComponent, ClothingGotUnequippedEvent>(OnUnequipped);
     }
 
     private void OnIsWeightless(Entity<AntiGravityClothingComponent> ent, ref InventoryRelayedEvent<IsWeightlessEvent> args)
@@ -20,4 +23,14 @@ public sealed class AntiGravityClothingSystem : EntitySystem
         args.Args.Handled = true;
         args.Args.IsWeightless = true;
     }
+
+    private void OnEquipped(Entity<AntiGravityClothingComponent> entity, ref ClothingGotEquippedEvent args)
+    {
+        _gravity.RefreshWeightless(args.Wearer, true);
+    }
+
+    private void OnUnequipped(Entity<AntiGravityClothingComponent> entity, ref ClothingGotUnequippedEvent args)
+    {
+        _gravity.RefreshWeightless(args.Wearer, false);
+    }
 }
index 4bef74fd33506da8c06f047abfaed925e1f76eca..c0a8b40866a333f32148537e5780c3c4979c490f 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Alert;
+using Content.Shared.Inventory;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
 
@@ -16,10 +17,4 @@ public sealed partial class MagbootsComponent : Component
     /// </summary>
     [DataField]
     public bool RequiresGrid = true;
-
-    /// <summary>
-    /// Slot the clothing has to be worn in to work.
-    /// </summary>
-    [DataField]
-    public string Slot = "shoes";
 }
index fd5a2cc336a5cd7582d3286f7b66766852db31c9..58bc2f0796c9486aebdc98e6bcbe7b33b91212fe 100644 (file)
@@ -32,14 +32,8 @@ public sealed class SharedMagbootsSystem : EntitySystem
 
     private void OnToggled(Entity<MagbootsComponent> ent, ref ItemToggledEvent args)
     {
-        var (uid, comp) = ent;
-        // only stick to the floor if being worn in the correct slot
-        if (_container.TryGetContainingContainer((uid, null, null), out var container) &&
-            _inventory.TryGetSlotEntity(container.Owner, comp.Slot, out var worn)
-            && uid == worn)
-        {
+        if (_container.TryGetContainingContainer((ent.Owner, null, null), out var container))
             UpdateMagbootEffects(container.Owner, ent, args.Activated);
-        }
     }
 
     private void OnGotUnequipped(Entity<MagbootsComponent> ent, ref ClothingGotUnequippedEvent args)
@@ -58,6 +52,8 @@ public sealed class SharedMagbootsSystem : EntitySystem
         if (TryComp<MovedByPressureComponent>(user, out var moved))
             moved.Enabled = !state;
 
+        _gravity.RefreshWeightless(user, !state);
+
         if (state)
             _alerts.ShowAlert(user, ent.Comp.MagbootsAlert);
         else
index 31ff0348091ff2d4d7e519f3a452f1a452a85a1a..c70c7ab61e0ca30de29585087d1397c385692b8e 100644 (file)
@@ -164,12 +164,11 @@ public abstract partial class SharedDoAfterSystem : EntitySystem
         if (args.Target is { } target && !xformQuery.TryGetComponent(target, out targetXform))
             return true;
 
-        TransformComponent? usedXform = null;
-        if (args.Used is { } @using && !xformQuery.TryGetComponent(@using, out usedXform))
+        if (args.Used is { } @using && !xformQuery.HasComp(@using))
             return true;
 
         // TODO: Re-use existing xform query for these calculations.
-        if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User, xform: userXform)))
+        if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User)))
         {
             // Whether the user has moved too much from their original position.
             if (!_transform.InRange(userXform.Coordinates, doAfter.UserPosition, args.MovementThreshold))
index 36e971862a54d5c6cecc55be43cd1309a4b04c68..4b29b9a9de02ec7a4d1281562fce37aecca331dc 100644 (file)
@@ -73,7 +73,7 @@ namespace Content.Shared.Friction
 
                 // If we're not touching the ground, don't use tileFriction.
                 // TODO: Make IsWeightless event-based; we already have grid traversals tracked so just raise events
-                if (body.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid, body, xform) || !xform.Coordinates.IsValid(EntityManager))
+                if (body.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid) || !xform.Coordinates.IsValid(EntityManager))
                     friction = xform.GridUid == null || !_gridQuery.HasComp(xform.GridUid) ? _offGridDamping : _airDamping;
                 else
                     friction = _frictionModifier * GetTileFriction(uid, body, xform);
index 67650baecdaa8a980bfefe6c482e8a1823f7dddb..c0064ebf7825c0b4ac0688489206b4b25dcd8d89 100644 (file)
@@ -10,20 +10,17 @@ public sealed partial class FloatingVisualsComponent : Component
     /// <summary>
     /// How long it takes to go from the bottom of the animation to the top.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
     [DataField, AutoNetworkedField]
     public float AnimationTime = 2f;
 
     /// <summary>
     /// How far it goes in any direction.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite)]
     [DataField, AutoNetworkedField]
     public Vector2 Offset = new(0, 0.2f);
 
-    [ViewVariables(VVAccess.ReadWrite)]
-    [AutoNetworkedField]
-    public bool CanFloat = false;
+    [DataField, AutoNetworkedField]
+    public bool CanFloat;
 
     public readonly string AnimationKey = "gravity";
 }
diff --git a/Content.Shared/Gravity/GravityAffectedComponent.cs b/Content.Shared/Gravity/GravityAffectedComponent.cs
new file mode 100644 (file)
index 0000000..2da7458
--- /dev/null
@@ -0,0 +1,17 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Gravity;
+
+/// <summary>
+/// This Component allows a target to be considered "weightless" when Weightless is true. Without this component, the
+/// target will never be weightless.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class GravityAffectedComponent : Component
+{
+    /// <summary>
+    /// If true, this entity will be considered "weightless"
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Weightless = true;
+}
index bbe3de69ea0be3170d4d12a2f866d2fa6d835cfe..ede8f74c7a0c880665cfd9993a69cc1b96e32552 100644 (file)
@@ -5,34 +5,20 @@ using Robust.Shared.Serialization;
 namespace Content.Shared.Gravity
 {
     [RegisterComponent]
+    [AutoGenerateComponentState]
     [NetworkedComponent]
     public sealed partial class GravityComponent : Component
     {
-        [DataField("gravityShakeSound")]
+        [DataField, AutoNetworkedField]
         public SoundSpecifier GravityShakeSound { get; set; } = new SoundPathSpecifier("/Audio/Effects/alert.ogg");
 
-        [ViewVariables(VVAccess.ReadWrite)]
-        public bool EnabledVV
-        {
-            get => Enabled;
-            set
-            {
-                if (Enabled == value) return;
-                Enabled = value;
-                var ev = new GravityChangedEvent(Owner, value);
-                IoCManager.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(Owner, ref ev);
-                Dirty();
-            }
-        }
-
-        [DataField("enabled")]
+        [DataField, AutoNetworkedField]
         public bool Enabled;
 
         /// <summary>
         /// Inherent gravity ensures GravitySystem won't change Enabled according to the gravity generators attached to this entity.
         /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("inherent")]
+        [DataField, AutoNetworkedField]
         public bool Inherent;
     }
 }
index 6ca974f2ed34c718e95909f02ca3fa7a530ceaeb..ae5c73b49875d7e6a8e8c63bd61dd9aeccfebd02 100644 (file)
@@ -8,15 +8,14 @@ namespace Content.Shared.Gravity;
 /// </summary>
 public abstract class SharedFloatingVisualizerSystem : EntitySystem
 {
-    [Dependency] private readonly SharedGravitySystem GravitySystem = default!;
+    [Dependency] private readonly SharedGravitySystem _gravity = default!;
 
     public override void Initialize()
     {
         base.Initialize();
 
         SubscribeLocalEvent<FloatingVisualsComponent, ComponentStartup>(OnComponentStartup);
-        SubscribeLocalEvent<GravityChangedEvent>(OnGravityChanged);
-        SubscribeLocalEvent<FloatingVisualsComponent, EntParentChangedMessage>(OnEntParentChanged);
+        SubscribeLocalEvent<FloatingVisualsComponent, WeightlessnessChangedEvent>(OnWeightlessnessChanged);
     }
 
     /// <summary>
@@ -24,48 +23,28 @@ public abstract class SharedFloatingVisualizerSystem : EntitySystem
     /// </summary>
     public virtual void FloatAnimation(EntityUid uid, Vector2 offset, string animationKey, float animationTime, bool stop = false) { }
 
-    protected bool CanFloat(EntityUid uid, FloatingVisualsComponent component, TransformComponent? transform = null)
+    protected bool CanFloat(Entity<FloatingVisualsComponent> entity)
     {
-        if (!Resolve(uid, ref transform))
-            return false;
-
-        if (transform.MapID == MapId.Nullspace)
-            return false;
-
-        component.CanFloat = GravitySystem.IsWeightless(uid, xform: transform);
-        Dirty(uid, component);
-        return component.CanFloat;
+        entity.Comp.CanFloat = _gravity.IsWeightless(entity.Owner);
+        Dirty(entity);
+        return entity.Comp.CanFloat;
     }
 
-    private void OnComponentStartup(EntityUid uid, FloatingVisualsComponent component, ComponentStartup args)
+    private void OnComponentStartup(Entity<FloatingVisualsComponent> entity, ref ComponentStartup args)
     {
-        if (CanFloat(uid, component))
-            FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
+        if (CanFloat(entity))
+            FloatAnimation(entity, entity.Comp.Offset, entity.Comp.AnimationKey, entity.Comp.AnimationTime);
     }
 
-    private void OnGravityChanged(ref GravityChangedEvent args)
+    private void OnWeightlessnessChanged(Entity<FloatingVisualsComponent> entity, ref WeightlessnessChangedEvent args)
     {
-        var query = EntityQueryEnumerator<FloatingVisualsComponent, TransformComponent>();
-        while (query.MoveNext(out var uid, out var floating, out var transform))
-        {
-            if (transform.MapID == MapId.Nullspace)
-                continue;
-
-            if (transform.GridUid != args.ChangedGridIndex)
-                continue;
+        if (entity.Comp.CanFloat == args.Weightless)
+            return;
 
-            floating.CanFloat = !args.HasGravity;
-            Dirty(uid, floating);
+        entity.Comp.CanFloat = CanFloat(entity);
+        Dirty(entity);
 
-            if (!args.HasGravity)
-                FloatAnimation(uid, floating.Offset, floating.AnimationKey, floating.AnimationTime);
-        }
-    }
-
-    private void OnEntParentChanged(EntityUid uid, FloatingVisualsComponent component, ref EntParentChangedMessage args)
-    {
-        var transform = args.Transform;
-        if (CanFloat(uid, component, transform))
-            FloatAnimation(uid, component.Offset, component.AnimationKey, component.AnimationTime);
+        if (args.Weightless)
+            FloatAnimation(entity, entity.Comp.Offset, entity.Comp.AnimationKey, entity.Comp.AnimationTime);
     }
 }
index e20771d6038125b6ab2ea0eba2cdb0c1a101fe09..b54f9b21c8ea4b3027657c2be3582b824cca9087 100644 (file)
 using Content.Shared.Alert;
 using Content.Shared.Inventory;
-using Content.Shared.Movement.Components;
-using Robust.Shared.GameStates;
+using Content.Shared.Throwing;
+using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.Map;
 using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
+using Robust.Shared.Physics.Events;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
 using Robust.Shared.Timing;
 
-namespace Content.Shared.Gravity
+namespace Content.Shared.Gravity;
+
+public abstract partial class SharedGravitySystem : EntitySystem
 {
-    public abstract partial class SharedGravitySystem : EntitySystem
+    [Dependency] protected readonly IGameTiming Timing = default!;
+    [Dependency] private readonly AlertsSystem _alerts = default!;
+
+    public static readonly ProtoId<AlertPrototype> WeightlessAlert = "Weightless";
+
+    protected EntityQuery<GravityComponent> GravityQuery;
+    private EntityQuery<GravityAffectedComponent> _weightlessQuery;
+    private EntityQuery<PhysicsComponent> _physicsQuery;
+
+    public override void Initialize()
     {
-        [Dependency] protected readonly IGameTiming Timing = default!;
-        [Dependency] private readonly AlertsSystem _alerts = default!;
+        base.Initialize();
+        // Grid Gravity
+        SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
+        SubscribeLocalEvent<GravityChangedEvent>(OnGravityChange);
+
+        // Weightlessness
+        SubscribeLocalEvent<GravityAffectedComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<GravityAffectedComponent, EntParentChangedMessage>(OnEntParentChanged);
+        SubscribeLocalEvent<GravityAffectedComponent, PhysicsBodyTypeChangedEvent>(OnBodyTypeChanged);
+
+        // Alerts
+        SubscribeLocalEvent<AlertSyncEvent>(OnAlertsSync);
+        SubscribeLocalEvent<AlertsComponent, WeightlessnessChangedEvent>(OnWeightlessnessChanged);
+        SubscribeLocalEvent<AlertsComponent, EntParentChangedMessage>(OnAlertsParentChange);
+
+        // Impulse
+        SubscribeLocalEvent<GravityAffectedComponent, ShooterImpulseEvent>(OnShooterImpulse);
+        SubscribeLocalEvent<GravityAffectedComponent, ThrowerImpulseEvent>(OnThrowerImpulse);
+
+        GravityQuery = GetEntityQuery<GravityComponent>();
+        _weightlessQuery = GetEntityQuery<GravityAffectedComponent>();
+        _physicsQuery = GetEntityQuery<PhysicsComponent>();
+    }
 
-        public static readonly ProtoId<AlertPrototype> WeightlessAlert = "Weightless";
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+        UpdateShake();
+    }
 
-        private EntityQuery<GravityComponent> _gravityQuery;
+    public bool IsWeightless(Entity<GravityAffectedComponent?> entity)
+    {
+        // If we can be weightless and are weightless, return true, otherwise return false
+        return _weightlessQuery.Resolve(entity, ref entity.Comp, false) && entity.Comp.Weightless;
+    }
 
-        public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null)
-        {
-            Resolve(uid, ref body, false);
+    private bool GetWeightless(Entity<GravityAffectedComponent, PhysicsComponent?> entity)
+    {
+        if (!_physicsQuery.Resolve(entity, ref entity.Comp2, false))
+            return false;
 
-            if ((body?.BodyType & (BodyType.Static | BodyType.Kinematic)) != 0)
-                return false;
+        if (entity.Comp2.BodyType is BodyType.Static or BodyType.Kinematic)
+            return false;
 
-            if (TryComp<MovementIgnoreGravityComponent>(uid, out var ignoreGravityComponent))
-                return ignoreGravityComponent.Weightless;
+        // Check if something other than the grid or map is overriding our gravity
+        var ev = new IsWeightlessEvent();
+        RaiseLocalEvent(entity, ref ev);
+        if (ev.Handled)
+            return ev.IsWeightless;
 
-            var ev = new IsWeightlessEvent(uid);
-            RaiseLocalEvent(uid, ref ev);
-            if (ev.Handled)
-                return ev.IsWeightless;
+        return !EntityGridOrMapHaveGravity(entity.Owner);
+    }
 
-            if (!Resolve(uid, ref xform))
-                return true;
+    /// <summary>
+    /// Refreshes weightlessness status, needs to be called anytime it would change.
+    /// </summary>
+    /// <param name="entity">The entity we are updating the weightless status of</param>
+    public void RefreshWeightless(Entity<GravityAffectedComponent?> entity)
+    {
+        if (!_weightlessQuery.Resolve(entity, ref entity.Comp))
+            return;
 
-            // If grid / map has gravity
-            if (EntityGridOrMapHaveGravity((uid, xform)))
-                return false;
+        UpdateWeightless(entity!);
+    }
 
-            return true;
-        }
+    /// <summary>
+    /// Overload of <see cref="RefreshWeightless(Entity{GravityAffectedComponent?})"/> which also takes a bool for the weightlessness value we want to change to.
+    /// This method is LOAD BEARING for UninitializedSaveTest. DO NOT REMOVE IT.
+    /// </summary>
+    /// <param name="entity">The entity we are updating the weightless status of</param>
+    /// <param name="weightless">The weightless value we are trying to change to, helps avoid needless networking</param>
+    public void RefreshWeightless(Entity<GravityAffectedComponent?> entity, bool weightless)
+    {
+        if (!_weightlessQuery.Resolve(entity, ref entity.Comp))
+            return;
 
-        /// <summary>
-        /// Checks if a given entity is currently standing on a grid or map that supports having gravity at all.
-        /// </summary>
-        public bool EntityOnGravitySupportingGridOrMap(Entity<TransformComponent?> entity)
-        {
-            entity.Comp ??= Transform(entity);
+        // Only update if we're changing our weightless status
+        if (entity.Comp.Weightless == weightless)
+            return;
 
-            return _gravityQuery.HasComp(entity.Comp.GridUid) ||
-                   _gravityQuery.HasComp(entity.Comp.MapUid);
-        }
+        UpdateWeightless(entity!);
+    }
 
+    private void UpdateWeightless(Entity<GravityAffectedComponent> entity)
+    {
+        var newWeightless = GetWeightless(entity);
 
-        /// <summary>
-        /// Checks if a given entity is currently standing on a grid or map that has gravity of some kind.
-        /// </summary>
-        public bool EntityGridOrMapHaveGravity(Entity<TransformComponent?> entity)
-        {
-            entity.Comp ??= Transform(entity);
+        // Don't network or raise events if it's not changing
+        if (newWeightless == entity.Comp.Weightless)
+            return;
 
-            return _gravityQuery.TryComp(entity.Comp.GridUid, out var gravity) && gravity.Enabled ||
-                   _gravityQuery.TryComp(entity.Comp.MapUid, out var mapGravity) && mapGravity.Enabled;
-        }
+        entity.Comp.Weightless = newWeightless;
+        Dirty(entity);
 
-        public override void Initialize()
-        {
-            base.Initialize();
-            SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
-            SubscribeLocalEvent<AlertSyncEvent>(OnAlertsSync);
-            SubscribeLocalEvent<AlertsComponent, EntParentChangedMessage>(OnAlertsParentChange);
-            SubscribeLocalEvent<GravityChangedEvent>(OnGravityChange);
-            SubscribeLocalEvent<GravityComponent, ComponentGetState>(OnGetState);
-            SubscribeLocalEvent<GravityComponent, ComponentHandleState>(OnHandleState);
-
-            _gravityQuery = GetEntityQuery<GravityComponent>();
-        }
+        var ev = new WeightlessnessChangedEvent(entity.Comp.Weightless);
+        RaiseLocalEvent(entity, ref ev);
+    }
 
-        public override void Update(float frameTime)
-        {
-            base.Update(frameTime);
-            UpdateShake();
-        }
+    private void OnMapInit(Entity<GravityAffectedComponent> entity, ref MapInitEvent args)
+    {
+        RefreshWeightless((entity.Owner, entity.Comp));
+    }
 
-        private void OnHandleState(EntityUid uid, GravityComponent component, ref ComponentHandleState args)
-        {
-            if (args.Current is not GravityComponentState state)
-                return;
-
-            if (component.EnabledVV == state.Enabled)
-                return;
-            component.EnabledVV = state.Enabled;
-            var ev = new GravityChangedEvent(uid, component.EnabledVV);
-            RaiseLocalEvent(uid, ref ev, true);
-        }
+    private void OnWeightlessnessChanged(Entity<AlertsComponent> entity, ref WeightlessnessChangedEvent args)
+    {
+        if (args.Weightless)
+            _alerts.ShowAlert(entity, WeightlessAlert);
+        else
+            _alerts.ClearAlert(entity, WeightlessAlert);
+    }
 
-        private void OnGetState(EntityUid uid, GravityComponent component, ref ComponentGetState args)
-        {
-            args.State = new GravityComponentState(component.EnabledVV);
-        }
+    private void OnEntParentChanged(Entity<GravityAffectedComponent> entity, ref EntParentChangedMessage args)
+    {
+        // If we've moved but are still on the same grid, then don't do anything.
+        if (args.OldParent == args.Transform.GridUid)
+            return;
 
-        private void OnGravityChange(ref GravityChangedEvent ev)
-        {
-            var alerts = AllEntityQuery<AlertsComponent, TransformComponent>();
-            while(alerts.MoveNext(out var uid, out _, out var xform))
-            {
-                if (xform.GridUid != ev.ChangedGridIndex)
-                    continue;
-
-                if (!ev.HasGravity)
-                {
-                    _alerts.ShowAlert(uid, WeightlessAlert);
-                }
-                else
-                {
-                    _alerts.ClearAlert(uid, WeightlessAlert);
-                }
-            }
-        }
+        RefreshWeightless((entity.Owner, entity.Comp), !EntityGridOrMapHaveGravity((entity, args.Transform)));
+    }
 
-        private void OnAlertsSync(AlertSyncEvent ev)
-        {
-            if (IsWeightless(ev.Euid))
-            {
-                _alerts.ShowAlert(ev.Euid, WeightlessAlert);
-            }
-            else
-            {
-                _alerts.ClearAlert(ev.Euid, WeightlessAlert);
-            }
-        }
+    private void OnBodyTypeChanged(Entity<GravityAffectedComponent> entity, ref PhysicsBodyTypeChangedEvent args)
+    {
+        // No need to update weightlessness if we're not weightless and we're a body type that can't be weightless
+        if (args.New is BodyType.Static or BodyType.Kinematic && entity.Comp.Weightless == false)
+            return;
 
-        private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref EntParentChangedMessage args)
-        {
-            if (IsWeightless(uid))
-            {
-                _alerts.ShowAlert(uid, WeightlessAlert);
-            }
-            else
-            {
-                _alerts.ClearAlert(uid, WeightlessAlert);
-            }
-        }
+        RefreshWeightless((entity.Owner, entity.Comp));
+    }
+
+    /// <summary>
+    /// Checks if a given entity is currently standing on a grid or map that supports having gravity at all.
+    /// </summary>
+    public bool EntityOnGravitySupportingGridOrMap(Entity<TransformComponent?> entity)
+    {
+        entity.Comp ??= Transform(entity);
+
+        return GravityQuery.HasComp(entity.Comp.GridUid) ||
+               GravityQuery.HasComp(entity.Comp.MapUid);
+    }
+
+    /// <summary>
+    /// Checks if a given entity is currently standing on a grid or map that has gravity of some kind.
+    /// </summary>
+    public bool EntityGridOrMapHaveGravity(Entity<TransformComponent?> entity)
+    {
+        entity.Comp ??= Transform(entity);
+
+        // DO NOT SET TO WEIGHTLESS IF THEY'RE IN NULL-SPACE
+        // TODO: If entities actually properly pause when leaving PVS rather than entering null-space this can probably go.
+        if (entity.Comp.MapID == MapId.Nullspace)
+            return true;
 
-        private void OnGridInit(GridInitializeEvent ev)
+        return GravityQuery.TryComp(entity.Comp.GridUid, out var gravity) && gravity.Enabled ||
+               GravityQuery.TryComp(entity.Comp.MapUid, out var mapGravity) && mapGravity.Enabled;
+    }
+
+    private void OnGravityChange(ref GravityChangedEvent args)
+    {
+        var gravity = AllEntityQuery<GravityAffectedComponent, TransformComponent>();
+        while(gravity.MoveNext(out var uid, out var weightless, out var xform))
         {
-            EnsureComp<GravityComponent>(ev.EntityUid);
+            if (xform.GridUid != args.ChangedGridIndex)
+                continue;
+
+            RefreshWeightless((uid, weightless), !args.HasGravity);
         }
+    }
 
-        [Serializable, NetSerializable]
-        private sealed class GravityComponentState : ComponentState
-        {
-            public bool Enabled { get; }
+    private void OnAlertsSync(AlertSyncEvent ev)
+    {
+        if (IsWeightless(ev.Euid))
+            _alerts.ShowAlert(ev.Euid, WeightlessAlert);
+        else
+            _alerts.ClearAlert(ev.Euid, WeightlessAlert);
+    }
+
+    private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref EntParentChangedMessage args)
+    {
+        if (IsWeightless(uid))
+            _alerts.ShowAlert(uid, WeightlessAlert);
+        else
+            _alerts.ClearAlert(uid, WeightlessAlert);
+    }
 
-            public GravityComponentState(bool enabled)
-            {
-                Enabled = enabled;
-            }
+    private void OnGridInit(GridInitializeEvent ev)
+    {
+        EnsureComp<GravityComponent>(ev.EntityUid);
+    }
+
+    [Serializable, NetSerializable]
+    private sealed class GravityComponentState : ComponentState
+    {
+        public bool Enabled { get; }
+
+        public GravityComponentState(bool enabled)
+        {
+            Enabled = enabled;
         }
     }
 
-    [ByRefEvent]
-    public record struct IsWeightlessEvent(EntityUid Entity, bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent
+    private void OnThrowerImpulse(Entity<GravityAffectedComponent> entity, ref ThrowerImpulseEvent args)
+    {
+        args.Push = true;
+    }
+
+    private void OnShooterImpulse(Entity<GravityAffectedComponent> entity, ref ShooterImpulseEvent args)
     {
-        SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET;
+        args.Push = true;
     }
 }
+
+/// <summary>
+/// Raised to determine if an entity's weightlessness is being overwritten by a component or item with a component.
+/// </summary>
+/// <param name="IsWeightless">Whether we should be weightless</param>
+/// <param name="Handled">Whether something is trying to override our weightlessness</param>
+[ByRefEvent]
+public record struct IsWeightlessEvent(bool IsWeightless = false, bool Handled = false) : IInventoryRelayEvent
+{
+    SlotFlags IInventoryRelayEvent.TargetSlots => ~SlotFlags.POCKET;
+}
+
+/// <summary>
+/// Raised on an entity when their weightless status changes.
+/// </summary>
+[ByRefEvent]
+public readonly record struct WeightlessnessChangedEvent(bool Weightless);
index 77c468e871f22bf0deca06303e3cf144a2330c7f..1723600ad13e76a9191d6bb4af7886a63c33d783 100644 (file)
@@ -1,34 +1,17 @@
-using Content.Shared.Clothing;
-using Content.Shared.Gravity;
-using Content.Shared.Inventory;
 using Robust.Shared.GameStates;
-using Robust.Shared.Map;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Serialization;
 
 namespace Content.Shared.Movement.Components
 {
     /// <summary>
     /// Ignores gravity entirely.
     /// </summary>
-    [RegisterComponent, NetworkedComponent]
+    [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
     public sealed partial class MovementIgnoreGravityComponent : Component
     {
         /// <summary>
-        /// Whether or not gravity is on or off for this object.
+        /// Whether gravity is on or off for this object. This will always override the current Gravity State.
         /// </summary>
-        [DataField("gravityState")] public bool Weightless = false;
-    }
-
-    [NetSerializable, Serializable]
-    public sealed class MovementIgnoreGravityComponentState : ComponentState
-    {
+        [DataField, AutoNetworkedField]
         public bool Weightless;
-
-        public MovementIgnoreGravityComponentState(MovementIgnoreGravityComponent component)
-        {
-            Weightless = component.Weightless;
-        }
     }
 }
index 44fc3933e306f4154e627677f0acd08572d4fb0d..3ffdd6ec26f7c85e2b8d3bc8a1caad3a926cfe90 100644 (file)
@@ -84,7 +84,7 @@ public sealed class FrictionContactsSystem : EntitySystem
         var frictionNoInput = 0.0f;
         var acceleration = 0.0f;
 
-        var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(entity, physicsComponent);
+        var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(entity.Owner);
 
         var remove = true;
         var entries = 0;
index 52cdf219e5329e5b0e3dcc156a234555b2f872de..93f6650fa986a3cb73c1c15ea524cde83ced1bcc 100644 (file)
@@ -1,33 +1,35 @@
-using Content.Shared.Movement.Components;
+using Content.Shared.Gravity;
+using Content.Shared.Movement.Components;
 using Content.Shared.Movement.Events;
-using Robust.Shared.GameStates;
 
 namespace Content.Shared.Movement.Systems;
 
 public sealed class MovementIgnoreGravitySystem : EntitySystem
 {
+    [Dependency] SharedGravitySystem _gravity = default!;
     public override void Initialize()
     {
-        SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentGetState>(GetState);
-        SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentHandleState>(HandleState);
         SubscribeLocalEvent<MovementAlwaysTouchingComponent, CanWeightlessMoveEvent>(OnWeightless);
+        SubscribeLocalEvent<MovementIgnoreGravityComponent, IsWeightlessEvent>(OnIsWeightless);
+        SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentStartup>(OnComponentStartup);
     }
 
-    private void OnWeightless(EntityUid uid, MovementAlwaysTouchingComponent component, ref CanWeightlessMoveEvent args)
+    private void OnWeightless(Entity<MovementAlwaysTouchingComponent> entity, ref CanWeightlessMoveEvent args)
     {
         args.CanMove = true;
     }
 
-    private void HandleState(EntityUid uid, MovementIgnoreGravityComponent component, ref ComponentHandleState args)
+    private void OnIsWeightless(Entity<MovementIgnoreGravityComponent> entity, ref IsWeightlessEvent args)
     {
-        if (args.Next is null)
-            return;
+        // We don't check if the event has been handled as this component takes precedent over other things.
 
-        component.Weightless = ((MovementIgnoreGravityComponentState) args.Next).Weightless;
+        args.IsWeightless = entity.Comp.Weightless;
+        args.Handled = true;
     }
 
-    private void GetState(EntityUid uid, MovementIgnoreGravityComponent component, ref ComponentGetState args)
+    private void OnComponentStartup(Entity<MovementIgnoreGravityComponent> entity, ref ComponentStartup args)
     {
-        args.State = new MovementIgnoreGravityComponentState(component);
+        EnsureComp<GravityAffectedComponent>(entity);
+        _gravity.RefreshWeightless(entity.Owner, entity.Comp.Weightless);
     }
 }
index f8495fcd18aba89c2987ca1129430c4b2ed1d404..b3c84aed4d3718083182a8313f29e3dd9b04eade 100644 (file)
@@ -195,8 +195,7 @@ public abstract partial class SharedMoverController : VirtualController
         }
 
         // If the body is in air but isn't weightless then it can't move
-        // TODO: MAKE ISWEIGHTLESS EVENT BASED
-        var weightless = _gravity.IsWeightless(uid, physicsComponent, xform);
+        var weightless = _gravity.IsWeightless(uid);
         var inAirHelpless = false;
 
         if (physicsComponent.BodyStatus != BodyStatus.OnGround && !CanMoveInAirQuery.HasComponent(uid))
@@ -624,8 +623,7 @@ public abstract partial class SharedMoverController : VirtualController
         if (!TryComp<PhysicsComponent>(ent, out var physicsComponent) || !XformQuery.TryComp(ent, out var xform))
             return;
 
-        // TODO: Make IsWeightless event based!!!
-        if (physicsComponent.BodyStatus != BodyStatus.OnGround || _gravity.IsWeightless(ent, physicsComponent, xform))
+        if (physicsComponent.BodyStatus != BodyStatus.OnGround || _gravity.IsWeightless(ent.Owner))
             args.Modifier *= ent.Comp.BaseWeightlessFriction;
         else
             args.Modifier *= ent.Comp.BaseFriction;
index 8a0b085a83201066f59f5829498e76f1ce0c892b..2dda24416318d1360f4cff06c663256d01a4250a 100644 (file)
@@ -85,7 +85,7 @@ public sealed class SpeedModifierContactsSystem : EntitySystem
         var sprintSpeed = 0.0f;
 
         // Cache the result of the airborne check, as it's expensive and independent of contacting entities, hence need only be done once.
-        var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid, physicsComponent);
+        var isAirborne = physicsComponent.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(uid);
 
         bool remove = true;
         var entries = 0;
index c2c88b2742dcdbe6d432f8f76dda747bb05f718d..b1ccb3be2a2e25c71307daadb3b145ddf5f0ee17 100644 (file)
@@ -216,7 +216,7 @@ public abstract class SharedConveyorController : VirtualController
             return true;
 
         if (physics.BodyStatus == BodyStatus.InAir ||
-            _gravity.IsWeightless(entity, physics, xform))
+            _gravity.IsWeightless(entity.Owner))
         {
             return true;
         }
index 14703f3177ea859f60e14067c0a0283fe6a808d7..d1443e5da2cb322cd787e077a01d9a540b0718b1 100644 (file)
@@ -139,7 +139,7 @@ public sealed class StepTriggerSystem : EntitySystem
         // and the entity is flying or currently weightless
         // Makes sense simulation wise to have this be part of steptrigger directly IMO
         if (!component.IgnoreWeightless && TryComp<PhysicsComponent>(otherUid, out var physics) &&
-            (physics.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(otherUid, physics)))
+            (physics.BodyStatus == BodyStatus.InAir || _gravity.IsWeightless(otherUid)))
             return false;
 
         var msg = new StepTriggerAttemptEvent { Source = uid, Tripper = otherUid };
index 4e44901c57f792a75b222b27efcd7d3e208320ec..db68c3517cee380fc4377cfbf3c20830eb06f210 100644 (file)
@@ -242,7 +242,7 @@ public sealed class ThrowingSystem : EntitySystem
         RaiseLocalEvent(user.Value, ref pushEv);
         const float massLimit = 5f;
 
-        if (pushEv.Push || _gravity.IsWeightless(user.Value))
+        if (pushEv.Push)
             _physics.ApplyLinearImpulse(user.Value, -impulseVector / physics.Mass * pushbackRatio * MathF.Min(massLimit, physics.Mass), body: userPhysics);
     }
 }
index 61ee8cdada0559a9d42ce396127b26efbfa8cd01..31f86d023684f98c95392d2f12ae75e6b7d5ff54 100644 (file)
@@ -391,7 +391,7 @@ public abstract partial class SharedGunSystem : EntitySystem
         var shooterEv = new ShooterImpulseEvent();
         RaiseLocalEvent(user, ref shooterEv);
 
-        if (shooterEv.Push || _gravity.IsWeightless(user, userPhysics))
+        if (shooterEv.Push)
             CauseImpulse(fromCoordinates, toCoordinates.Value, user, userPhysics);
     }
 
index cfd9c1363195d60c8afbe0d07d034dcfd3f7eaae..90bb90e6630dce7a2b804dbe02a143b678e27579 100644 (file)
@@ -9,6 +9,7 @@
     noRot: true
     drawdepth: Mobs
   - type: MobCollision
+  - type: GravityAffected
   - type: Physics
     bodyType: KinematicController
   - type: Fixtures
index e57223e1b40c6c32460abf35cac9a2d773cd5078..15f67245f1b31e969bd3a1d0c1e91ae2f70253fc 100644 (file)
@@ -6,11 +6,13 @@
   - type: Clickable
   - type: InteractionOutline
   - type: MovementIgnoreGravity
+    weightless: true
   - type: Sprite
     sprite: Objects/Fun/immovable_rod.rsi
     state: icon
     noRot: false
   - type: ImmovableRod
+  - type: GravityAffected
   - type: Physics
     bodyType: Dynamic
     linearDamping: 0
@@ -78,8 +80,6 @@
     damage:
       types:
         Blunt: 190
-  - type: MovementIgnoreGravity
-    gravityState: true
   - type: InputMover
   - type: MovementSpeedModifier
     baseWeightlessAcceleration: 5
index 4c4e44c28cd29eb96363074909355ce70093e4c7..bda59013787d02f296ed20233f8692f6b3560bff 100644 (file)
@@ -64,6 +64,7 @@
   - type: Pullable
   - type: Physics
     bodyType: KinematicController
+  - type: GravityAffected
   - type: Clickable
   - type: WiresPanel
   - type: Fixtures
index bb67d3f4cf7f8084a8964c15153022a6472d16c8..8490ba0042d7b32a64e4e14ce55e86651b059d32 100644 (file)
@@ -24,6 +24,7 @@
     soundHit:
       collection: MetalThud
   - type: CollisionWake
+  - type: GravityAffected
   - type: Physics
     bodyType: Dynamic
     fixedRotation: false
index 71971a6624313b5e8771b6c18e588e676977d52a..d4936c859c61cd73342c988e0d74e43a06a617a9 100644 (file)
@@ -7,6 +7,7 @@
   - type: Transform
     anchored: true
   - type: Clickable
+  - type: GravityAffected
   - type: Physics
     bodyType: Static
   - type: Fixtures