]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add paradox clone to admin antag control (#36105)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Thu, 27 Mar 2025 17:04:25 +0000 (18:04 +0100)
committerGitHub <noreply@github.com>
Thu, 27 Mar 2025 17:04:25 +0000 (10:04 -0700)
* make paradox clone receive the original's objectives

* antag control verb

* rename verb

Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs
Content.Server/GameTicking/Rules/Components/ParadoxCloneRuleComponent.cs
Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs
Resources/Locale/en-US/administration/antag.ftl
Resources/Textures/Interface/Misc/job_icons.rsi/ParadoxClone.png [new file with mode: 0644]
Resources/Textures/Interface/Misc/job_icons.rsi/meta.json

index b8a05c3e8f39601da9fe822705801fb527212337..79c5c86fe1f4fabcccb7e79b03a770565f678f67 100644 (file)
@@ -1,9 +1,11 @@
 using Content.Server.Administration.Commands;
 using Content.Server.Antag;
+using Content.Server.GameTicking;
 using Content.Server.GameTicking.Rules.Components;
 using Content.Server.Zombies;
 using Content.Shared.Administration;
 using Content.Shared.Database;
+using Content.Shared.Humanoid;
 using Content.Shared.Mind.Components;
 using Content.Shared.Roles;
 using Content.Shared.Verbs;
@@ -17,6 +19,7 @@ public sealed partial class AdminVerbSystem
 {
     [Dependency] private readonly AntagSelectionSystem _antag = default!;
     [Dependency] private readonly ZombieSystem _zombie = default!;
+    [Dependency] private readonly GameTicker _gameTicker = default!;
 
     [ValidatePrototypeId<EntityPrototype>]
     private const string DefaultTraitorRule = "Traitor";
@@ -36,6 +39,8 @@ public sealed partial class AdminVerbSystem
     [ValidatePrototypeId<StartingGearPrototype>]
     private const string PirateGearId = "PirateGear";
 
+    private readonly EntProtoId _paradoxCloneRuleId = "ParadoxCloneSpawn";
+
     // All antag verbs have names so invokeverb works.
     private void AddAntagVerbs(GetVerbsEvent<Verb> args)
     {
@@ -157,5 +162,29 @@ public sealed partial class AdminVerbSystem
             Message = string.Join(": ", thiefName, Loc.GetString("admin-verb-make-thief")),
         };
         args.Verbs.Add(thief);
+
+        var paradoxCloneName = Loc.GetString("admin-verb-text-make-paradox-clone");
+        Verb paradox = new()
+        {
+            Text = paradoxCloneName,
+            Category = VerbCategory.Antag,
+            Icon = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/job_icons.rsi"), "ParadoxClone"),
+            Act = () =>
+            {
+                var ruleEnt = _gameTicker.AddGameRule(_paradoxCloneRuleId);
+
+                if (!TryComp<ParadoxCloneRuleComponent>(ruleEnt, out var paradoxCloneRuleComp))
+                    return;
+
+                paradoxCloneRuleComp.OriginalBody = args.Target; // override the target player
+
+                _gameTicker.StartGameRule(ruleEnt);
+            },
+            Impact = LogImpact.High,
+            Message = string.Join(": ", paradoxCloneName, Loc.GetString("admin-verb-make-paradox-clone")),
+        };
+
+        if (HasComp<HumanoidAppearanceComponent>(args.Target)) // only humanoids can be cloned
+            args.Verbs.Add(paradox);
     }
 }
index 143659748a4a5f15a5aadef1c5fb63dc9c215cd6..e28a0bc35fd55078e803abfcb7eacc4547bd23cd 100644 (file)
@@ -22,12 +22,19 @@ public sealed partial class ParadoxCloneRuleComponent : Component
     [DataField]
     public EntProtoId GibProto = "MobParadoxTimed";
 
+    /// <summary>
+    ///     Entity of the original player.
+    ///     Gets randomly chosen from all alive players if not specified.
+    /// </summary>
+    [DataField]
+    public EntityUid? OriginalBody;
+
     /// <summary>
     ///     Mind entity of the original player.
     ///     Gets assigned when cloning.
     /// </summary>
     [DataField]
-    public EntityUid? Original;
+    public EntityUid? OriginalMind;
 
     /// <summary>
     ///     Whitelist for Objectives to be copied to the clone.
index 8a5cab85b864e5769754b01c91bdde63e8320b1f..ab8864caaa7249188b760c5e1b155624d975f4ea 100644 (file)
@@ -47,28 +47,42 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComp
         if (args.Session?.AttachedEntity is not { } spawner)
             return;
 
-        // get possible targets
-        var allHumans = _mind.GetAliveHumans();
-
-        // we already checked when starting the gamerule, but someone might have died since then.
-        if (allHumans.Count == 0)
+        if (ent.Comp.OriginalBody != null) // target was overridden, for example by admin antag control
         {
-            Log.Warning("Could not find any alive players to create a paradox clone from!");
-            return;
+            if (Deleted(ent.Comp.OriginalBody.Value) || !_mind.TryGetMind(ent.Comp.OriginalBody.Value, out var originalMindId, out var _))
+            {
+                Log.Warning("Could not find mind of target player to paradox clone!");
+                return;
+            }
+            ent.Comp.OriginalMind = originalMindId;
         }
+        else
+        {
+            // get possible targets
+            var allAliveHumanoids = _mind.GetAliveHumans();
+
+            // we already checked when starting the gamerule, but someone might have died since then.
+            if (allAliveHumanoids.Count == 0)
+            {
+                Log.Warning("Could not find any alive players to create a paradox clone from!");
+                return;
+            }
 
-        // pick a random player
-        var playerToClone = _random.Pick(allHumans);
-        var bodyToClone = playerToClone.Comp.OwnedEntity;
+            // pick a random player
+            var randomHumanoidMind = _random.Pick(allAliveHumanoids);
+            ent.Comp.OriginalMind = randomHumanoidMind;
+            ent.Comp.OriginalBody = randomHumanoidMind.Comp.OwnedEntity;
+
+        }
 
-        if (bodyToClone == null || !_cloning.TryCloning(bodyToClone.Value, _transform.GetMapCoordinates(spawner), ent.Comp.Settings, out var clone))
+        if (ent.Comp.OriginalBody == null || !_cloning.TryCloning(ent.Comp.OriginalBody.Value, _transform.GetMapCoordinates(spawner), ent.Comp.Settings, out var clone))
         {
-            Log.Error($"Unable to make a paradox clone of entity {ToPrettyString(bodyToClone)}");
+            Log.Error($"Unable to make a paradox clone of entity {ToPrettyString(ent.Comp.OriginalBody)}");
             return;
         }
 
         var targetComp = EnsureComp<TargetOverrideComponent>(clone.Value);
-        targetComp.Target = playerToClone.Owner; // set the kill target
+        targetComp.Target = ent.Comp.OriginalMind; // set the kill target
 
         var gibComp = EnsureComp<GibOnRoundEndComponent>(clone.Value);
         gibComp.SpawnProto = ent.Comp.GibProto;
@@ -78,17 +92,16 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComp
         _sensor.SetAllSensors(clone.Value, SuitSensorMode.SensorOff);
 
         args.Entity = clone;
-        ent.Comp.Original = playerToClone.Owner;
     }
 
     private void AfterAntagEntitySelected(Entity<ParadoxCloneRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
     {
-        if (ent.Comp.Original == null)
+        if (ent.Comp.OriginalMind == null)
             return;
 
         if (!_mind.TryGetMind(args.EntityUid, out var cloneMindId, out var cloneMindComp))
             return;
 
-        _mind.CopyObjectives(ent.Comp.Original.Value, (cloneMindId, cloneMindComp), ent.Comp.ObjectiveWhitelist, ent.Comp.ObjectiveBlacklist);
+        _mind.CopyObjectives(ent.Comp.OriginalMind.Value, (cloneMindId, cloneMindComp), ent.Comp.ObjectiveWhitelist, ent.Comp.ObjectiveBlacklist);
     }
 }
index ef4ad98c7c3314bdb9aab309bbec7791e446347b..1433cc1dc481f7960f407eefca1005027179d00c 100644 (file)
@@ -6,6 +6,7 @@ admin-verb-make-nuclear-operative = Make target into a lone Nuclear Operative.
 admin-verb-make-pirate = Make the target into a pirate. Note this doesn't configure the game rule.
 admin-verb-make-head-rev = Make the target into a Head Revolutionary.
 admin-verb-make-thief = Make the target into a thief.
+admin-verb-make-paradox-clone = Create a Paradox Clone ghost role of the target.
 
 admin-verb-text-make-traitor = Make Traitor
 admin-verb-text-make-initial-infected = Make Initial Infected
@@ -14,5 +15,6 @@ admin-verb-text-make-nuclear-operative = Make Nuclear Operative
 admin-verb-text-make-pirate = Make Pirate
 admin-verb-text-make-head-rev = Make Head Rev
 admin-verb-text-make-thief = Make Thief
+admin-verb-text-make-paradox-clone = Create Paradox Clone
 
 admin-overlay-antag-classic = ANTAG
diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/ParadoxClone.png b/Resources/Textures/Interface/Misc/job_icons.rsi/ParadoxClone.png
new file mode 100644 (file)
index 0000000..6d1add3
Binary files /dev/null and b/Resources/Textures/Interface/Misc/job_icons.rsi/ParadoxClone.png differ
index e2a26933fc88c2bc8d7fdf857d46315820e1cc9f..f6cc4d99e1c2fe949e530ff97b85c11fb9d22536 100644 (file)
@@ -1,7 +1,7 @@
 {
     "version": 1,
     "license": "CC-BY-SA-3.0",
-    "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord) | Mindshield icon taken from https://github.com/tgstation/tgstation/blob/ce6beb8a4d61235d9a597a7126c407160ed674ea/icons/mob/huds/hud.dmi | Admin recolored from MedicalIntern by TsjipTsjip | StationAi resprite to 8x8 size by lunarcomets | Service Worker resprite by anno_midi (Discord) and spanky-spanky (Github) | service icons darkened by frobnic8 (Discord and Github)",
+    "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord) | Mindshield icon taken from https://github.com/tgstation/tgstation/blob/ce6beb8a4d61235d9a597a7126c407160ed674ea/icons/mob/huds/hud.dmi | Admin recolored from MedicalIntern by TsjipTsjip | StationAi resprite to 8x8 size by lunarcomets | Service Worker resprite by anno_midi (Discord) and spanky-spanky (Github) | service icons darkened by frobnic8 (Discord and Github) | paradoxClone taken from tg station at commit https://github.com/tgstation/tgstation/commit/d0db1ff267557017ae7bde68e6490e70cbb6e42f and modifed by slarticodefast (Github)",
 
     "size": {
         "x": 8,
         },
         {
             "name": "Admin"
+        },
+        {
+            "name": "ParadoxClone"
         }
     ]
 }