From a4080e496b7d1d62cfc42b7c1ab0132b7c76b860 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Tue, 25 Nov 2025 20:54:17 -0600 Subject: [PATCH 01/20] C --- .../en-US/_Null/machines/claimant_stake.ftl | 1 + .../Machines/claimant_stake.rsi/base.png | Bin 0 -> 1063 bytes .../Machines/claimant_stake.rsi/meta.json | 18 ++++++++++++++++++ .../Machines/claimant_stake.rsi/on.png | Bin 0 -> 417 bytes 4 files changed, 19 insertions(+) create mode 100644 Resources/Locale/en-US/_Null/machines/claimant_stake.ftl create mode 100644 Resources/Textures/_Null/Structures/Machines/claimant_stake.rsi/base.png create mode 100644 Resources/Textures/_Null/Structures/Machines/claimant_stake.rsi/meta.json create mode 100644 Resources/Textures/_Null/Structures/Machines/claimant_stake.rsi/on.png diff --git a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl new file mode 100644 index 00000000000..1f86d4c8f13 --- /dev/null +++ b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl @@ -0,0 +1 @@ +emancipation-grid-examined-user = This is a claimant stake belonging to {$user}! \ No newline at end of file diff --git a/Resources/Textures/_Null/Structures/Machines/claimant_stake.rsi/base.png b/Resources/Textures/_Null/Structures/Machines/claimant_stake.rsi/base.png new file mode 100644 index 0000000000000000000000000000000000000000..80b0fce2c90dc31e6dbddcc6aef13a288d7d7caf GIT binary patch literal 1063 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0U~I~Cb`J1#c2)=|%1_J8No8Qr zI6rlwr3bU4M5}%HQRbtEM3ls$U9?$09tv8d(d5)2xQDCUyxpmccekLl_rb6YA2RlE zNoVKk=CC?`VA!~AL(rCq8~$w;6l&q%A?#a*JqA&w>75&G+wCt-LqSDae^+)%@E3du6k>DeiyzJKlZYeEYUj>N`rh{!Mk3 z_#;redPOV$f=uRf>$_&{WEP)&;nNJ$8086CsaY#U_RKwHe%9$T@0Ii7RljdOe6wP*LXE!*wSv-}nO$(EZ~v~szHI-ojpZFlxNMH%E-uCUfw1rCueQ#Wnp1sW@c_`YGP_?%D}+f+T59uo}Cb%GNHe3 z_N-}hW>1?sWomMAo|KeaTx=vLwoh&anJ-fk-L15RjOeSEA?V8lqsTXQ*eHlXuGwsHP`1!ZXd&Q;UHE z$YEuWVq|4t1hTw1 z8G!mHursg#RTvl<8!#?_nEL-ePyh%T+t1#qO6uASG=tsK#WBRffA6G+d50BvTHXq9 z#qC~t_tw_t@Ac>BHnO;^__e-WMCkWtNx@*5m7VVxlv;MwIOr~TZ<6S`?jf6l8s}`Q zDL&19Zj*&S&S8i?RH^-SGSiJ2C%!dYyL_hS7xU)UqCHG2m>yKvF>Jo}TjoAPT5?Zw zkA)dCLngy7fu9EVirk<8Nhhq4_|F(2 V;Ku2y#-iXD}xjxD+42tvp@3Xlx~Oh9v) zz$$|*Er2YjE<*zYknC!8=SA7~pXLIY0iG_7Ar*0NZyItPFyLSfIQjQ|j>48;x7G_^ zY*~)K*N}?Q2nSkl*YEwU{JON-zKwJNNxd{`dFpJq8K?@jAkC WO<&}L{Dg1^5Z}|)&t;ucLK6UeG+p-q literal 0 HcmV?d00001 From eba55ef43369065bd2aa8b226a5e9bf2b0122669 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Tue, 25 Nov 2025 20:54:33 -0600 Subject: [PATCH 02/20] Corrected Null Machine Localizations --- .../en-US/_Null/{machines.ftl => machines/emancipation_grid.ftl} | 0 Resources/Locale/en-US/_Null/{ => machines}/holopads.ftl | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Resources/Locale/en-US/_Null/{machines.ftl => machines/emancipation_grid.ftl} (100%) rename Resources/Locale/en-US/_Null/{ => machines}/holopads.ftl (100%) diff --git a/Resources/Locale/en-US/_Null/machines.ftl b/Resources/Locale/en-US/_Null/machines/emancipation_grid.ftl similarity index 100% rename from Resources/Locale/en-US/_Null/machines.ftl rename to Resources/Locale/en-US/_Null/machines/emancipation_grid.ftl diff --git a/Resources/Locale/en-US/_Null/holopads.ftl b/Resources/Locale/en-US/_Null/machines/holopads.ftl similarity index 100% rename from Resources/Locale/en-US/_Null/holopads.ftl rename to Resources/Locale/en-US/_Null/machines/holopads.ftl From 0d03443f5cd462bde0fab1eac7a5ad8e4b747295 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Tue, 25 Nov 2025 22:06:06 -0600 Subject: [PATCH 03/20] Fixed Sprite for Claimant Stake Lights --- .../Machines/claimant_stake.rsi/on.png | Bin 417 -> 417 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/_Null/Structures/Machines/claimant_stake.rsi/on.png b/Resources/Textures/_Null/Structures/Machines/claimant_stake.rsi/on.png index 7b00d8be23f41861fcb1d522642efee23f328384..310d561a54ce565e348ccc8b2de9248a3c559d3a 100644 GIT binary patch delta 146 zcmZ3;ypVZ+9 z978JN-rjQLVo>B@aCrH@-mK%>m9wp0VYrx6B?{gHk1iQ6f_+ra){Jn-$j7B&k1H)av_qX!v(rWuYmH;&| xXuPlpojT_nJ41ta?)#Vg@9*7v3=;n1b%f=bzQ_mp3E>VPzNf37%Q~loCIC~yIV}JH From 843dd661747723790c464bef2df63a4b2f58279d Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:56:32 -0600 Subject: [PATCH 04/20] Minor Todo correction on ZombieSystem.cs --- Content.Server/Zombies/ZombieSystem.cs | 3 --- .../Locale/en-US/_Null/machines/claimant_stake.ftl | 12 +++++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index 56b60e62e02..c47450b2677 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -69,9 +69,6 @@ public override void Initialize() SubscribeLocalEvent(OnPendingMapInit); SubscribeLocalEvent(OnDamageChanged); - - // TODO: Add OnCure event read, or whatever method of curing besides cloning comes up. - // Also a possible list of component *types* could be better, especially if using reflection to handle them. } private void OnBeforeRemoveAnomalyOnDeath(Entity ent, diff --git a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl index 1f86d4c8f13..65b121aabb3 100644 --- a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl +++ b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl @@ -1 +1,11 @@ -emancipation-grid-examined-user = This is a claimant stake belonging to {$user}! \ No newline at end of file +claimant-stake-default-user = nobody identifiable +claimant-stake-alone-popup = Only one claimant stake allowed per hull! +claimant-stake-invalid-wreck-popup = This is not a valid wreckage! +claimant-stake-repeat-claim-popup = This wreckage already has been claimed! +claimant-stake-grid-claiming = Only one claimant stake allowed per hull! +claimant-stake-grid-claimant-examine = This hull has {$claimant -> +*[false] [color=red]no claimants[/color] +[true] [color=green]a claimant[/color] +}. +claimant-stake-examined-user = It belongs to {$user}! +claimant-wreckage-name = Wreckage Claim of {$user} \ No newline at end of file From 09d7064f722f8f479805ff554a5dd2e89637e9bd Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Tue, 25 Nov 2025 23:57:10 -0600 Subject: [PATCH 05/20] Extending Outside of Scope to Fix Comments in Multiple Classes & Handle Null Extension Class --- .../Systems/ShuttleConsoleLockSystem.cs | 8 ++-- .../_Null/Systems/NullExtensionSystem.cs | 45 +++++++++++++++++++ .../Systems/SharedClaimantStakeSystem.cs | 3 ++ .../Surgery/SharedSurgerySystem.Steps.cs | 9 +--- 4 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 Content.Server/_Null/Systems/NullExtensionSystem.cs create mode 100644 Content.Shared/_Null/Systems/SharedClaimantStakeSystem.cs diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleLockSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleLockSystem.cs index 5fca9752f2c..9cbaf773918 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleLockSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleLockSystem.cs @@ -249,9 +249,9 @@ private bool TryUnlockWithVoucher(EntityUid console, EntityUid voucher, ShuttleC var deedFound = false; var query = EntityQueryEnumerator(); - while (query.MoveNext(out var entity, out var deed)) + while (query.MoveNext(out _, out var deed)) { - var deedShuttleId = deed.ShuttleUid.HasValue ? deed.ShuttleUid.Value.ToString() : null; + var deedShuttleId = deed.ShuttleUid?.ToString(); // Check if this deed was purchased with this specific voucher and matches the shuttle ID if (deed.PurchasedWithVoucher && @@ -302,9 +302,9 @@ private bool TryLockWithVoucher(EntityUid console, EntityUid voucher, ShuttleCon var deedFound = false; var query = EntityQueryEnumerator(); - while (query.MoveNext(out var entity, out var deed)) + while (query.MoveNext(out _, out var deed)) { - var deedShuttleId = deed.ShuttleUid.HasValue ? deed.ShuttleUid.Value.ToString() : null; + var deedShuttleId = deed.ShuttleUid?.ToString(); // Check if this deed was purchased with this specific voucher and matches the shuttle ID if (deed.PurchasedWithVoucher && diff --git a/Content.Server/_Null/Systems/NullExtensionSystem.cs b/Content.Server/_Null/Systems/NullExtensionSystem.cs new file mode 100644 index 00000000000..8b56872d02a --- /dev/null +++ b/Content.Server/_Null/Systems/NullExtensionSystem.cs @@ -0,0 +1,45 @@ +using System.Diagnostics.CodeAnalysis; +using Robust.Shared.Prototypes; + +namespace Content.Server._Null.Systems; + +[SuppressMessage("ReSharper", "InconsistentNaming")] +public interface INullExtensionSystem +{ + bool AreAkinEntitiesPresentOnGrid(Entity ent) where T : IComponent; + EntProtoId? GetProtoID(EntityUid ent); +} + +public sealed partial class NullExtensionSystem : EntitySystem, INullExtensionSystem +{ + public override void Initialize() + { + base.Initialize(); + IoCManager.Register(); + } + public bool AreAkinEntitiesPresentOnGrid(Entity ent) where T : IComponent + { + var query = AllEntityQuery(); + + while (query.MoveNext(out var other)) + { + if (other.Equals(ent.Comp)) + continue; + + if (other.GetType() != ent.Comp.GetType()) + continue; + + return true; + } + + return false; + } + + public EntProtoId? GetProtoID(EntityUid ent) + { + if (!TryComp(ent, out var metaData)) + return null; + + return metaData.EntityPrototype?.ID; + } +} diff --git a/Content.Shared/_Null/Systems/SharedClaimantStakeSystem.cs b/Content.Shared/_Null/Systems/SharedClaimantStakeSystem.cs new file mode 100644 index 00000000000..21c991cd111 --- /dev/null +++ b/Content.Shared/_Null/Systems/SharedClaimantStakeSystem.cs @@ -0,0 +1,3 @@ +namespace Content.Shared._Null.Systems; + +public abstract class SharedClaimantStakeSystem : EntitySystem; diff --git a/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.Steps.cs b/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.Steps.cs index 49d9334f1aa..d0807d60b6e 100644 --- a/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.Steps.cs +++ b/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.Steps.cs @@ -27,6 +27,7 @@ using Content.Shared.Item; using Content.Shared.Popups; using Robust.Shared.Prototypes; + //using Content.Shared.Mood; namespace Content.Shared._Shitmed.Medical.Surgery; @@ -246,14 +247,6 @@ private void OnToolCanPerform(Entity ent, ref SurgeryCanPe } } - private EntProtoId? GetProtoId(EntityUid entityUid) - { - if (!TryComp(entityUid, out var metaData)) - return null; - - return metaData.EntityPrototype?.ID; - } - // I wonder if theres not a function that can do this already. private bool HasDamageGroup(EntityUid entity, string[] group, out DamageableComponent? damageable) { From 49c2b92276363640e8b54ea1ceddc49255e96c73 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:00:25 -0600 Subject: [PATCH 06/20] Modified GCAbleObjectComponent.cs to Permit ClaimantStakeSystem.cs Access --- .../Worldgen/Components/GC/GCAbleObjectComponent.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Content.Server/Worldgen/Components/GC/GCAbleObjectComponent.cs b/Content.Server/Worldgen/Components/GC/GCAbleObjectComponent.cs index b6f7ee16846..d1a9eb422d4 100644 --- a/Content.Server/Worldgen/Components/GC/GCAbleObjectComponent.cs +++ b/Content.Server/Worldgen/Components/GC/GCAbleObjectComponent.cs @@ -1,3 +1,4 @@ +using Content.Server._Null.Systems; using Content.Server.Worldgen.Prototypes; using Content.Server.Worldgen.Systems.GC; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -5,11 +6,11 @@ namespace Content.Server.Worldgen.Components.GC; /// -/// This is used for whether or not a GCable object is "dirty". Firing GCDirtyEvent on the object is the correct way to +/// This is used for whether a GCable object is "dirty". Firing GCDirtyEvent on the object is the correct way to /// set this up. /// [RegisterComponent] -[Access(typeof(GCQueueSystem))] +[Access(typeof(GCQueueSystem), typeof(ClaimantStakeSystem))] public sealed partial class GCAbleObjectComponent : Component { /// From 26576aba45b9af091f764b8f65ab134efabda758 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:02:21 -0600 Subject: [PATCH 07/20] Added Power Popup to claimant_stake.ftl --- Resources/Locale/en-US/_Null/machines/claimant_stake.ftl | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl index 65b121aabb3..191e504f956 100644 --- a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl +++ b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl @@ -1,6 +1,7 @@ claimant-stake-default-user = nobody identifiable claimant-stake-alone-popup = Only one claimant stake allowed per hull! claimant-stake-invalid-wreck-popup = This is not a valid wreckage! +claimant-stake-no-power-popup = There is not enough power available! claimant-stake-repeat-claim-popup = This wreckage already has been claimed! claimant-stake-grid-claiming = Only one claimant stake allowed per hull! claimant-stake-grid-claimant-examine = This hull has {$claimant -> From c5e81dd49aadece0f5e420c0470cd672e4145fd7 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:16:29 -0600 Subject: [PATCH 08/20] Added Claiming Locale to claimant_stake.ftl --- Resources/Locale/en-US/_Null/machines/claimant_stake.ftl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl index 191e504f956..6d3539717ec 100644 --- a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl +++ b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl @@ -2,6 +2,8 @@ claimant-stake-default-user = nobody identifiable claimant-stake-alone-popup = Only one claimant stake allowed per hull! claimant-stake-invalid-wreck-popup = This is not a valid wreckage! claimant-stake-no-power-popup = There is not enough power available! +claimant-stake-claim-success = Wreck successfully claimed! +claimant-stake-disabling-popup = Claimant stake shutting down! claimant-stake-repeat-claim-popup = This wreckage already has been claimed! claimant-stake-grid-claiming = Only one claimant stake allowed per hull! claimant-stake-grid-claimant-examine = This hull has {$claimant -> From a0c5835cc23425d00b2c3c5cd97f7b2170ac8698 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:24:42 -0600 Subject: [PATCH 09/20] Refactored EmancipationGridSystem.cs --- .../_Null/Systems/EmancipationGridSystem.cs | 72 ++++++------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/Content.Server/_Null/Systems/EmancipationGridSystem.cs b/Content.Server/_Null/Systems/EmancipationGridSystem.cs index 7c24caa5086..99e2b8de5cb 100644 --- a/Content.Server/_Null/Systems/EmancipationGridSystem.cs +++ b/Content.Server/_Null/Systems/EmancipationGridSystem.cs @@ -3,7 +3,6 @@ using Content.Server.Construction; using Content.Server.Explosion.Components; using Content.Server.Materials; -using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Examine; using Content.Shared.Interaction; @@ -14,6 +13,8 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Physics.Components; +#pragma warning disable CS0618 // Type or member is obsolete + namespace Content.Server._Null.Systems; public sealed class EmancipationGridSystem : EntitySystem @@ -25,6 +26,7 @@ public sealed class EmancipationGridSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly MaterialStorageSystem _material = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly INullExtensionSystem _nullExt = default!; public override void Initialize() { @@ -66,23 +68,6 @@ private void OnComponentShutdown(Entity ent, ref Comp ent.Comp.EmancipatedGrid = null; } - private bool AreOtherEmancipationGridsPresent(Entity device) - { - // Checking to ensure there aren't other emancipation grids, else it will simply not work! - var emancipationGrids = AllEntityQuery(); - while (emancipationGrids.MoveNext(out var otherDeviceComponent)) - { - if (otherDeviceComponent == device.Comp) - continue; - if (otherDeviceComponent.EmancipatedGrid != Transform(device).GridUid) - continue; // Short-Circuit - _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), device, PopupType.Medium); - return true; - } - - return false; - } - private void OnComponentInit(Entity device, ref ComponentInit args) { EnsureComp(device); @@ -95,25 +80,31 @@ private void OnComponentInit(Entity device, ref Compo new SoundPathSpecifier(EmancipationGridComponent.DefaultOutputSound, EmancipationGridComponent.DefaultAudioParameters); - if (AreOtherEmancipationGridsPresent(device)) + if (_nullExt.AreAkinEntitiesPresentOnGrid(device)) { - Stop(device); + _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), device, PopupType.Medium); + this.Stop(EntityManager, device); + device.Comp.EmancipatedGrid = null; return; } - Start(device); + this.Start(EntityManager, device); + device.Comp.EmancipatedGrid = Transform(device).GridUid; // Set current grid } private void HandlePowerChange(Entity device, ref PowerChangedEvent args) { device.Comp.IsPowered = this.IsPowered(device.Owner, EntityManager); - if (AreOtherEmancipationGridsPresent(device) || !device.Comp.IsPowered) + if (_nullExt.AreAkinEntitiesPresentOnGrid(device) || !device.Comp.IsPowered) { - Stop(device); + _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), device, PopupType.Medium); + this.Stop(EntityManager, device); + device.Comp.EmancipatedGrid = null; return; } - Start(device); + this.Start(EntityManager, device); + device.Comp.EmancipatedGrid = Transform(device).GridUid; // Set current grid } private void HandleItemDeletion(EmancipationGridComponent emancipationComponent, EntityUid entityToDelete) @@ -154,9 +145,10 @@ private void OnActivate(Entity ent, ref ActivateInWor return; // Doesn't matter whether it is powered or not, if there are other Emancipation Grids, this turns-off NOW. - if (AreOtherEmancipationGridsPresent(ent)) + if (_nullExt.AreAkinEntitiesPresentOnGrid(ent)) { - Stop(ent); + _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), ent, PopupType.Medium); + this.Stop(EntityManager, ent); args.Handled = true; return; } @@ -164,7 +156,8 @@ private void OnActivate(Entity ent, ref ActivateInWor // if the device isn't powered, simply turn it on. if (!ent.Comp.IsPowered) { - Start(ent); + this.Start(EntityManager, ent); + ent.Comp.EmancipatedGrid = Transform(ent).GridUid; // Set current grid args.Handled = true; return; } @@ -174,7 +167,8 @@ private void OnActivate(Entity ent, ref ActivateInWor var actualYield = (int)ent.Comp.CurrentExpectedYield; // Can only have an integer of biomass, for comparisons. if (actualYield == 0) // If it has nothing, then clearly, we must turn this thing off. { - Stop(ent); + this.Stop(EntityManager, ent); + ent.Comp.EmancipatedGrid = null; args.Handled = true; return; } @@ -192,28 +186,6 @@ private void OnActivate(Entity ent, ref ActivateInWor args.Handled = true; } - /// - /// Boots the Emancipation grid and assigns the Grid ID to its component. - /// - /// - private void Start(Entity device) - { - device.Comp.EmancipatedGrid = Transform(device).GridUid; // Set current grid - if (TryComp(device, out var power)) - power.PowerDisabled = false; - } - - /// - /// Effectively shuts down the Emancipation Grid. - /// - /// - private void Stop(Entity device) - { - device.Comp.EmancipatedGrid = null; - if (TryComp(device, out var power)) - power.PowerDisabled = true; - } - #endregion [SuppressMessage("ReSharper", "RedundantJumpStatement")] From d5ff11138ecc3d33ef28d350486b533356b73eb6 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:25:16 -0600 Subject: [PATCH 10/20] Added Additional functionality to StaticPowerSystem.cs, and Changed IsPowered() to check if Power is disabled. Note: May cause unexpected behaviour elsewhere. --- .../Power/EntitySystems/StaticPowerSystem.cs | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/Content.Server/Power/EntitySystems/StaticPowerSystem.cs b/Content.Server/Power/EntitySystems/StaticPowerSystem.cs index 61a23e501df..a496f61ac44 100644 --- a/Content.Server/Power/EntitySystems/StaticPowerSystem.cs +++ b/Content.Server/Power/EntitySystems/StaticPowerSystem.cs @@ -6,11 +6,59 @@ public static class StaticPowerSystem { // Using this makes the call shorter. // ReSharper disable once UnusedParameter.Global - public static bool IsPowered(this EntitySystem system, EntityUid uid, IEntityManager entManager, ApcPowerReceiverComponent? receiver = null) + public static bool IsPowered(this EntitySystem system, + EntityUid uid, + IEntityManager entManager, + ApcPowerReceiverComponent? receiver = null) { if (receiver == null && !entManager.TryGetComponent(uid, out receiver)) return true; + if (receiver.PowerDisabled) + return false; + return receiver.Powered; } + + public static void Toggle(this EntitySystem system, IEntityManager entManager, Entity ent) where T : IComponent + { + if (IsPowered(system, ent.Owner, entManager)) + Stop(system, entManager, ent); + else + Start(system, entManager, ent); + } + + public static void Stop(this EntitySystem system, IEntityManager entManager, Entity ent) where T : IComponent + { + if (entManager.TryGetComponent(ent, out var receiver)) + Stop(system, entManager, ent, receiver); + } + + /// + /// Forces power to be disabled. + /// + public static void Stop( + this EntitySystem system, + IEntityManager entManager, + EntityUid uid, + ApcPowerReceiverComponent? receiver = null) + { + if (receiver == null && !entManager.TryGetComponent(uid, out receiver)) + return; + receiver.PowerDisabled = true; + } + + /// + /// Lifts the enforced power disable, if there is any. + /// + public static void Start( + this EntitySystem system, + IEntityManager entManager, + EntityUid uid, + ApcPowerReceiverComponent? receiver = null) + { + if (receiver == null && !entManager.TryGetComponent(uid, out receiver)) + return; + receiver.PowerDisabled = false; + } } From 27c23010fdaac5226052fdc040f8868d2193e4de Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:25:37 -0600 Subject: [PATCH 11/20] Added Color assignation methods to IFFComponent.cs --- .../Shuttles/Components/IFFComponent.cs | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Shuttles/Components/IFFComponent.cs b/Content.Shared/Shuttles/Components/IFFComponent.cs index d88cdfa92e5..5dc98924534 100644 --- a/Content.Shared/Shuttles/Components/IFFComponent.cs +++ b/Content.Shared/Shuttles/Components/IFFComponent.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared._Null.Systems; using Content.Shared.Shuttles.Systems; using Robust.Shared.GameStates; @@ -7,7 +9,8 @@ namespace Content.Shared.Shuttles.Components; /// Handles what a grid should look like on radar. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -[Access(typeof(SharedShuttleSystem))] +[Access(typeof(SharedShuttleSystem), typeof(SharedClaimantStakeSystem))] +[SuppressMessage("ReSharper", "InconsistentNaming")] public sealed partial class IFFComponent : Component { public static readonly Color SelfColor = Color.MediumSpringGreen; @@ -26,9 +29,27 @@ public sealed partial class IFFComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public Color Color = IFFColor; + [SuppressMessage("ReSharper", "InconsistentNaming")] + public void SetColor(string HEX) + { + // Add # to ensure the hex conversion works. + if (!HEX.StartsWith('#')) + { + HEX = '#' + HEX; + } + // Accounts for alpha values. Alpha is maximized. + if (HEX.Length <= 7) + { + HEX += "FF"; + } + var color = Color.FromHex(HEX); + SetColor(color); + } + public void SetColor(Color color) => Color = color; + // Frontier: POI IFF protection /// - /// Whether or not this entity's IFF can be changed. + /// Whether this entity's IFF can be changed. /// [ViewVariables(VVAccess.ReadWrite), DataField(serverOnly: true)] public bool ReadOnly; @@ -36,6 +57,7 @@ public sealed partial class IFFComponent : Component } [Flags] +[SuppressMessage("ReSharper", "InconsistentNaming")] public enum IFFFlags : byte { None = 0, From f92b6b23e9d0802880e445db7afb6d8acde622f3 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:34:53 -0600 Subject: [PATCH 12/20] Added Shutdown Locale to claimant_stake.ftl Note: The locale for Powering-Down is unused but left inside just in case. --- Resources/Locale/en-US/_Null/machines/claimant_stake.ftl | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl index 6d3539717ec..29e12f5daa5 100644 --- a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl +++ b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl @@ -2,6 +2,7 @@ claimant-stake-default-user = nobody identifiable claimant-stake-alone-popup = Only one claimant stake allowed per hull! claimant-stake-invalid-wreck-popup = This is not a valid wreckage! claimant-stake-no-power-popup = There is not enough power available! +claimant-stake-shutdown-popup = Claimant stake shutting down! claimant-stake-claim-success = Wreck successfully claimed! claimant-stake-disabling-popup = Claimant stake shutting down! claimant-stake-repeat-claim-popup = This wreckage already has been claimed! From d2d016f932f7b2e4aa8c5d166df9f98e7db4cf89 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:44:09 -0600 Subject: [PATCH 13/20] Fixed NullExtensionSystem.cs to actually check for Grids --- Content.Server/_Null/Systems/NullExtensionSystem.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Content.Server/_Null/Systems/NullExtensionSystem.cs b/Content.Server/_Null/Systems/NullExtensionSystem.cs index 8b56872d02a..21f54d549dd 100644 --- a/Content.Server/_Null/Systems/NullExtensionSystem.cs +++ b/Content.Server/_Null/Systems/NullExtensionSystem.cs @@ -1,6 +1,8 @@ using System.Diagnostics.CodeAnalysis; using Robust.Shared.Prototypes; +#pragma warning disable CS0618 // Type or member is obsolete + namespace Content.Server._Null.Systems; [SuppressMessage("ReSharper", "InconsistentNaming")] @@ -10,6 +12,7 @@ public interface INullExtensionSystem EntProtoId? GetProtoID(EntityUid ent); } +[SuppressMessage("Usage", "RA0030:Consider using the non-generic variant of this method")] public sealed partial class NullExtensionSystem : EntitySystem, INullExtensionSystem { public override void Initialize() @@ -17,6 +20,7 @@ public override void Initialize() base.Initialize(); IoCManager.Register(); } + public bool AreAkinEntitiesPresentOnGrid(Entity ent) where T : IComponent { var query = AllEntityQuery(); @@ -29,6 +33,9 @@ public bool AreAkinEntitiesPresentOnGrid(Entity ent) where T : IComponent if (other.GetType() != ent.Comp.GetType()) continue; + if (Transform(other.Owner).GridUid != Transform(ent.Owner).GridUid) + continue; + return true; } @@ -37,9 +44,6 @@ public bool AreAkinEntitiesPresentOnGrid(Entity ent) where T : IComponent public EntProtoId? GetProtoID(EntityUid ent) { - if (!TryComp(ent, out var metaData)) - return null; - - return metaData.EntityPrototype?.ID; + return !TryComp(ent, out var metaData) ? null : metaData.EntityPrototype?.ID; } } From 2af70f662792e8281fa761bc74097e9f1c2354c1 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 00:56:49 -0600 Subject: [PATCH 14/20] Updated StaticPowerSystem.cs --- Content.Server/Power/EntitySystems/StaticPowerSystem.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Content.Server/Power/EntitySystems/StaticPowerSystem.cs b/Content.Server/Power/EntitySystems/StaticPowerSystem.cs index a496f61ac44..1b2df972aa0 100644 --- a/Content.Server/Power/EntitySystems/StaticPowerSystem.cs +++ b/Content.Server/Power/EntitySystems/StaticPowerSystem.cs @@ -14,10 +14,7 @@ public static bool IsPowered(this EntitySystem system, if (receiver == null && !entManager.TryGetComponent(uid, out receiver)) return true; - if (receiver.PowerDisabled) - return false; - - return receiver.Powered; + return !receiver.PowerDisabled && receiver.Powered; } public static void Toggle(this EntitySystem system, IEntityManager entManager, Entity ent) where T : IComponent From e1b45cc5f8d8ae13466e846e2192337c98967d3a Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:02:44 -0600 Subject: [PATCH 15/20] Added Claimant Stake Prototype & Flatpack Note: Flatpack is in the Flatpack vendor. --- .../Inventories/flatpackvend.yml | 3 +- .../Entities/Objects/Devices/flatpack.yml | 12 +++- .../Structures/Machines/claimant_stake.yml | 56 +++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 Resources/Prototypes/_Null/Entities/Structures/Machines/claimant_stake.yml diff --git a/Resources/Prototypes/_NF/Catalog/VendingMachines/Inventories/flatpackvend.yml b/Resources/Prototypes/_NF/Catalog/VendingMachines/Inventories/flatpackvend.yml index 0cdec47c938..747d02f327f 100644 --- a/Resources/Prototypes/_NF/Catalog/VendingMachines/Inventories/flatpackvend.yml +++ b/Resources/Prototypes/_NF/Catalog/VendingMachines/Inventories/flatpackvend.yml @@ -31,4 +31,5 @@ AirlockGlassShuttleFlatpack: 4294967295 # Infinite ComputerWithdrawBankATMFlatpack: 4294967295 # Infinite TelevisionFlatpack: 4294967295 # Infinite - GaslockFlatpack: 4 # Null Sector - This is a brand new implementation that has some pushback. This is finite to give the benefit of the doubt, but may be made infinite later if it proves to be a fair addition. + GaslockFlatpack: 4 # Null Sector - This is a new implementation with some pushback. This is finite to give the benefit of the doubt, but may be made infinite later if it proves to be a fair addition. + ClaimantStakeFlatpack: 3 # Null Sector diff --git a/Resources/Prototypes/_Null/Entities/Objects/Devices/flatpack.yml b/Resources/Prototypes/_Null/Entities/Objects/Devices/flatpack.yml index ac0b8660be8..61d412fa0f4 100644 --- a/Resources/Prototypes/_Null/Entities/Objects/Devices/flatpack.yml +++ b/Resources/Prototypes/_Null/Entities/Objects/Devices/flatpack.yml @@ -24,4 +24,14 @@ entity: Gaslock - type: Sprite layers: - - state: command_airlock_directional \ No newline at end of file + - state: command_airlock_directional + +# Claimant Stake Flatpack +- type: entity + parent: BaseFlatpack + id: ClaimantStakeFlatpack + name: claimant stake flatpack + description: A flatpack used for constructing a wreckage claimant stake. + components: + - type: Flatpack + entity: ClaimantStake \ No newline at end of file diff --git a/Resources/Prototypes/_Null/Entities/Structures/Machines/claimant_stake.yml b/Resources/Prototypes/_Null/Entities/Structures/Machines/claimant_stake.yml new file mode 100644 index 00000000000..df3617d8ecc --- /dev/null +++ b/Resources/Prototypes/_Null/Entities/Structures/Machines/claimant_stake.yml @@ -0,0 +1,56 @@ +- type: entity + id: ClaimantStake + parent: BaseMachinePowered + name: wreckage claimant stake + description: An efficient piece of technology that allows one to claim a wreckage for their own. + placement: + mode: SnapgridCenter + components: + - type: Transform + anchored: true + - type: Physics + bodyType: Static + - type: Sprite + sprite: _Null/Structures/Machines/claimant_stake.rsi + snapCardinals: true + layers: + - state: base + - state: on + map: ["enum.PowerDeviceVisualLayers.Powered"] + visible: false + shader: unshaded + - type: ApcPowerReceiver + powerLoad: 2000 + - type: PointLight + enabled: false + color: "#88B0D1" # orange + radius: 2.0 + energy: 2 + - type: LitOnPowered + - type: ExtensionCableReceiver + - type: Appearance + - type: AmbientOnPowered + - type: AmbientSound + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/anomaly_generator.ogg + - type: GenericVisualizer + visuals: + enum.PowerDeviceVisuals.Powered: + enum.PowerDeviceVisualLayers.Powered: + True: { visible: true } + False: { visible: false } + # - type: Machine + # board: EmancipationGridCircuitboard + # - type: Construction + # graph: Machine + # node: machine + # containers: + # - machine_board + # - machine_parts + # - type: ContainerContainer + # containers: + # machine_board: !type:Container + # machine_parts: !type:Container + - type: ClaimantStake From ea88c0b7d587df410ee68132dfd537595c3c7a92 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:47:08 -0600 Subject: [PATCH 16/20] Updated claimant_stake.ftl --- Resources/Locale/en-US/_Null/machines/claimant_stake.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl index 29e12f5daa5..72c7deb3c5c 100644 --- a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl +++ b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl @@ -4,7 +4,7 @@ claimant-stake-invalid-wreck-popup = This is not a valid wreckage! claimant-stake-no-power-popup = There is not enough power available! claimant-stake-shutdown-popup = Claimant stake shutting down! claimant-stake-claim-success = Wreck successfully claimed! -claimant-stake-disabling-popup = Claimant stake shutting down! +claimant-stake-prompt-user-popup = Activate claimant stake to enable it! claimant-stake-repeat-claim-popup = This wreckage already has been claimed! claimant-stake-grid-claiming = Only one claimant stake allowed per hull! claimant-stake-grid-claimant-examine = This hull has {$claimant -> From 804849b65a68fd6897e01a27cbf6a4dfe59f5409 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:47:32 -0600 Subject: [PATCH 17/20] Refactored Null Extension System --- Content.Server/_Null/Systems/EmancipationGridSystem.cs | 6 +++--- Content.Server/_Null/Systems/NullExtensionSystem.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Content.Server/_Null/Systems/EmancipationGridSystem.cs b/Content.Server/_Null/Systems/EmancipationGridSystem.cs index 99e2b8de5cb..88dd3c70aaa 100644 --- a/Content.Server/_Null/Systems/EmancipationGridSystem.cs +++ b/Content.Server/_Null/Systems/EmancipationGridSystem.cs @@ -80,7 +80,7 @@ private void OnComponentInit(Entity device, ref Compo new SoundPathSpecifier(EmancipationGridComponent.DefaultOutputSound, EmancipationGridComponent.DefaultAudioParameters); - if (_nullExt.AreAkinEntitiesPresentOnGrid(device)) + if (_nullExt.SimilarEntitiesArePresentOnGrid(device)) { _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), device, PopupType.Medium); this.Stop(EntityManager, device); @@ -95,7 +95,7 @@ private void OnComponentInit(Entity device, ref Compo private void HandlePowerChange(Entity device, ref PowerChangedEvent args) { device.Comp.IsPowered = this.IsPowered(device.Owner, EntityManager); - if (_nullExt.AreAkinEntitiesPresentOnGrid(device) || !device.Comp.IsPowered) + if (_nullExt.SimilarEntitiesArePresentOnGrid(device) || !device.Comp.IsPowered) { _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), device, PopupType.Medium); this.Stop(EntityManager, device); @@ -145,7 +145,7 @@ private void OnActivate(Entity ent, ref ActivateInWor return; // Doesn't matter whether it is powered or not, if there are other Emancipation Grids, this turns-off NOW. - if (_nullExt.AreAkinEntitiesPresentOnGrid(ent)) + if (_nullExt.SimilarEntitiesArePresentOnGrid(ent)) { _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), ent, PopupType.Medium); this.Stop(EntityManager, ent); diff --git a/Content.Server/_Null/Systems/NullExtensionSystem.cs b/Content.Server/_Null/Systems/NullExtensionSystem.cs index 21f54d549dd..5d268fe27db 100644 --- a/Content.Server/_Null/Systems/NullExtensionSystem.cs +++ b/Content.Server/_Null/Systems/NullExtensionSystem.cs @@ -8,7 +8,7 @@ namespace Content.Server._Null.Systems; [SuppressMessage("ReSharper", "InconsistentNaming")] public interface INullExtensionSystem { - bool AreAkinEntitiesPresentOnGrid(Entity ent) where T : IComponent; + bool SimilarEntitiesArePresentOnGrid(Entity ent) where T : IComponent; EntProtoId? GetProtoID(EntityUid ent); } @@ -21,7 +21,7 @@ public override void Initialize() IoCManager.Register(); } - public bool AreAkinEntitiesPresentOnGrid(Entity ent) where T : IComponent + public bool SimilarEntitiesArePresentOnGrid(Entity ent) where T : IComponent { var query = AllEntityQuery(); From f7b4c4811e7ea8f5332e1c24fd9da0e475637b16 Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:58:30 -0600 Subject: [PATCH 18/20] Added Yet more Claimant Stake Locale --- Resources/Locale/en-US/_Null/machines/claimant_stake.ftl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl index 72c7deb3c5c..a5d1546d425 100644 --- a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl +++ b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl @@ -3,8 +3,10 @@ claimant-stake-alone-popup = Only one claimant stake allowed per hull! claimant-stake-invalid-wreck-popup = This is not a valid wreckage! claimant-stake-no-power-popup = There is not enough power available! claimant-stake-shutdown-popup = Claimant stake shutting down! +claimant-stake-claim-pending = Claiming wreck... claimant-stake-claim-success = Wreck successfully claimed! claimant-stake-prompt-user-popup = Activate claimant stake to enable it! +claimant-stake-activated-popup = Claimant stake activated! claimant-stake-repeat-claim-popup = This wreckage already has been claimed! claimant-stake-grid-claiming = Only one claimant stake allowed per hull! claimant-stake-grid-claimant-examine = This hull has {$claimant -> From 2b43405737db4ab50906ad609e1dab60fa79d8fc Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:01:10 -0600 Subject: [PATCH 19/20] Final Localization Adjustment --- Resources/Locale/en-US/_Null/machines/claimant_stake.ftl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl index a5d1546d425..ab934c1d695 100644 --- a/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl +++ b/Resources/Locale/en-US/_Null/machines/claimant_stake.ftl @@ -2,10 +2,13 @@ claimant-stake-default-user = nobody identifiable claimant-stake-alone-popup = Only one claimant stake allowed per hull! claimant-stake-invalid-wreck-popup = This is not a valid wreckage! claimant-stake-no-power-popup = There is not enough power available! +claimant-stake-unanchored-popup = Claimant stake must be anchored! claimant-stake-shutdown-popup = Claimant stake shutting down! +claimant-stake-claim-erased-popup = Claim successfully erased! claimant-stake-claim-pending = Claiming wreck... -claimant-stake-claim-success = Wreck successfully claimed! +claimant-stake-claim-success = Wreck successfully claimed! Enjoy your new home! claimant-stake-prompt-user-popup = Activate claimant stake to enable it! +claimant-stake-too-complex-popup = You can't seem to interact with this! claimant-stake-activated-popup = Claimant stake activated! claimant-stake-repeat-claim-popup = This wreckage already has been claimed! claimant-stake-grid-claiming = Only one claimant stake allowed per hull! @@ -14,4 +17,4 @@ claimant-stake-grid-claimant-examine = This hull has {$claimant -> [true] [color=green]a claimant[/color] }. claimant-stake-examined-user = It belongs to {$user}! -claimant-wreckage-name = Wreckage Claim of {$user} \ No newline at end of file +claimant-wreckage-name = Wreckage Claim of {$user} From 15904aea60e73c06b35b93835b79ad1d3d40a48e Mon Sep 17 00:00:00 2001 From: LukeZurg22 <11780262+LukeZurg22@users.noreply.github.com> Date: Wed, 26 Nov 2025 21:06:27 -0600 Subject: [PATCH 20/20] Claimant Stake Systems & Components In Addition to Other System Changes --- .../Power/EntitySystems/StaticPowerSystem.cs | 21 +- .../Components/ClaimantStakeComponent.cs | 63 +++ .../_Null/Systems/ClaimantStakeSystem.cs | 406 ++++++++++++++++++ .../_Null/Systems/EmancipationGridSystem.cs | 14 +- .../_Null/Systems/NullExtensionSystem.cs | 13 +- 5 files changed, 489 insertions(+), 28 deletions(-) create mode 100644 Content.Server/_Null/Components/ClaimantStakeComponent.cs create mode 100644 Content.Server/_Null/Systems/ClaimantStakeSystem.cs diff --git a/Content.Server/Power/EntitySystems/StaticPowerSystem.cs b/Content.Server/Power/EntitySystems/StaticPowerSystem.cs index 1b2df972aa0..8a3e8119545 100644 --- a/Content.Server/Power/EntitySystems/StaticPowerSystem.cs +++ b/Content.Server/Power/EntitySystems/StaticPowerSystem.cs @@ -14,31 +14,28 @@ public static bool IsPowered(this EntitySystem system, if (receiver == null && !entManager.TryGetComponent(uid, out receiver)) return true; - return !receiver.PowerDisabled && receiver.Powered; + return receiver.Powered; } - public static void Toggle(this EntitySystem system, IEntityManager entManager, Entity ent) where T : IComponent + public static void Toggle(this EntitySystem system, IEntityManager entManager, Entity ent) + where T : IComponent { if (IsPowered(system, ent.Owner, entManager)) - Stop(system, entManager, ent); + Disable(system, entManager, ent); else - Start(system, entManager, ent); + Enable(system, entManager, ent); } - public static void Stop(this EntitySystem system, IEntityManager entManager, Entity ent) where T : IComponent + public static void Disable(this EntitySystem system, IEntityManager entManager, Entity ent) where T : IComponent { if (entManager.TryGetComponent(ent, out var receiver)) - Stop(system, entManager, ent, receiver); + Disable(entManager, ent, receiver); } /// /// Forces power to be disabled. /// - public static void Stop( - this EntitySystem system, - IEntityManager entManager, - EntityUid uid, - ApcPowerReceiverComponent? receiver = null) + public static void Disable(IEntityManager entManager, EntityUid uid, ApcPowerReceiverComponent? receiver = null) { if (receiver == null && !entManager.TryGetComponent(uid, out receiver)) return; @@ -48,7 +45,7 @@ public static void Stop( /// /// Lifts the enforced power disable, if there is any. /// - public static void Start( + public static void Enable( this EntitySystem system, IEntityManager entManager, EntityUid uid, diff --git a/Content.Server/_Null/Components/ClaimantStakeComponent.cs b/Content.Server/_Null/Components/ClaimantStakeComponent.cs new file mode 100644 index 00000000000..973af11d8c9 --- /dev/null +++ b/Content.Server/_Null/Components/ClaimantStakeComponent.cs @@ -0,0 +1,63 @@ +using Content.Server._Null.Systems; +using Content.Shared.Shuttles.Components; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Server._Null.Components; + +[RegisterComponent, Access(typeof(ClaimantStakeSystem))] +public sealed partial class ClaimantStakeComponent : Component +{ + [ViewVariables(VVAccess.ReadOnly)] + public EntityUid? PlayerOwner { get; set; } + + [ViewVariables(VVAccess.ReadOnly)] + public EntityUid? ClaimedGrid { get; set; } + + [DataField("useSound")] + public SoundSpecifier? SoundUse { get; set; } + + [DataField("awakeSound")] + public SoundSpecifier? SoundAwake { get; set; } + + [DataField("sleepSound")] + public SoundSpecifier? ShutdownSound { get; set; } + + public ClaimantStakeStatus StakeStatus = ClaimantStakeStatus.Offline; + + public const float AwaitBeepSoundTime = 5f; + public float RemainingTime = AwaitBeepSoundTime; + + /// + /// Check to see if this device is receiving power. Can be toggled, but it would be unwise due to possible + /// unpredicted behaviour. + /// + public bool Enabled = false; + + #region Data Retention + + public IFFFlags OldFlags = IFFFlags.None; + public string? OldColorHex = null; + public string? OldGridName = null; + public string? NewColorHex { get; set; } + + #endregion + + [ValidatePrototypeId] + public const string WreckagePrototype = "NFBaseWreckDebris"; + + public const string WreckageRemovalQueueName = "SpaceDebris"; + public const string DefaultUseSound = "/Audio/Effects/metal_crunch.ogg"; + public const string DefaultAwakeSound = "/Audio/Effects/RingtoneNotes/asharp.ogg"; + public const string DefaultShutdownSound = "/Audio/Effects/sparks4.ogg"; + + public static AudioParams DefaultAudioParameters = AudioParams.Default.WithVolume(-3); +} + +public enum ClaimantStakeStatus : byte +{ + Offline, + Warming, + Online, + Declaiming, +} diff --git a/Content.Server/_Null/Systems/ClaimantStakeSystem.cs b/Content.Server/_Null/Systems/ClaimantStakeSystem.cs new file mode 100644 index 00000000000..22983bd1c97 --- /dev/null +++ b/Content.Server/_Null/Systems/ClaimantStakeSystem.cs @@ -0,0 +1,406 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using Content.Server._Null.Components; +using Content.Server.Explosion.Components; +using Content.Server.Power.Components; +using Content.Server.Power.EntitySystems; +using Content.Server.Worldgen.Components.Debris; +using Content.Server.Worldgen.Components.GC; +using Content.Shared._Null.Systems; +using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.Popups; +using Content.Shared.Power; +using Content.Shared.Shuttles.Components; +using Content.Shared.Tiles; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; + +namespace Content.Server._Null.Systems; + +public sealed class ClaimantStakeSystem : SharedClaimantStakeSystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly MetaDataSystem _meta = default!; + [Dependency] private readonly INullExtensionSystem _nullExt = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnComponentStartup); + SubscribeLocalEvent(OnComponentShutdown); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnActivate); + } + + private void OnComponentStartup(Entity ent, ref ComponentStartup args) + { + // Set initial power state + if (TryComp(ent, out var powerReceiver)) + ent.Comp.Enabled = powerReceiver.Powered; + } + + private void OnActivate(Entity ent, ref ActivateInWorldEvent args) + { + if (args.Handled || !args.Complex) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-too-complex-popup"), ent, PopupType.MediumCaution); + return; + } + + // If the thing is already enabled, and it has no claimed grid, and it CAN be enabled, + // it is reasonable to assume the user wants to claim the grid rather than turn it off. + var canEnable = CanEnable(ent, allowEnabledComp: true); + if (ent.Comp.Enabled && ent.Comp.ClaimedGrid == null && canEnable) + { + BeginHandleClaim(ent, args.User); + args.Handled = true; + return; + } + // From here, it is assuming the user is aiming to toggle the machine rather than establish a claim. + + // Resetting grid claimant in advance. + ent.Comp.ClaimedGrid = null; + ent.Comp.Enabled ^= true; + + if (!ent.Comp.Enabled) + { + Disable(ent); + } + else if (CanEnable(ent)) + { + Enable(ent); + } + args.Handled = true; + } + + public bool CanEnable(Entity ent, bool allowEnabledComp = false) + { + if (IsInvalidWreck(ent, out _, out _)) + return false; + + // If it is actively turning off or on, it is best not to interrupt the Claimant Stake. + // The user can only interact with it only when it's fully on or offline. + if (ent.Comp.StakeStatus is not (ClaimantStakeStatus.Offline or ClaimantStakeStatus.Online)) + return false; + + if (allowEnabledComp == false && ent.Comp.Enabled == false) + return false; + + // If it is receiving power then it should be fine. + if (TryComp(ent, out var powerReceiver)) + { + // Disabling power forcefully happens to screw with power reception. If it's not disabled then it goes to- + // -show that it must be receiving power in order to work. If it is already disabled then who cares, go- + // -enable it. + if (!powerReceiver.PowerDisabled && powerReceiver.Load > powerReceiver.PowerReceived) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-no-power-popup"), ent, PopupType.SmallCaution); + return false; + } + } + + return true; + } + + private void OnPowerChanged(Entity ent, ref PowerChangedEvent args) + { + ent.Comp.Enabled = args.Powered; + if (IsInvalidWreck(ent, out _, out _)) + { + ent.Comp.Enabled = false; + return; + } + + // Device was DISABLED + if (ent.Comp.Enabled == false && ent.Comp.ClaimedGrid != null) + { + BeginHandleClaim(ent, claimant: null); + } + } + + [SuppressMessage("ReSharper", "RedundantJumpStatement")] + private bool IsInvalidWreck(Entity ent, + out EntityUid? gridId, + out MetaDataComponent? outMeta) + { + gridId = null; + outMeta = null; + + // Claimant Stake Anchoring + var xform = Transform(ent); + if (!xform.Anchored) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-unanchored-popup"), ent, PopupType.SmallCaution); + return false; + } + + // If there's no transform or no grid, it's invalid for claiming. + if (!TryComp(ent, out TransformComponent? transform) || transform.GridUid == null) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-invalid-wreck-popup"), ent, PopupType.MediumCaution); + return true; + } + + var currentGrid = transform.GridUid; + + // Require metadata and parents to be present. + if (!TryComp(currentGrid, out MetaDataComponent? metaData) || metaData.EntityPrototype?.Parents == null) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-invalid-wreck-popup"), ent, PopupType.MediumCaution); + return true; + } + + // Ensure the grid's prototype parents include the allowed wreck prototype. + var parents = metaData.EntityPrototype.Parents.ToList(); + if (!parents.Any(prototype => prototype.Equals(ClaimantStakeComponent.WreckagePrototype))) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-invalid-wreck-popup"), ent, PopupType.MediumCaution); + return true; + } + + // If it's already the same claimed grid, it's invalid to claim. + if (currentGrid.Value == ent.Comp.ClaimedGrid) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-repeat-claim-popup"), ent, PopupType.MediumCaution); + return true; + } + + // If there are other similar stakes on the same grid, declaring is forbidden. + if (_nullExt.SimilarEntitiesArePresentOnGrid(ent)) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-alone-popup"), ent, PopupType.Medium); + return true; + } + + gridId = currentGrid; + outMeta = metaData; + return false; // Not invalid + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var claimantStake)) + { + switch (claimantStake.StakeStatus) + { + case ClaimantStakeStatus.Warming: + TickTimer(frameTime, (uid, claimantStake)); + break; + case ClaimantStakeStatus.Declaiming: + TickTimer(frameTime, (uid, claimantStake), isDeclaiming: true); + break; + } + } + } + + // Keep this accepting Entity to match other methods that use that wrapper. + private void TickTimer(float frameTime, Entity ent, bool isDeclaiming = false) + { + ent.Comp.RemainingTime -= frameTime; + if (ent.Comp.RemainingTime >= 0) + return; + + var stakeLocation = Transform(ent).Coordinates; + ent.Comp.RemainingTime = ClaimantStakeComponent.AwaitBeepSoundTime; + string popup; + SoundSpecifier? playSound; + if (isDeclaiming) + { + ent.Comp.StakeStatus = ClaimantStakeStatus.Offline; + DeclaimGrid(ent); + + popup = Loc.GetString("claimant-stake-claim-erased-popup"); + playSound = ent.Comp.ShutdownSound; + this.Disable(EntityManager, ent); + } + else + { + ent.Comp.StakeStatus = ClaimantStakeStatus.Online; + ClaimGrid(ent); + + popup = Loc.GetString("claimant-stake-claim-success"); + playSound = ent.Comp.SoundAwake; + } + + _audio.PlayPvs(playSound, stakeLocation); + _popup.PopupEntity(popup, ent, PopupType.Large); + } + + private void OnComponentInit(Entity ent, ref ComponentInit args) + { + EnsureComp(ent); + + ent.Comp.SoundUse ??= + new SoundPathSpecifier( + ClaimantStakeComponent.DefaultUseSound, + ClaimantStakeComponent.DefaultAudioParameters); + ent.Comp.SoundAwake ??= + new SoundPathSpecifier( + ClaimantStakeComponent.DefaultAwakeSound, + ClaimantStakeComponent.DefaultAudioParameters); + ent.Comp.ShutdownSound ??= + new SoundPathSpecifier( + ClaimantStakeComponent.DefaultShutdownSound, + ClaimantStakeComponent.DefaultAudioParameters); + + _popup.PopupEntity(Loc.GetString("claimant-stake-prompt-user-popup"), ent); +//Dirty(ent); + } + + private void Enable(Entity ent) + { + this.Enable(EntityManager, ent); + _popup.PopupEntity(Loc.GetString("claimant-stake-activated-popup"), ent, PopupType.Medium); + } + + private void Disable(Entity ent) + { + BeginHandleClaim(ent, claimant: null); + } + + #region Grid Claiming & Declaiming + + /// + /// Handles the INITIALIZATION of claiming and un-claiming of wreckage. Ticker handles the rest. + /// + /// The claimant stake itself. + /// If the claimant is null, this acts as a toggle to declaim a wreck. + private void BeginHandleClaim(Entity ent, EntityUid? claimant = null) + { + // If it is actively turning off or on, it is best not to interrupt the Claimant Stake. + // The user can only interact with it only when it's fully on or offline. + if (ent.Comp.StakeStatus is not (ClaimantStakeStatus.Offline or ClaimantStakeStatus.Online)) + return; + + var stakeLocation = Transform(ent.Owner).Coordinates; + ent.Comp.PlayerOwner = claimant; + SoundSpecifier? playSound; + if (claimant != null) + { + _popup.PopupEntity(Loc.GetString("claimant-stake-claim-pending"), ent, PopupType.Medium); + playSound = ent.Comp.SoundUse; + ent.Comp.StakeStatus = ClaimantStakeStatus.Warming; + } + else + { + _popup.PopupEntity(Loc.GetString("claimant-stake-shutdown-popup"), ent, PopupType.Medium); + playSound = ent.Comp.ShutdownSound; + ent.Comp.StakeStatus = ClaimantStakeStatus.Declaiming; + } + + _audio.PlayPvs(playSound, stakeLocation); + } + + private void ClaimGrid(Entity ent, bool removeWreckComponents = true) + { + if (IsInvalidWreck(ent, out var currentGrid, out var metaData)) + { + this.Disable(EntityManager, ent); + BeginHandleClaim(ent, claimant: null); // Declaim the grid. + return; + } + + ent.Comp.ClaimedGrid = currentGrid; + + #region Changes to Grid + + ent.Comp.OldGridName = metaData?.EntityName; + _meta.SetEntityName(currentGrid!.Value, + Loc.GetString("claimant-wreckage-name", ("user", GetClaimantName(ent.Comp.PlayerOwner))), + metaData); + + if (TryComp(currentGrid, out IFFComponent? iff)) + { + ent.Comp.OldColorHex = iff.Color.ToHexNoAlpha(); + ent.Comp.OldFlags = iff.Flags; + if (!string.IsNullOrEmpty(ent.Comp.NewColorHex)) + { + iff.SetColor(ent.Comp.NewColorHex); + } + + iff.Flags = IFFFlags.IsPlayerShuttle; + } + + if (removeWreckComponents) + { + RemComp(currentGrid.Value); + RemComp(currentGrid.Value); + RemComp(currentGrid.Value); + } + + #endregion + } + + private void DeclaimGrid(Entity ent, bool ensureWreckComponents = true) + { + ent.Comp.PlayerOwner = null; + ent.Comp.ClaimedGrid = null; + ent.Comp.StakeStatus = ClaimantStakeStatus.Offline; + + if (!TryComp(ent, out TransformComponent? transform) || transform.GridUid == null) + return; + + var currentGrid = transform.GridUid; + if (TryComp(currentGrid, out MetaDataComponent? metaData) && metaData.EntityPrototype?.Parents != null) + { + if (!string.IsNullOrEmpty(ent.Comp.OldGridName)) + { + _meta.SetEntityName(currentGrid.Value, ent.Comp.OldGridName, metaData); + } + } + + if (TryComp(currentGrid, out IFFComponent? iff)) + { + if (!string.IsNullOrEmpty(ent.Comp.OldColorHex)) + { + iff.SetColor(ent.Comp.OldColorHex); + } + + iff.Flags = ent.Comp.OldFlags; + } + + if (!ensureWreckComponents) + return; + + EnsureComp(currentGrid.Value); + EnsureComp(currentGrid.Value); + var comp = EnsureComp(currentGrid.Value); + comp.Queue = ClaimantStakeComponent.WreckageRemovalQueueName; + } + + #endregion + + private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) + { + DeclaimGrid(ent, false); + } + + private string GetClaimantName(EntityUid? uid) + { + var isValidId = uid != null; + return !isValidId + ? Loc.GetString("claimant-stake-default-user") + : TryComp(uid, out var meta) + ? meta.EntityName + : Loc.GetString("claimant-stake-default-user"); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + StringBuilder stringBuilder = new(); + var hasPlayerClaimant = ent.Comp.PlayerOwner != null; + var playerName = GetClaimantName(ent.Comp.PlayerOwner); + stringBuilder.Append(Loc.GetString("claimant-stake-grid-claimant-examine", ("claimant", hasPlayerClaimant))); + stringBuilder.Append(' '); + stringBuilder.AppendLine(Loc.GetString("claimant-stake-examined-user", ("user", playerName))); + args.PushMarkup(stringBuilder.ToString()); + } +} diff --git a/Content.Server/_Null/Systems/EmancipationGridSystem.cs b/Content.Server/_Null/Systems/EmancipationGridSystem.cs index 88dd3c70aaa..a8391520439 100644 --- a/Content.Server/_Null/Systems/EmancipationGridSystem.cs +++ b/Content.Server/_Null/Systems/EmancipationGridSystem.cs @@ -83,12 +83,12 @@ private void OnComponentInit(Entity device, ref Compo if (_nullExt.SimilarEntitiesArePresentOnGrid(device)) { _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), device, PopupType.Medium); - this.Stop(EntityManager, device); + this.Disable(EntityManager, device); device.Comp.EmancipatedGrid = null; return; } - this.Start(EntityManager, device); + this.Enable(EntityManager, device); device.Comp.EmancipatedGrid = Transform(device).GridUid; // Set current grid } @@ -98,12 +98,12 @@ private void HandlePowerChange(Entity device, ref Pow if (_nullExt.SimilarEntitiesArePresentOnGrid(device) || !device.Comp.IsPowered) { _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), device, PopupType.Medium); - this.Stop(EntityManager, device); + this.Disable(EntityManager, device); device.Comp.EmancipatedGrid = null; return; } - this.Start(EntityManager, device); + this.Enable(EntityManager, device); device.Comp.EmancipatedGrid = Transform(device).GridUid; // Set current grid } @@ -148,7 +148,7 @@ private void OnActivate(Entity ent, ref ActivateInWor if (_nullExt.SimilarEntitiesArePresentOnGrid(ent)) { _popup.PopupEntity(Loc.GetString("emancipation-grid-alone-popup"), ent, PopupType.Medium); - this.Stop(EntityManager, ent); + this.Disable(EntityManager, ent); args.Handled = true; return; } @@ -156,7 +156,7 @@ private void OnActivate(Entity ent, ref ActivateInWor // if the device isn't powered, simply turn it on. if (!ent.Comp.IsPowered) { - this.Start(EntityManager, ent); + this.Enable(EntityManager, ent); ent.Comp.EmancipatedGrid = Transform(ent).GridUid; // Set current grid args.Handled = true; return; @@ -167,7 +167,7 @@ private void OnActivate(Entity ent, ref ActivateInWor var actualYield = (int)ent.Comp.CurrentExpectedYield; // Can only have an integer of biomass, for comparisons. if (actualYield == 0) // If it has nothing, then clearly, we must turn this thing off. { - this.Stop(EntityManager, ent); + this.Disable(EntityManager, ent); ent.Comp.EmancipatedGrid = null; args.Handled = true; return; diff --git a/Content.Server/_Null/Systems/NullExtensionSystem.cs b/Content.Server/_Null/Systems/NullExtensionSystem.cs index 5d268fe27db..cfe9be2382d 100644 --- a/Content.Server/_Null/Systems/NullExtensionSystem.cs +++ b/Content.Server/_Null/Systems/NullExtensionSystem.cs @@ -25,18 +25,13 @@ public bool SimilarEntitiesArePresentOnGrid(Entity ent) where T : ICompone { var query = AllEntityQuery(); - while (query.MoveNext(out var other)) + while (query.MoveNext(out var otherComponent)) { - if (other.Equals(ent.Comp)) + if (otherComponent.Owner.Equals(ent.Owner)) // If they are the same entity, skip. continue; - if (other.GetType() != ent.Comp.GetType()) - continue; - - if (Transform(other.Owner).GridUid != Transform(ent.Owner).GridUid) - continue; - - return true; + if (Transform(otherComponent.Owner).GridUid == Transform(ent.Owner).GridUid) + return true; // Both entities differ, and both share the same grid. } return false;