]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Predict thieving beacon (#39610)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Mon, 5 Jan 2026 12:17:17 +0000 (13:17 +0100)
committerGitHub <noreply@github.com>
Mon, 5 Jan 2026 12:17:17 +0000 (12:17 +0000)
predict thieving beacon

Content.Server/Objectives/Components/StealAreaComponent.cs [deleted file]
Content.Shared/Foldable/DeployFoldableSystem.cs
Content.Shared/Foldable/FoldableSystem.cs
Content.Shared/Objectives/Components/StealAreaComponent.cs [new file with mode: 0644]
Content.Shared/Thief/Components/ThiefBeaconComponent.cs [moved from Content.Server/Thief/Components/ThiefBeaconComponent.cs with 71% similarity]
Content.Shared/Thief/Systems/ThiefBeaconSystem.cs [moved from Content.Server/Thief/Systems/ThiefBeaconSystem.cs with 70% similarity]

diff --git a/Content.Server/Objectives/Components/StealAreaComponent.cs b/Content.Server/Objectives/Components/StealAreaComponent.cs
deleted file mode 100644 (file)
index 26e752f..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-using Content.Server.Objectives.Systems;
-using Content.Server.Thief.Systems;
-
-namespace Content.Server.Objectives.Components;
-
-/// <summary>
-/// An abstract component that allows other systems to count adjacent objects as "stolen" when controlling other systems
-/// </summary>
-[RegisterComponent, Access(typeof(StealConditionSystem), typeof(ThiefBeaconSystem))]
-public sealed partial class StealAreaComponent : Component
-{
-    [DataField]
-    public bool Enabled = true;
-
-    [DataField]
-    public float Range = 1f;
-
-    /// <summary>
-    /// all the minds that will be credited with stealing from this area.
-    /// </summary>
-    [DataField]
-    public HashSet<EntityUid> Owners = new();
-}
index c690f3d51f8d7898094368e4544bb612ac69cefd..8b510664e4e33a6527f6a0a48122504a42c150e2 100644 (file)
@@ -37,7 +37,7 @@ public sealed class DeployFoldableSystem : EntitySystem
     private void OnDragDropDragged(Entity<DeployFoldableComponent> ent, ref DragDropDraggedEvent args)
     {
         if (!TryComp<FoldableComponent>(ent, out var foldable)
-            || !_foldable.TrySetFolded(ent, foldable, true))
+            || !_foldable.TrySetFolded(ent, foldable, true, args.User))
             return;
 
         _hands.PickupOrDrop(args.User, ent.Owner);
@@ -77,7 +77,7 @@ public sealed class DeployFoldableSystem : EntitySystem
             || !_hands.TryDrop((args.User, hands), args.Used, targetDropLocation: args.ClickLocation))
             return;
 
-        if (!_foldable.TrySetFolded(ent, foldable, false))
+        if (!_foldable.TrySetFolded(ent, foldable, false, args.User))
         {
             _hands.TryPickup(args.User, args.Used, handsComp: hands);
             return;
index 63ef376d5f2ca125c727e0d2221985c0e3709d24..acfec8ac1f5503eea4bcccf2abda316a9fbb9a1e 100644 (file)
@@ -78,14 +78,14 @@ public sealed class FoldableSystem : EntitySystem
     /// <summary>
     /// Set the folded state of the given <see cref="FoldableComponent"/>
     /// </summary>
-    public void SetFolded(EntityUid uid, FoldableComponent component, bool folded)
+    public void SetFolded(EntityUid uid, FoldableComponent component, bool folded, EntityUid? user = null)
     {
         component.IsFolded = folded;
         Dirty(uid, component);
         _appearance.SetData(uid, FoldedVisuals.State, folded);
         _buckle.StrapSetEnabled(uid, !component.IsFolded);
 
-        var ev = new FoldedEvent(folded);
+        var ev = new FoldedEvent(folded, user);
         RaiseLocalEvent(uid, ref ev);
     }
 
@@ -97,7 +97,7 @@ public sealed class FoldableSystem : EntitySystem
 
     public bool TryToggleFold(EntityUid uid, FoldableComponent comp, EntityUid? folder = null)
     {
-        var result = TrySetFolded(uid, comp, !comp.IsFolded);
+        var result = TrySetFolded(uid, comp, !comp.IsFolded, folder);
         if (!result && folder != null)
         {
             if (comp.IsFolded)
@@ -129,7 +129,7 @@ public sealed class FoldableSystem : EntitySystem
     /// <summary>
     /// Try to fold/unfold
     /// </summary>
-    public bool TrySetFolded(EntityUid uid, FoldableComponent comp, bool state)
+    public bool TrySetFolded(EntityUid uid, FoldableComponent comp, bool state, EntityUid? user = null)
     {
         if (state == comp.IsFolded)
             return false;
@@ -137,7 +137,7 @@ public sealed class FoldableSystem : EntitySystem
         if (!CanToggleFold(uid, comp))
             return false;
 
-        SetFolded(uid, comp, state);
+        SetFolded(uid, comp, state, user);
         return true;
     }
 
@@ -180,6 +180,7 @@ public record struct FoldAttemptEvent(FoldableComponent Comp, bool Cancelled = f
 /// <summary>
 /// Event raised on an entity after it has been folded.
 /// </summary>
-/// <param name="IsFolded"></param>
+/// <param name="IsFolded">True is it has been folded, false if it has been unfolded.</param>
+/// <param name="User">The player who did the folding.</param>
 [ByRefEvent]
-public readonly record struct FoldedEvent(bool IsFolded);
+public readonly record struct FoldedEvent(bool IsFolded, EntityUid? User);
diff --git a/Content.Shared/Objectives/Components/StealAreaComponent.cs b/Content.Shared/Objectives/Components/StealAreaComponent.cs
new file mode 100644 (file)
index 0000000..50e9d32
--- /dev/null
@@ -0,0 +1,38 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Objectives.Components;
+
+/// <summary>
+/// An abstract component that allows other systems to count adjacent objects as "stolen" when controlling other systems
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class StealAreaComponent : Component
+{
+    /// <summary>
+    /// Is the component currently enabled?
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Enabled = true;
+
+    /// <summary>
+    /// The range to check for items in.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public float Range = 1f;
+
+    /// <summary>
+    /// All the minds that will be credited with stealing from this area.
+    /// </summary>
+    /// <remarks>
+    /// TODO: Network this when we have WeakEntityReference.
+    /// </remarks>
+    [DataField]
+    public HashSet<EntityUid> Owners = new();
+
+    /// <summary>
+    /// The count of the owner hashset.
+    /// This is a separate datafield because networking the list would cause PVS errors if an entity inside would be deleted and networked.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public int OwnerCount = 0;
+}
similarity index 71%
rename from Content.Server/Thief/Components/ThiefBeaconComponent.cs
rename to Content.Shared/Thief/Components/ThiefBeaconComponent.cs
index 65db79f8617c187de249412b06518075ade66c78..5701254c98d5444fd07ede829e510bb975ac9b1b 100644 (file)
@@ -1,12 +1,14 @@
-using Content.Server.Thief.Systems;
+using Content.Shared.Thief.Systems;
 using Robust.Shared.Audio;
+using Robust.Shared.GameStates;
 
-namespace Content.Server.Thief.Components;
+namespace Content.Shared.Thief.Components;
 
 /// <summary>
 /// working together with StealAreaComponent, allows the thief to count objects near the beacon as stolen when setting up.
 /// </summary>
-[RegisterComponent, Access(typeof(ThiefBeaconSystem))]
+[RegisterComponent, NetworkedComponent]
+[Access(typeof(ThiefBeaconSystem))]
 public sealed partial class ThiefBeaconComponent : Component
 {
     [DataField]
similarity index 70%
rename from Content.Server/Thief/Systems/ThiefBeaconSystem.cs
rename to Content.Shared/Thief/Systems/ThiefBeaconSystem.cs
index 069966a8a43683453c208985d57594a7cc6ba5a0..bff33cb7811b566716a54712fb09100ca5c1de51 100644 (file)
@@ -1,15 +1,15 @@
-using Content.Server.Mind;
-using Content.Server.Objectives.Components;
-using Content.Server.Thief.Components;
+using Content.Shared.Mind;
+using Content.Shared.Objectives.Components;
+using Content.Shared.Roles;
+using Content.Shared.Thief.Components;
 using Content.Shared.Examine;
 using Content.Shared.Foldable;
 using Content.Shared.Popups;
 using Content.Shared.Verbs;
-using Content.Shared.Roles;
 using Content.Shared.Roles.Components;
 using Robust.Shared.Audio.Systems;
 
-namespace Content.Server.Thief.Systems;
+namespace Content.Shared.Thief.Systems;
 
 /// <summary>
 /// <see cref="ThiefBeaconComponent"/>
@@ -18,7 +18,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
 {
     [Dependency] private readonly SharedAudioSystem _audio = default!;
     [Dependency] private readonly SharedPopupSystem _popup = default!;
-    [Dependency] private readonly MindSystem _mind = default!;
+    [Dependency] private readonly SharedMindSystem _mind = default!;
     [Dependency] private readonly SharedRoleSystem _roles = default!;
     public override void Initialize()
     {
@@ -46,7 +46,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
         {
             Act = () =>
             {
-                SetCoordinate(beacon, mind.Value);
+                SetCoordinate(beacon, mind.Value, user);
             },
             Message = Loc.GetString("thief-fulton-verb-message"),
             Text = Loc.GetString("thief-fulton-verb-text"),
@@ -56,7 +56,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
     private void OnFolded(Entity<ThiefBeaconComponent> beacon, ref FoldedEvent args)
     {
         if (args.IsFolded)
-            ClearCoordinate(beacon);
+            ClearCoordinate(beacon, args.User);
     }
 
     private void OnExamined(Entity<ThiefBeaconComponent> beacon, ref ExaminedEvent args)
@@ -64,23 +64,25 @@ public sealed class ThiefBeaconSystem : EntitySystem
         if (!TryComp<StealAreaComponent>(beacon, out var area))
             return;
 
-        args.PushText(Loc.GetString(area.Owners.Count == 0
+        args.PushText(Loc.GetString(area.OwnerCount == 0
             ? "thief-fulton-examined-unset"
             : "thief-fulton-examined-set"));
     }
 
-    private void SetCoordinate(Entity<ThiefBeaconComponent> beacon, EntityUid mind)
+    private void SetCoordinate(Entity<ThiefBeaconComponent> beacon, EntityUid mind, EntityUid? user = null)
     {
         if (!TryComp<StealAreaComponent>(beacon, out var area))
             return;
 
-        _audio.PlayPvs(beacon.Comp.LinkSound, beacon);
-        _popup.PopupEntity(Loc.GetString("thief-fulton-set"), beacon);
-        area.Owners.Clear(); //We only reconfigure the beacon for ourselves, we don't need multiple thieves to steal from the same beacon.
+        _audio.PlayPredicted(beacon.Comp.LinkSound, beacon, user);
+        _popup.PopupClient(Loc.GetString("thief-fulton-set"), beacon, user);
+        area.Owners.Clear(); // We only reconfigure the beacon for ourselves, we don't need multiple thieves to steal from the same beacon.
         area.Owners.Add(mind);
+        area.OwnerCount = area.Owners.Count;
+        Dirty(beacon.Owner, area);
     }
 
-    private void ClearCoordinate(Entity<ThiefBeaconComponent> beacon)
+    private void ClearCoordinate(Entity<ThiefBeaconComponent> beacon, EntityUid? user = null)
     {
         if (!TryComp<StealAreaComponent>(beacon, out var area))
             return;
@@ -88,8 +90,10 @@ public sealed class ThiefBeaconSystem : EntitySystem
         if (area.Owners.Count == 0)
             return;
 
-        _audio.PlayPvs(beacon.Comp.UnlinkSound, beacon);
-        _popup.PopupEntity(Loc.GetString("thief-fulton-clear"), beacon);
+        _audio.PlayPredicted(beacon.Comp.UnlinkSound, beacon, user);
+        _popup.PopupClient(Loc.GetString("thief-fulton-clear"), beacon, user);
         area.Owners.Clear();
+        area.OwnerCount = area.Owners.Count;
+        Dirty(beacon.Owner, area);
     }
 }