From 820cf9d2761fbf81cc562c8df93c95db2cac6f11 Mon Sep 17 00:00:00 2001 From: Typheus <10276357+Typheus@users.noreply.github.com> Date: Tue, 9 Jun 2026 23:38:24 -0400 Subject: [PATCH 1/2] Updated AI remote and positronic brain system with regards to AI control. AI now required a borg to have a special brain inserted which refuses ghost roles and allows for long range AI shunting. Added new AIShuntReceiver brain and graphic for borgs. --- .../Silicons/Borgs/AiRemoteControlSystem.cs | 34 ++++++++++- .../Silicons/Borgs/PositronicJumpSystem.cs | 49 +++++++++++++--- .../Components/AIShuntReceiverComponent.cs | 15 +++++ .../Recipes/Lathes/Packs/robotics.yml | 1 + .../Prototypes/Recipes/Lathes/robotics.yml | 14 +++++ .../Objects/Specific/Robotics/mmi.yml | 54 ++++++++++++++++++ .../Robotics/mmi.rsi/aishuntreceiver.png | Bin 0 -> 389 bytes .../mmi.rsi/aishuntreceiver_overlay.png | Bin 0 -> 303 bytes .../Specific/Robotics/mmi.rsi/meta.json | 23 ++++++++ 9 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 Content.Shared/_HL/Silicons/Components/AIShuntReceiverComponent.cs create mode 100644 Resources/Prototypes/_HL/Entities/Objects/Specific/Robotics/mmi.yml create mode 100644 Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/aishuntreceiver.png create mode 100644 Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/aishuntreceiver_overlay.png create mode 100644 Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json diff --git a/Content.Server/_CorvaxNext/Silicons/Borgs/AiRemoteControlSystem.cs b/Content.Server/_CorvaxNext/Silicons/Borgs/AiRemoteControlSystem.cs index 9c28cbc3cff..c1b9d8e7207 100644 --- a/Content.Server/_CorvaxNext/Silicons/Borgs/AiRemoteControlSystem.cs +++ b/Content.Server/_CorvaxNext/Silicons/Borgs/AiRemoteControlSystem.cs @@ -19,6 +19,9 @@ using Content.Shared.Body.Part; using Robust.Shared.Containers; using Robust.Shared.Player; +using Content.Shared._HL.Silicons.Components; +using Content.Shared.Interaction.Events; +using Content.Server.Popups; namespace Content.Server._CorvaxNext.Silicons.Borgs; @@ -35,6 +38,7 @@ public sealed class AiRemoteControlSystem : SharedAiRemoteControlSystem [Dependency] private readonly SharedTransformSystem _xformSystem = default!; [Dependency] private readonly PositronicJumpSystem _positronicJumpSystem = default!; //Hardlight: Incorporates positronic jump system into transfer [Dependency] private readonly TransformSystem _transformSystem = default!; //Used to prevent AI from selecting borgs from list that aren't on same grid + [Dependency] private readonly PopupSystem _popupSystem = default!; public override void Initialize() { @@ -49,7 +53,26 @@ public override void Initialize() SubscribeLocalEvent(OnBrainInserted); SubscribeLocalEvent(OnBrainRemoved); + + SubscribeLocalEvent(OnShuntRadioUsed); //Hardlight: For setting the AIShuntRadioComponent grid + } + + //Hardlight: + /// + /// Sets the receiver's assigned grid so an AI on that grid can access it. + /// + /// + /// + private void OnShuntRadioUsed(Entity ent, ref UseInHandEvent args) + { + if (!TryComp(ent, out var radioComponent)) + return; + + radioComponent.AssignedGrid = _transformSystem.GetGrid(args.User); + + _popupSystem.PopupEntity("Radio linked to local grid", args.User, args.User); } + //Hardlight End private void OnBrainInserted(EntityUid uid, AiRemoteBrainComponent component, EntGotInsertedIntoContainerMessage args) { @@ -204,7 +227,15 @@ private void OnToggleRemoteDevicesScreen(EntityUid uid, StationAiHeldComponent c var targetEntity = GetEntity(GetNetEntity(queryUid)); var targetGrid = _transformSystem.GetGrid(targetEntity); - if (targetGrid == null || targetGrid != aiGrid) + //Checks to make sure the borg entity has the AIShuntReceiver component + if (!TryComp(targetEntity, out var chassis)) + continue; + + if (!TryComp(chassis.BrainContainer.ContainedEntity, out var radioComponent)) + continue; + + //Checks to make sure AIShuntReceiver is set to proper grid + if (radioComponent.AssignedGrid == null || radioComponent?.AssignedGrid != aiGrid) continue; //Only lists borgs that pass the valid candidate check @@ -212,6 +243,7 @@ private void OnToggleRemoteDevicesScreen(EntityUid uid, StationAiHeldComponent c if (!_positronicJumpSystem.IsTargetValidControlCandidate(uid, targetEntity)) continue; //Hardlight end + var data = new RemoteDevicesData { NetEntityUid = GetNetEntity(queryUid), diff --git a/Content.Server/_CorvaxNext/Silicons/Borgs/PositronicJumpSystem.cs b/Content.Server/_CorvaxNext/Silicons/Borgs/PositronicJumpSystem.cs index a443e1b1d38..d0cb8eeeccf 100644 --- a/Content.Server/_CorvaxNext/Silicons/Borgs/PositronicJumpSystem.cs +++ b/Content.Server/_CorvaxNext/Silicons/Borgs/PositronicJumpSystem.cs @@ -1,25 +1,28 @@ -using System.Linq; +using Content.Server._CorvaxNext.Silicons.Borgs.Components; +using Content.Server._Mono.SpaceArtillery.Components; +using Content.Server._NF.Roles.Systems; // VRS: move JobTrackingComponent across shunt (HL #1354) using Content.Server.Mech.Systems; using Content.Server.Silicons.Borgs; using Content.Server.Silicons.Laws; using Content.Server.SurveillanceCamera; -using Content.Server._CorvaxNext.Silicons.Borgs.Components; -using Content.Server._Mono.SpaceArtillery.Components; -using Content.Server._NF.Roles.Systems; // VRS: move JobTrackingComponent across shunt (HL #1354) +using Content.Shared._CorvaxNext.Silicons.Borgs; +using Content.Shared._HL.Silicons.Components; using Content.Shared._NF.Roles.Components; // VRS: move JobTrackingComponent across shunt (HL #1354) -using Content.Shared.Mech.Components; using Content.Shared.Actions; +using Content.Shared.Mech.Components; using Content.Shared.Mind; using Content.Shared.PAI; using Content.Shared.Popups; -using Content.Shared._CorvaxNext.Silicons.Borgs; +using Content.Shared.Roles.Jobs; using Content.Shared.Silicons.Borgs.Components; using Content.Shared.Silicons.Laws.Components; using Content.Shared.Silicons.StationAi; using Content.Shared.Turrets; using Content.Shared.Verbs; -using Robust.Shared.GameStates; +using Robust.Server.GameObjects; using Robust.Shared.Containers; +using Robust.Shared.GameStates; +using System.Linq; namespace Content.Server._CorvaxNext.Silicons.Borgs; @@ -33,6 +36,7 @@ public sealed class PositronicJumpSystem : EntitySystem [Dependency] private readonly MechSystem _mech = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly JobTrackingSystem _jobTracking = default!; // VRS: HL #1354 + [Dependency] private readonly TransformSystem _transformSystem = default!; private const string ReturnToAiAction = "ActionBackToAi"; @@ -118,6 +122,10 @@ private void OnBorgChassisGetVerbs(EntityUid uid, BorgChassisComponent component if (_mind.TryGetMind(uid, out _, out _)) return; + //Hardlight: If the user is an AI, then make sure target isn't invalid borg + if (HasComp(args.User) && !AITargetingValidBorg(args.User, uid)) + return; + //Hardlight End var user = args.User; var target = uid; @@ -329,6 +337,11 @@ public bool TryTakeControl(EntityUid user, EntityUid target) if (mind.OwnedEntity == target) return false; + //Hardlight: If user is AI make sure they're not taking over an invalid borg + if (HasComp(user) && !AITargetingValidBorg(user, target)) + return false; + //Hardlight end + if (mind.OwnedEntity != null) TransferReturnState(mind.OwnedEntity.Value, target); @@ -362,6 +375,28 @@ public bool IsTargetValidControlCandidate(EntityUid user, EntityUid target) return true; } + //Hardlight: + /// + /// Checks if the target is a borg and if so, make sure it has the proper receiver + /// installed to restrict AI takeovers. + /// + /// + /// + /// + public bool AITargetingValidBorg(EntityUid user, EntityUid target) + { + if (!TryComp(target, out var chassis)) + return true; + + if (!TryComp(chassis.BrainContainer.ContainedEntity, out var radioComponent)) + return false; + + if (radioComponent.AssignedGrid == null || radioComponent?.AssignedGrid != _transformSystem.GetGrid(user)) + return false; + + return true; + } + //Hardlight End public bool TryReturnControl(EntityUid target) { if (TryComp(target, out var remoteMechPilot)) diff --git a/Content.Shared/_HL/Silicons/Components/AIShuntReceiverComponent.cs b/Content.Shared/_HL/Silicons/Components/AIShuntReceiverComponent.cs new file mode 100644 index 00000000000..956162c6e02 --- /dev/null +++ b/Content.Shared/_HL/Silicons/Components/AIShuntReceiverComponent.cs @@ -0,0 +1,15 @@ +using Robust.Shared.GameStates; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Shared._HL.Silicons.Components +{ + [RegisterComponent, NetworkedComponent] + public sealed partial class AIShuntReceiverComponent : Component + { + [ViewVariables] + [DataField] + public EntityUid? AssignedGrid = null; + } +} diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/robotics.yml b/Resources/Prototypes/Recipes/Lathes/Packs/robotics.yml index 72261e21d76..937a9cf64f2 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/robotics.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/robotics.yml @@ -5,6 +5,7 @@ recipes: - MMI - PositronicBrain + - AIShuntReceiver # Hardlight - SciFlash - CyborgEndoskeleton - Quadborgendoskeleton diff --git a/Resources/Prototypes/Recipes/Lathes/robotics.yml b/Resources/Prototypes/Recipes/Lathes/robotics.yml index fe75756551e..7e78227c859 100644 --- a/Resources/Prototypes/Recipes/Lathes/robotics.yml +++ b/Resources/Prototypes/Recipes/Lathes/robotics.yml @@ -246,6 +246,20 @@ Silver: 100 Plasma: 1000 +# Hardlight +- type: latheRecipe + parent: BaseRoboticsRecipe + id: AIShuntReceiver + result: AIShuntReceiver + completetime: 3 + materials: + Steel: 500 + Plastic: 500 + Gold: 100 + Silver: 100 + Plasma: 1000 +# Hardlight End + # Modules - type: latheRecipe diff --git a/Resources/Prototypes/_HL/Entities/Objects/Specific/Robotics/mmi.yml b/Resources/Prototypes/_HL/Entities/Objects/Specific/Robotics/mmi.yml new file mode 100644 index 00000000000..caa159542ab --- /dev/null +++ b/Resources/Prototypes/_HL/Entities/Objects/Specific/Robotics/mmi.yml @@ -0,0 +1,54 @@ +- type: entity + parent: [ BaseItem, BaseSiliconBrainLanguages ] + id: AIShuntReceiver + name: AI Shunt Receiver + description: A long range receiver for an AI to shunt into. Works off-grid at any distance. Must be linked to AI's grid first. + components: + - type: Sprite + sprite: _HL/Objects/Specific/Robotics/mmi.rsi + layers: + - state: aishuntreceiver + - state: aishuntreceiver_overlay + - type: Input + context: human + - type: Organ + slotId: brain + - type: Brain + - type: BlockMovement + - type: Examiner + - type: BorgBrain + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - type: ActiveRadio + channels: + - Binary + - Common + - type: NameIdentifier + group: PositronicBrain + - type: DoAfter + - type: Actions + - type: TypingIndicator + proto: robot + - type: Speech + speechSounds: Pai + - type: Alerts + - type: MobState + allowedStates: + - Alive + - Dead + - type: Appearance + - type: Tag + tags: + - CannotSuicide + - type: GuideHelp + guides: + - Cyborgs + - Robotics + - type: ChangeVoiceInContainer + whitelist: + components: + - SecretStash + - type: RetroMonitorView # Mono - #3902 + - type: AIShuntReceiver diff --git a/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/aishuntreceiver.png b/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/aishuntreceiver.png new file mode 100644 index 0000000000000000000000000000000000000000..1539f14d517e453a1af7c75d6c22a54fc36c2213 GIT binary patch literal 389 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?3oArNM~bhqvgP*A$W zHKHUqKdq!Zu_%?nIW?!avREOwq%IRGo-?qr`tEqr%zj<6({zeREsLyzn&!z5uYMM{?bs2psCH|{#VocN zc3mpPgSeeTA-e`V8A@p^JPTzGss&-n~lMuzhpNgvkO+{WwfaLye9cKQ6qb z=Ha1Mp3>5{SNIu&WDRVb5>l)ctd}#aKC)${aG}(+sdM{7YhF#^>`Ae7@QoF9eRJjH zW}eg2&K-Z*u;PUan{jcn$i`^%{K?N7Ud&KgBh(_(z^fB$)Iaf!Nx_RFifq>0){`zX z_{h6<280AWslR`wlE?L>C(q2CkuK9)F01<|H@vL=`;Yf0UxQ^+V`Jk2$0x5ObNQiO emMCCnU}(H1c%eoA&01g(GI+ZBxvXYWa7k%OX1+p5etyou)dbOc7(1=Xpv^DYFl9MwnI=OqSZE|mUWZR($1WNrhDR5#ex<+7w=xOs_3~2P&)!F zn438H+41?ym#o&?{pRiG-R!+S{V_R cF%%sC!=P-k{$TT-MkbK3r>mdKI;Vst04@M*ssI20 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json b/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json new file mode 100644 index 00000000000..96b999ebdcc --- /dev/null +++ b/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json @@ -0,0 +1,23 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/cf45322c7ee16f9d7e43d5260daf24ceb77c1b25", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "aishuntreceiver" + }, + { + "name": "aishuntreceiver_overlay", + "delays": [ + [ + 0.8, + 0.8 + ] + ] + } + ] +} From 61c65cad0dea951f9a771f5323672635090c99cb Mon Sep 17 00:00:00 2001 From: Typheus <10276357+Typheus@users.noreply.github.com> Date: Wed, 10 Jun 2026 01:30:31 -0400 Subject: [PATCH 2/2] Updated the copyright for the meta used for the ai shunt receiver to give proper credit to the PAI face designer. --- .../Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json b/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json index 96b999ebdcc..11686c561df 100644 --- a/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json +++ b/Resources/Textures/_HL/Objects/Specific/Robotics/mmi.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/cf45322c7ee16f9d7e43d5260daf24ceb77c1b25", + "copyright": "PAI overlay sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/9ddb8cf084e292571d4e9c79745db25befbd82fe and edited by Typheus", "size": { "x": 32, "y": 32