]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Implant whitelist/blacklisting (#20678)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Fri, 27 Oct 2023 02:34:02 +0000 (03:34 +0100)
committerGitHub <noreply@github.com>
Fri, 27 Oct 2023 02:34:02 +0000 (22:34 -0400)
* add whitelist and blacklist to implant and implanter components

* handle whitelist and blacklist in systems

* move hardcoded whitelist/blacklist to base implanter + add admeme implanter

* give implants sensible whitelists

* cleaner CheckTarget and fix

* remove unused imports

* network lists

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
Content.Server/Implants/ImplanterSystem.cs
Content.Shared/Implants/Components/ImplanterComponent.cs
Content.Shared/Implants/Components/SubdermalImplantComponent.cs
Content.Shared/Implants/SharedImplanterSystem.cs
Resources/Locale/en-US/implant/implant.ftl
Resources/Prototypes/Entities/Objects/Misc/implanters.yml
Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml

index f3072769e4dc9c720c3640aee6f5438b98221697..0d46241f41463680a38cc1d5f1fb11e9e628edc7 100644 (file)
@@ -1,11 +1,9 @@
-using Content.Server.Guardian;
 using Content.Server.Popups;
 using Content.Shared.DoAfter;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Implants;
 using Content.Shared.Implants.Components;
 using Content.Shared.Interaction;
-using Content.Shared.Mobs.Components;
 using Content.Shared.Popups;
 using Robust.Shared.Containers;
 
@@ -33,28 +31,39 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
         if (args.Target == null || !args.CanReach || args.Handled)
             return;
 
-        //Simplemobs and regular mobs should be injectable, but only regular mobs have mind.
-        //So just don't implant/draw anything that isn't living or is a guardian
-        //TODO: Rework a bit when surgery is in to work with implant cases
-        if (!HasComp<MobStateComponent>(args.Target.Value) || HasComp<GuardianComponent>(args.Target.Value))
+        var target = args.Target.Value;
+        if (!CheckTarget(target, component.Whitelist, component.Blacklist))
             return;
 
         //TODO: Rework when surgery is in for implant cases
         if (component.CurrentMode == ImplanterToggleMode.Draw && !component.ImplantOnly)
         {
-            TryDraw(component, args.User, args.Target.Value, uid);
+            TryDraw(component, args.User, target, uid);
         }
         else
         {
-            if (!CanImplant(args.User, args.Target.Value, uid, component, out _, out _))
+            if (!CanImplant(args.User, target, uid, component, out var implant, out _))
+            {
+                // no popup if implant doesn't exist
+                if (implant == null)
+                    return;
+
+                // show popup to the user saying implant failed
+                var name = Identity.Name(target, EntityManager, args.User);
+                var msg = Loc.GetString("implanter-component-implant-failed", ("implant", implant), ("target", name));
+                _popup.PopupEntity(msg, target, args.User);
+                // prevent further interaction since popup was shown
+                args.Handled = true;
                 return;
+            }
 
             //Implant self instantly, otherwise try to inject the target.
-            if (args.User == args.Target)
-                Implant(args.User, args.Target.Value, uid, component);
+            if (args.User == target)
+                Implant(target, target, uid, component);
             else
-                TryImplant(component, args.User, args.Target.Value, uid);
+                TryImplant(component, args.User, target, uid);
         }
+
         args.Handled = true;
     }
 
index 85891826f901c2ac48b7efee7818ea58520e4f62..32a3636163346e90cc05d2969427d58a2556e296 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Whitelist;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
@@ -15,6 +16,19 @@ public sealed partial class ImplanterComponent : Component
     public const string ImplanterSlotId = "implanter_slot";
     public const string ImplantSlotId = "implant";
 
+    /// <summary>
+    /// Whitelist to check entities against before implanting.
+    /// Implants get their own whitelist which is checked afterwards.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public EntityWhitelist? Whitelist;
+
+    /// <summary>
+    /// Blacklist to check entities against before implanting.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public EntityWhitelist? Blacklist;
+
     /// <summary>
     /// Used for implanters that start with specific implants
     /// </summary>
index b2fdb14e4c5176899e97697ee37ef91b6ce81d78..5edc26ead32c472495b3436bbea8861035951d54 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Actions;
+using Content.Shared.Whitelist;
 using Robust.Shared.GameStates;
 using Robust.Shared.Prototypes;
 
@@ -34,6 +35,20 @@ public sealed partial class SubdermalImplantComponent : Component
     [ViewVariables(VVAccess.ReadWrite)]
     [DataField("permanent"), AutoNetworkedField]
     public bool Permanent = false;
+
+    /// <summary>
+    /// Target whitelist for this implant specifically.
+    /// Only checked if the implanter allows implanting on the target to begin with.
+    /// </summary>
+    [DataField]
+    public EntityWhitelist? Whitelist;
+
+    /// <summary>
+    /// Target blacklist for this implant specifically.
+    /// Only checked if the implanter allows implanting on the target to begin with.
+    /// </summary>
+    [DataField]
+    public EntityWhitelist? Blacklist;
 }
 
 /// <summary>
index 1cf9f446630da465efdcf6fb6aeee16d7341755d..404e6da5089f59fc9e111afc1d872b226f6fbfd9 100644 (file)
@@ -6,6 +6,7 @@ using Content.Shared.Examine;
 using Content.Shared.IdentityManagement;
 using Content.Shared.Implants.Components;
 using Content.Shared.Popups;
+using Content.Shared.Whitelist;
 using Robust.Shared.Containers;
 using Robust.Shared.Serialization;
 
@@ -85,11 +86,23 @@ public abstract class SharedImplanterSystem : EntitySystem
         if (!TryComp(implant, out implantComp))
             return false;
 
+        if (!CheckTarget(target, component.Whitelist, component.Blacklist) ||
+            !CheckTarget(target, implantComp.Whitelist, implantComp.Blacklist))
+        {
+            return false;
+        }
+
         var ev = new AddImplantAttemptEvent(user, target, implant.Value, implanter);
         RaiseLocalEvent(target, ev);
         return !ev.Cancelled;
     }
 
+    protected bool CheckTarget(EntityUid target, EntityWhitelist? whitelist, EntityWhitelist? blacklist)
+    {
+        return whitelist?.IsValid(target, EntityManager) != false &&
+            blacklist?.IsValid(target, EntityManager) != true;
+    }
+
     //Draw the implant out of the target
     //TODO: Rework when surgery is in so implant cases can be a thing
     public void Draw(EntityUid implanter, EntityUid user, EntityUid target, ImplanterComponent component)
index bdc82c291d87a42641a765abd062832d1baee0de..22db4460affda87aedb64019a456c162ba805728 100644 (file)
@@ -1,6 +1,7 @@
 ## Implanter Attempt Messages
 
 implanter-component-implanting-target = {$user} is trying to implant you with something!
+implanter-component-implant-failed = The {$implant} cannot be given to {$target}!
 implanter-draw-failed-permanent = The {$implant} in {$target} is fused with them and cannot be removed!
 implanter-draw-failed = You tried to remove an implant but found nothing.
 
index 41966ab93f82e481b6dc6d0421c6c2a631d756b7..0ca029a12096b6cf46ea13ae0ca1757bece1cb83 100644 (file)
       containers:
         implanter_slot: !type:ContainerSlot { }
     - type: Implanter
+      whitelist:
+        components:
+        - Body # no chair microbomb
+      blacklist:
+        components:
+        - Guardian # no holoparasite macrobomb wombo combo
       currentMode: Draw
       implanterSlot:
         name: Implant
       tags:
         - Trash
 
+- type: entity
+  parent: Implanter
+  id: ImplanterAdmeme
+  suffix: Admeme
+  components:
+  - type: Implanter
+    # go wild with sentient chairs with macrobombs
+    whitelist: null
+    blacklist: null
+
 - type: entity
   id: BaseImplantOnlyImplanter
   parent: Implanter
index 6632010a79b89658838a92bd75fef184153f4793..bc7a3a38ad307a406ddf19b81e83c686b9f8a857 100644 (file)
@@ -21,6 +21,9 @@
   noSpawn: true
   components:
     - type: SubdermalImplant
+      whitelist:
+        components:
+        - MobState # admeme implanting a chair with trombone implant needs to give the chair mobstate so it can die first
     - type: TriggerOnMobstateChange
       mobState:
       - Dead
@@ -81,6 +84,9 @@
   noSpawn: true
   components:
     - type: SubdermalImplant
+      whitelist:
+        components:
+        - MobState # admeme implanting a chair with tracking implant needs to give the chair mobstate so it can die first
     - type: SuitSensor
       randomMode: false
       controlsLocked: true
   components:
     - type: SubdermalImplant
       implantAction: ActionOpenStorageImplant
+      whitelist:
+        components:
+        - Hands # no use giving a mouse a storage implant, but a monkey is another story...
     - type: Item
       size: 9999
     - type: Storage
   components:
     - type: SubdermalImplant
       implantAction: ActionActivateFreedomImplant
+      whitelist:
+        components:
+        - Cuffable # useless if you cant be cuffed
 
 - type: entity
   parent: BaseSubdermalImplant
   components:
   - type: SubdermalImplant
     implantAction: ActionOpenUplinkImplant
+    whitelist:
+      components:
+      - Hands # prevent mouse buying grenade penguin since its not telepathic
   - type: Store
     preset: StorePresetUplink
     balance:
   components:
     - type: SubdermalImplant
       implantAction: ActionActivateDnaScramblerImplant
+      whitelist:
+        components:
+        - HumanoidAppearance # syndies cant turn hamlet into a human
 
 #Nuclear Operative/Special Exclusive implants
 
   components:
     - type: SubdermalImplant
       permanent: true
+      whitelist:
+        components:
+        - MobState # admeme implanting a chair with rattle implant needs to give the chair mobstate so it can die first
     - type: TriggerOnMobstateChange
       mobState:
       - Critical