]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
make paradox clone receive the original's objectives (#35829)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Sun, 16 Mar 2025 22:24:47 +0000 (23:24 +0100)
committerGitHub <noreply@github.com>
Sun, 16 Mar 2025 22:24:47 +0000 (15:24 -0700)
* make paradox clone receive the original's objectives

* remove redundant indexing

* Minor comment change

* fix ninja objectives

---------

Co-authored-by: beck-thompson <beck314159@hotmail.com>
Content.Server/GameTicking/Rules/Components/ParadoxCloneRuleComponent.cs
Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs
Content.Shared/Mind/SharedMindSystem.cs
Resources/Prototypes/Entities/Mobs/Player/clone.yml
Resources/Prototypes/GameRules/events.yml
Resources/Prototypes/Objectives/paradoxClone.yml
Resources/Prototypes/tags.yml

index a6ffb4e66993b371ceb8e4dcfccb4d2a8f72a020..d0d3c1e9db780cdc6596d428c7df5d9eab9782ca 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Cloning;
+using Content.Shared.Whitelist;
 using Robust.Shared.Prototypes;
 
 namespace Content.Server.GameTicking.Rules.Components;
@@ -20,4 +21,23 @@ public sealed partial class ParadoxCloneRuleComponent : Component
     /// </summary>
     [DataField]
     public EntProtoId GibProto = "MobParadoxTimed";
+
+    /// <summary>
+    ///     Mind entity of the original player.
+    ///     Gets assigned when cloning.
+    /// </summary>
+    [DataField]
+    public EntityUid? Original;
+
+    /// <summary>
+    ///     Whitelist for Objectives to be copied to the clone.
+    /// </summary>
+    [DataField]
+    public EntityWhitelist? ObjectiveWhitelist;
+
+    /// <summary>
+    ///     Blacklist for Objectives to be copied to the clone.
+    /// </summary>
+    [DataField]
+    public EntityWhitelist? ObjectiveBlacklist;
 }
index 80fad7d2ef379789b7db35306b8c010d8203edbf..076479727f76895b5522cbca76900477be4a1828 100644 (file)
@@ -13,7 +13,6 @@ namespace Content.Server.GameTicking.Rules;
 public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComponent>
 {
     [Dependency] private readonly SharedTransformSystem _transform = default!;
-    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
     [Dependency] private readonly SharedMindSystem _mind = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly CloningSystem _cloning = default!;
@@ -23,6 +22,7 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComp
         base.Initialize();
 
         SubscribeLocalEvent<ParadoxCloneRuleComponent, AntagSelectEntityEvent>(OnAntagSelectEntity);
+        SubscribeLocalEvent<ParadoxCloneRuleComponent, AfterAntagEntitySelectedEvent>(AfterAntagEntitySelected);
     }
 
     protected override void Started(EntityUid uid, ParadoxCloneRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
@@ -45,12 +45,6 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComp
         if (args.Session?.AttachedEntity is not { } spawner)
             return;
 
-        if (!_prototypeManager.TryIndex(ent.Comp.Settings, out var settings))
-        {
-            Log.Error($"Used invalid cloning settings {ent.Comp.Settings} for ParadoxCloneRule");
-            return;
-        }
-
         // get possible targets
         var allHumans = _mind.GetAliveHumans();
 
@@ -65,7 +59,7 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComp
         var playerToClone = _random.Pick(allHumans);
         var bodyToClone = playerToClone.Comp.OwnedEntity;
 
-        if (bodyToClone == null || !_cloning.TryCloning(bodyToClone.Value, _transform.GetMapCoordinates(spawner), settings, out var clone))
+        if (bodyToClone == null || !_cloning.TryCloning(bodyToClone.Value, _transform.GetMapCoordinates(spawner), ent.Comp.Settings, out var clone))
         {
             Log.Error($"Unable to make a paradox clone of entity {ToPrettyString(bodyToClone)}");
             return;
@@ -79,5 +73,17 @@ public sealed class ParadoxCloneRuleSystem : GameRuleSystem<ParadoxCloneRuleComp
         gibComp.PreventGibbingObjectives = new() { "ParadoxCloneKillObjective" }; // don't gib them if they killed the original.
 
         args.Entity = clone;
+        ent.Comp.Original = playerToClone.Owner;
+    }
+
+    private void AfterAntagEntitySelected(Entity<ParadoxCloneRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
+    {
+        if (ent.Comp.Original == 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);
     }
 }
index 0f5f9172cc692c569051bcc13d64ecff092cf6c8..fc1be3f1ba0328427fa06a565184319b833383be 100644 (file)
@@ -11,6 +11,7 @@ using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
 using Content.Shared.Objectives.Systems;
 using Content.Shared.Players;
+using Content.Shared.Whitelist;
 using Robust.Shared.Map;
 using Robust.Shared.Network;
 using Robust.Shared.Player;
@@ -26,6 +27,7 @@ public abstract class SharedMindSystem : EntitySystem
     [Dependency] private readonly SharedObjectivesSystem _objectives = default!;
     [Dependency] private readonly SharedPlayerSystem _player = default!;
     [Dependency] private readonly MetaDataSystem _metadata = default!;
+    [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
 
     [ViewVariables]
     protected readonly Dictionary<NetUserId, EntityUid> UserMinds = new();
@@ -364,6 +366,16 @@ public abstract class SharedMindSystem : EntitySystem
         var title = Name(objective);
         _adminLogger.Add(LogType.Mind, LogImpact.Low, $"Objective {objective} ({title}) removed from the mind of {MindOwnerLoggingString(mind)}");
         mind.Objectives.Remove(objective);
+
+        // garbage collection - only delete the objective entity if no mind uses it anymore
+        // This comes up for stuff like paradox clones where the objectives share the same entity
+        var mindQuery = new AllEntityQueryEnumerator<MindComponent>();
+        while (mindQuery.MoveNext(out _, out var queryComp))
+        {
+            if (queryComp.Objectives.Contains(objective))
+                return true;
+        }
+
         Del(objective);
         return true;
     }
@@ -395,6 +407,33 @@ public abstract class SharedMindSystem : EntitySystem
         return false;
     }
 
+    /// <summary>
+    /// Copies objectives from one mind to another, so that they are shared between two players.
+    /// </summary>
+    /// <remarks>
+    /// Only copies the reference to the objective entity, not the entity itself.
+    /// This relies on the fact that objectives are never changed after spawning them.
+    /// If someone ever changes that, they will have to address this.
+    /// </remarks>
+    /// <param name="source"> mind entity of the player to copy from </param>
+    /// <param name="target"> mind entity of the player to copy to </param>
+    /// <param name="except"> whitelist for objectives that should be copied </param>
+    /// <param name="except"> blacklist for objectives that should not be copied </param>
+    public void CopyObjectives(Entity<MindComponent?> source, Entity<MindComponent?> target, EntityWhitelist? whitelist = null, EntityWhitelist? blacklist = null)
+    {
+        if (!Resolve(source, ref source.Comp) || !Resolve(target, ref target.Comp))
+            return;
+
+        foreach (var objective in source.Comp.Objectives)
+        {
+            if (target.Comp.Objectives.Contains(objective))
+                continue; // target already has this objective
+
+            if (_whitelist.CheckBoth(objective, blacklist, whitelist))
+                AddObjective(target, target.Comp, objective);
+        }
+    }
+
     /// <summary>
     /// Tries to find an objective that has the same prototype as the argument.
     /// </summary>
index cdc273f667650f0bf98661957fee9b189fabcfb5..8208c30095bb5af5d61f74fd29fd7d7736e86c28 100644 (file)
@@ -27,6 +27,7 @@
   - Clumsy
   - MindShield
   - MimePowers
+  - SpaceNinja
   # accents
   - Accentless
   - BackwardsAccent
index 444c5e5d09a971c1ae3851c08e74ecdd91ceb311..f942cafe6c15e3951cc74252662856723e087573 100644 (file)
     reoccurrenceDelay: 20
     minimumPlayers: 15
   - type: ParadoxCloneRule
+    objectiveBlacklist:
+      tags:
+      - ParadoxCloneObjectiveBlacklist
   - type: AntagObjectives
     objectives:
     - ParadoxCloneKillObjective
index 80aa4f15df37053c1878297a723b9d1a538ac926..073c014af0be24c57bb8bc3cba39a067f72ea91b 100644 (file)
@@ -11,6 +11,9 @@
     roles:
       mindRoles:
       - ParadoxCloneRole
+  - type: Tag
+    tags:
+    - ParadoxCloneObjectiveBlacklist # don't copy the objectives from other clones
 
 - type: entity
   parent: [BaseParadoxCloneObjective, BaseLivingObjective]
index 179651a1132a32fe16185d0e6c78e0216f2ff7dc..e3dcb9b11b04f7612ca139c0e298611ab6c052be 100644 (file)
 - type: Tag
   id: Packet
 
+- type: Tag
+  id: ParadoxCloneObjectiveBlacklist # objective entities with this tag don't get copied to paradox clones
+
 - type: Tag
   id: Paper