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;
{
[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";
[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)
{
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);
}
}
[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.
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;
_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);
}
}
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
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
{
"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"
}
]
}