]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add prediction to Spill Container verb, add dummy TrySpill methods to shared (#25813)
authorTayrtahn <tayrtahn@gmail.com>
Fri, 29 Mar 2024 09:00:09 +0000 (05:00 -0400)
committerGitHub <noreply@github.com>
Fri, 29 Mar 2024 09:00:09 +0000 (02:00 -0700)
* Moved abstract spill methods to shared; added prediction to spill container verb.

* Rerun tests

* Requested changes

* Note Client behavior in Spill method docs

Content.Client/Fluids/PuddleSystem.cs
Content.Server/Fluids/Components/PreventSpillerComponent.cs [deleted file]
Content.Server/Fluids/EntitySystems/PuddleSystem.Spillable.cs
Content.Server/Fluids/EntitySystems/PuddleSystem.cs
Content.Shared/Fluids/Components/PreventSpillerComponent.cs [new file with mode: 0644]
Content.Shared/Fluids/Components/SpillableComponent.cs
Content.Shared/Fluids/SharedPuddleSystem.Spillable.cs
Content.Shared/Fluids/SharedPuddleSystem.cs

index 54b1d5b86b931a51b111690a7edd7c0ef9bfb8c4..5dbffe0fd2f13adb970e734e0fd3734aee07ac65 100644 (file)
@@ -1,7 +1,9 @@
 using Content.Client.IconSmoothing;
+using Content.Shared.Chemistry.Components;
 using Content.Shared.Fluids;
 using Content.Shared.Fluids.Components;
 using Robust.Client.GameObjects;
+using Robust.Shared.Map;
 
 namespace Content.Client.Fluids;
 
@@ -21,7 +23,7 @@ public sealed class PuddleSystem : SharedPuddleSystem
         if (args.Sprite == null)
             return;
 
-        float volume = 1f;
+        var volume = 1f;
 
         if (args.AppearanceData.TryGetValue(PuddleVisuals.CurrentVolume, out var volumeObj))
         {
@@ -64,4 +66,38 @@ public sealed class PuddleSystem : SharedPuddleSystem
             args.Sprite.Color *= baseColor;
         }
     }
+
+    #region Spill
+
+    // Maybe someday we'll have clientside prediction for entity spawning, but not today.
+    // Until then, these methods do nothing on the client.
+    /// <inheritdoc/>
+    public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null)
+    {
+        puddleUid = EntityUid.Invalid;
+        return false;
+    }
+
+    /// <inheritdoc/>
+    public override bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true)
+    {
+        puddleUid = EntityUid.Invalid;
+        return false;
+    }
+
+    /// <inheritdoc/>
+    public override bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true, TransformComponent? transformComponent = null)
+    {
+        puddleUid = EntityUid.Invalid;
+        return false;
+    }
+
+    /// <inheritdoc/>
+    public override bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true, bool tileReact = true)
+    {
+        puddleUid = EntityUid.Invalid;
+        return false;
+    }
+
+    #endregion Spill
 }
diff --git a/Content.Server/Fluids/Components/PreventSpillerComponent.cs b/Content.Server/Fluids/Components/PreventSpillerComponent.cs
deleted file mode 100644 (file)
index 37096f1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Content.Server.Fluids.Components;
-
-[RegisterComponent]
-public sealed partial class PreventSpillerComponent : Component
-{
-
-}
index efaca271d3bb6d657a71ddb3ad835ef5061f6cbc..ce5b5b363782ba1b939459ef17ecb9c01b17e3c2 100644 (file)
@@ -1,5 +1,4 @@
 using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Server.Fluids.Components;
 using Content.Server.Nutrition.EntitySystems;
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.EntitySystems;
@@ -8,7 +7,6 @@ using Content.Shared.Chemistry.Reagent;
 using Content.Shared.Clothing.Components;
 using Content.Shared.CombatMode.Pacification;
 using Content.Shared.Database;
-using Content.Shared.DoAfter;
 using Content.Shared.FixedPoint;
 using Content.Shared.Fluids.Components;
 using Content.Shared.IdentityManagement;
@@ -16,7 +14,6 @@ using Content.Shared.Inventory.Events;
 using Content.Shared.Popups;
 using Content.Shared.Spillable;
 using Content.Shared.Throwing;
-using Content.Shared.Verbs;
 using Content.Shared.Weapons.Melee.Events;
 using Robust.Shared.Player;
 
@@ -24,9 +21,6 @@ namespace Content.Server.Fluids.EntitySystems;
 
 public sealed partial class PuddleSystem
 {
-    [Dependency] private readonly OpenableSystem _openable = default!;
-    [Dependency] private readonly IEntityManager _entityManager = default!;
-
     protected override void InitializeSpillable()
     {
         base.InitializeSpillable();
@@ -34,7 +28,6 @@ public sealed partial class PuddleSystem
         SubscribeLocalEvent<SpillableComponent, LandEvent>(SpillOnLand);
         // Openable handles the event if it's closed
         SubscribeLocalEvent<SpillableComponent, MeleeHitEvent>(SplashOnMeleeHit, after: [typeof(OpenableSystem)]);
-        SubscribeLocalEvent<SpillableComponent, GetVerbsEvent<Verb>>(AddSpillVerb);
         SubscribeLocalEvent<SpillableComponent, GotEquippedEvent>(OnGotEquipped);
         SubscribeLocalEvent<SpillableComponent, SolutionContainerOverflowEvent>(OnOverflow);
         SubscribeLocalEvent<SpillableComponent, SpillDoAfterEvent>(OnDoAfter);
@@ -134,7 +127,7 @@ public sealed partial class PuddleSystem
         if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))
             return;
 
-        if (_openable.IsClosed(entity.Owner))
+        if (Openable.IsClosed(entity.Owner))
             return;
 
         if (args.User != null)
@@ -153,7 +146,7 @@ public sealed partial class PuddleSystem
     private void OnAttemptPacifiedThrow(Entity<SpillableComponent> ent, ref AttemptPacifiedThrowEvent args)
     {
         // Don’t care about closed containers.
-        if (_openable.IsClosed(ent))
+        if (Openable.IsClosed(ent))
             return;
 
         // Don’t care about empty containers.
@@ -163,57 +156,6 @@ public sealed partial class PuddleSystem
         args.Cancel("pacified-cannot-throw-spill");
     }
 
-    private void AddSpillVerb(Entity<SpillableComponent> entity, ref GetVerbsEvent<Verb> args)
-    {
-        if (!args.CanAccess || !args.CanInteract)
-            return;
-
-        if (!_solutionContainerSystem.TryGetSolution(args.Target, entity.Comp.SolutionName, out var soln, out var solution))
-            return;
-
-        if (_openable.IsClosed(args.Target))
-            return;
-
-        if (solution.Volume == FixedPoint2.Zero)
-            return;
-
-        if (_entityManager.HasComponent<PreventSpillerComponent>(args.User))
-            return;
-
-
-        Verb verb = new()
-        {
-            Text = Loc.GetString("spill-target-verb-get-data-text")
-        };
-
-        // TODO VERB ICONS spill icon? pouring out a glass/beaker?
-        if (entity.Comp.SpillDelay == null)
-        {
-            var target = args.Target;
-            verb.Act = () =>
-            {
-                var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume);
-                TrySpillAt(Transform(target).Coordinates, puddleSolution, out _);
-            };
-        }
-        else
-        {
-            var user = args.User;
-            verb.Act = () =>
-            {
-                _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, entity.Comp.SpillDelay ?? 0, new SpillDoAfterEvent(), entity.Owner, target: entity.Owner)
-                {
-                    BreakOnDamage = true,
-                    BreakOnMove = true,
-                    NeedHand = true,
-                });
-            };
-        }
-        verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately.
-        verb.DoContactInteraction = true;
-        args.Verbs.Add(verb);
-    }
-
     private void OnDoAfter(Entity<SpillableComponent> entity, ref SpillDoAfterEvent args)
     {
         if (args.Handled || args.Cancelled || args.Args.Target == null)
index e3481f98da3b7fabacb9e278e98139c9f40381b3..923210cc73c2c4d67e5fbcb5a1258ec2569508b6 100644 (file)
@@ -46,7 +46,6 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
     [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!;
     [Dependency] private readonly AudioSystem _audio = default!;
-    [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
     [Dependency] private readonly EntityLookupSystem _lookup = default!;
     [Dependency] private readonly ReactiveSystem _reactive = default!;
     [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -551,11 +550,8 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
 
     #region Spill
 
-    /// <summary>
-    ///     First splashes reagent on reactive entities near the spilling entity, then spills the rest regularly to a
-    ///     puddle. This is intended for 'destructive' spills, like when entities are destroyed or thrown.
-    /// </summary>
-    public bool TrySplashSpillAt(EntityUid uid,
+    /// <inheritdoc/>
+    public override bool TrySplashSpillAt(EntityUid uid,
         EntityCoordinates coordinates,
         Solution solution,
         out EntityUid puddleUid,
@@ -600,11 +596,8 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         return TrySpillAt(coordinates, solution, out puddleUid, sound);
     }
 
-    /// <summary>
-    ///     Spills solution at the specified coordinates.
-    /// Will add to an existing puddle if present or create a new one if not.
-    /// </summary>
-    public bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true)
+    /// <inheritdoc/>
+    public override bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true)
     {
         if (solution.Volume == 0)
         {
@@ -622,10 +615,8 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         return TrySpillAt(_map.GetTileRef(gridUid.Value, mapGrid, coordinates), solution, out puddleUid, sound);
     }
 
-    /// <summary>
-    /// <see cref="TrySpillAt(Robust.Shared.Map.EntityCoordinates,Content.Shared.Chemistry.Components.Solution,out Robust.Shared.GameObjects.EntityUid,bool)"/>
-    /// </summary>
-    public bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true,
+    /// <inheritdoc/>
+    public override bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true,
         TransformComponent? transformComponent = null)
     {
         if (!Resolve(uid, ref transformComponent, false))
@@ -637,10 +628,8 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
         return TrySpillAt(transformComponent.Coordinates, solution, out puddleUid, sound: sound);
     }
 
-    /// <summary>
-    /// <see cref="TrySpillAt(Robust.Shared.Map.EntityCoordinates,Content.Shared.Chemistry.Components.Solution,out Robust.Shared.GameObjects.EntityUid,bool)"/>
-    /// </summary>
-    public bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true,
+    /// <inheritdoc/>
+    public override bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true,
         bool tileReact = true)
     {
         if (solution.Volume <= 0)
diff --git a/Content.Shared/Fluids/Components/PreventSpillerComponent.cs b/Content.Shared/Fluids/Components/PreventSpillerComponent.cs
new file mode 100644 (file)
index 0000000..e396d9f
--- /dev/null
@@ -0,0 +1,12 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Fluids.Components;
+
+/// <summary>
+/// Blocks this entity's ability to spill solution containing entities via the verb menu.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class PreventSpillerComponent : Component
+{
+
+}
index a1b5fa17eb8c6ff62d6d69f247db365f5dbdf295..428d91f2de75f9fdf492dc2a67f6fd3a6d000b71 100644 (file)
@@ -2,6 +2,12 @@ using Content.Shared.FixedPoint;
 
 namespace Content.Shared.Fluids.Components;
 
+/// <summary>
+/// Makes a solution contained in this entity spillable.
+/// Spills can occur when a container with this component overflows,
+/// is used to melee attack something, is equipped (see <see cref="SpillWorn"/>),
+/// lands after being thrown, or has the Spill verb used.
+/// </summary>
 [RegisterComponent]
 public sealed partial class SpillableComponent : Component
 {
index 77730e5afc51ce3209fbe48c01734e9c77c77710..1e9e742a38fada7699b8d2ceaa5853b3913a9fe1 100644 (file)
@@ -1,14 +1,23 @@
+using Content.Shared.Database;
+using Content.Shared.DoAfter;
 using Content.Shared.Examine;
+using Content.Shared.FixedPoint;
 using Content.Shared.Fluids.Components;
+using Content.Shared.Nutrition.EntitySystems;
+using Content.Shared.Spillable;
+using Content.Shared.Verbs;
 using Content.Shared.Weapons.Melee;
 
 namespace Content.Shared.Fluids;
 
 public abstract partial class SharedPuddleSystem
 {
+    [Dependency] protected readonly SharedOpenableSystem Openable = default!;
+
     protected virtual void InitializeSpillable()
     {
         SubscribeLocalEvent<SpillableComponent, ExaminedEvent>(OnExamined);
+        SubscribeLocalEvent<SpillableComponent, GetVerbsEvent<Verb>>(AddSpillVerb);
     }
 
     private void OnExamined(Entity<SpillableComponent> entity, ref ExaminedEvent args)
@@ -21,4 +30,55 @@ public abstract partial class SharedPuddleSystem
                 args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon"));
         }
     }
+
+    private void AddSpillVerb(Entity<SpillableComponent> entity, ref GetVerbsEvent<Verb> args)
+    {
+        if (!args.CanAccess || !args.CanInteract)
+            return;
+
+        if (!_solutionContainerSystem.TryGetSolution(args.Target, entity.Comp.SolutionName, out var soln, out var solution))
+            return;
+
+        if (Openable.IsClosed(args.Target))
+            return;
+
+        if (solution.Volume == FixedPoint2.Zero)
+            return;
+
+        if (HasComp<PreventSpillerComponent>(args.User))
+            return;
+
+
+        Verb verb = new()
+        {
+            Text = Loc.GetString("spill-target-verb-get-data-text")
+        };
+
+        // TODO VERB ICONS spill icon? pouring out a glass/beaker?
+        if (entity.Comp.SpillDelay == null)
+        {
+            var target = args.Target;
+            verb.Act = () =>
+            {
+                var puddleSolution = _solutionContainerSystem.SplitSolution(soln.Value, solution.Volume);
+                TrySpillAt(Transform(target).Coordinates, puddleSolution, out _);
+            };
+        }
+        else
+        {
+            var user = args.User;
+            verb.Act = () =>
+            {
+                _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, entity.Comp.SpillDelay ?? 0, new SpillDoAfterEvent(), entity.Owner, target: entity.Owner)
+                {
+                    BreakOnDamage = true,
+                    BreakOnMove = true,
+                    NeedHand = true,
+                });
+            };
+        }
+        verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately.
+        verb.DoContactInteraction = true;
+        args.Verbs.Add(verb);
+    }
 }
index e4bd61baa8ebb1d0c9869bff1a19fc26035caa13..f573c042c5521e94ae9e657aeb2ad29bff1b5a6f 100644 (file)
@@ -1,12 +1,14 @@
 using Content.Shared.Chemistry.Components;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
+using Content.Shared.DoAfter;
 using Content.Shared.DragDrop;
 using Content.Shared.Examine;
 using Content.Shared.FixedPoint;
 using Content.Shared.Fluids.Components;
 using Content.Shared.Movement.Events;
 using Content.Shared.StepTrigger.Components;
+using Robust.Shared.Map;
 using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Fluids;
@@ -15,6 +17,7 @@ public abstract partial class SharedPuddleSystem : EntitySystem
 {
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
     [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
+    [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
 
     /// <summary>
     /// The lowest threshold to be considered for puddle sprite states as well as slipperiness of a puddle.
@@ -106,4 +109,54 @@ public abstract partial class SharedPuddleSystem : EntitySystem
                 args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
         }
     }
+
+    #region Spill
+    // These methods are in Shared to make it easier to interact with PuddleSystem in Shared code.
+    // Note that they always fail when run on the client, not creating a puddle and returning false.
+    // Adding proper prediction to this system would require spawning temporary puddle entities on the
+    // client and replacing or merging them with the ones spawned by the server when the client goes to
+    // replicate those, and I am not enough of a wizard to attempt implementing that.
+
+    /// <summary>
+    ///     First splashes reagent on reactive entities near the spilling entity, then spills the rest regularly to a
+    ///     puddle. This is intended for 'destructive' spills, like when entities are destroyed or thrown.
+    /// </summary>
+    /// <remarks>
+    /// On the client, this will always set <paramref name="puddleUid"/> to <see cref="EntityUid.Invalid"> and return false.
+    /// </remarks>
+    public abstract bool TrySplashSpillAt(EntityUid uid,
+        EntityCoordinates coordinates,
+        Solution solution,
+        out EntityUid puddleUid,
+        bool sound = true,
+        EntityUid? user = null);
+
+    /// <summary>
+    ///     Spills solution at the specified coordinates.
+    /// Will add to an existing puddle if present or create a new one if not.
+    /// </summary>
+    /// <remarks>
+    /// On the client, this will always set <paramref name="puddleUid"/> to <see cref="EntityUid.Invalid"> and return false.
+    /// </remarks>
+    public abstract bool TrySpillAt(EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true);
+
+    /// <summary>
+    /// <see cref="TrySpillAt(EntityCoordinates, Solution, out EntityUid, bool)"/>
+    /// </summary>
+    /// <remarks>
+    /// On the client, this will always set <paramref name="puddleUid"/> to <see cref="EntityUid.Invalid"> and return false.
+    /// </remarks>
+    public abstract bool TrySpillAt(EntityUid uid, Solution solution, out EntityUid puddleUid, bool sound = true,
+        TransformComponent? transformComponent = null);
+
+    /// <summary>
+    /// <see cref="TrySpillAt(EntityCoordinates, Solution, out EntityUid, bool)"/>
+    /// </summary>
+    /// <remarks>
+    /// On the client, this will always set <paramref name="puddleUid"/> to <see cref="EntityUid.Invalid"> and return false.
+    /// </remarks>
+    public abstract bool TrySpillAt(TileRef tileRef, Solution solution, out EntityUid puddleUid, bool sound = true,
+        bool tileReact = true);
+
+    #endregion Spill
 }