using Content.Shared.Antag; using Content.Shared.Cloning; using Content.Shared.GameTicking.Components; using Content.Shared.GameTicking.Rules.Components; using Content.Shared.Gibbing.Components; using Content.Shared.Medical.SuitSensors; using Content.Shared.Mind; using Content.Shared.Objectives.Components; using Content.Shared.Objectives.Systems; using Robust.Shared.Random; namespace Content.Shared.GameTicking.Rules; public sealed partial class ParadoxCloneRuleSystem : GameRuleSystem { [Dependency] private IRobustRandom _random = default!; [Dependency] private SharedCloningSystem _cloning = default!; [Dependency] private SharedMindSystem _mind = default!; [Dependency] private SharedSuitSensorSystem _sensor = default!; [Dependency] private AliveHumanoidTargetSystem _target = default!; protected override void Started(Entity rule, ref GameRuleStartedEvent args) { base.Started(rule, ref args); // check if we got enough potential cloning targets, otherwise cancel the gamerule so that the ghost role does not show up var allHumans = _target.GetMinds(); if (allHumans.Count == 0) { Log.Info("Could not find any alive players to create a paradox clone from! Ending gamerule."); ForceEndSelf((rule, rule.Comp2)); } } // we have to do the spawning here so we can transfer the mind to the correct entity and can assign the objectives correctly [SubscribeLocalEvent] private void OnAntagSelectEntity(Entity ent, ref AntagSelectEntityEvent args) { if (ent.Comp.OriginalBody != null) // target was overridden, for example by admin antag control { 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 = _target.GetMinds(); // 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 randomHumanoidMind = _random.Pick(allAliveHumanoids); ent.Comp.OriginalMind = randomHumanoidMind; ent.Comp.OriginalBody = randomHumanoidMind.Comp.OwnedEntity; } if (ent.Comp.OriginalBody == null || !_cloning.TryCloning(ent.Comp.OriginalBody.Value, args.Coords, ent.Comp.Settings, out var clone)) { Log.Error($"Unable to make a paradox clone of entity {ToPrettyString(ent.Comp.OriginalBody)}"); return; } var targetComp = EnsureComp(clone.Value); targetComp.Target = ent.Comp.OriginalMind; // set the kill target var gibComp = EnsureComp(clone.Value); gibComp.SpawnProto = ent.Comp.GibProto; gibComp.PreventGibbingObjectives = new() { "ParadoxCloneKillObjective" }; // don't gib them if they killed the original. // turn their suit sensors off so they don't immediately get noticed _sensor.SetAllSensors(clone.Value, SuitSensorMode.SensorOff); args.Entity = clone; } [SubscribeLocalEvent] private void AfterAntagEntitySelected(Entity ent, ref AfterAntagEntitySelectedEvent args) { if (ent.Comp.OriginalMind == null) return; if (!_mind.TryGetMind(args.EntityUid, out var cloneMindId, out var cloneMindComp)) return; _mind.CopyObjectives(ent.Comp.OriginalMind.Value, (cloneMindId, cloneMindComp), ent.Comp.ObjectiveWhitelist, ent.Comp.ObjectiveBlacklist); } }