Paradox Clone (#35794)
* polymorph fixes * paradox clone * forensics cleanup * bump doors * 4 * attribution * polymorphn't * clean up objectives * Update Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml * review * add virtual items to blacklist * allow them to roll sleeper agent
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.Cloning;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Gamerule component for spawning a paradox clone antagonist.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ParadoxCloneRuleComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Cloning settings to be used.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<CloningSettingsPrototype> Settings = "BaseClone";
|
||||
|
||||
/// <summary>
|
||||
/// Visual effect spawned when gibbing at round end.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntProtoId GibProto = "MobParadoxTimed";
|
||||
}
|
||||
83
Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs
Normal file
83
Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.Cloning;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Gibbing.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
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!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ParadoxCloneRuleComponent, AntagSelectEntityEvent>(OnAntagSelectEntity);
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, ParadoxCloneRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
|
||||
// check if we got enough potential cloning targets, otherwise cancel the gamerule so that the ghost role does not show up
|
||||
var allHumans = _mind.GetAliveHumans();
|
||||
|
||||
if (allHumans.Count == 0)
|
||||
{
|
||||
Log.Info("Could not find any alive players to create a paradox clone from! Ending gamerule.");
|
||||
ForceEndSelf(uid, gameRule);
|
||||
}
|
||||
}
|
||||
|
||||
// we have to do the spawning here so we can transfer the mind to the correct entity and can assign the objectives correctly
|
||||
private void OnAntagSelectEntity(Entity<ParadoxCloneRuleComponent> ent, ref AntagSelectEntityEvent args)
|
||||
{
|
||||
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();
|
||||
|
||||
// we already checked when starting the gamerule, but someone might have died since then.
|
||||
if (allHumans.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;
|
||||
|
||||
if (bodyToClone == null || !_cloning.TryCloning(bodyToClone.Value, _transform.GetMapCoordinates(spawner), settings, out var clone))
|
||||
{
|
||||
Log.Error($"Unable to make a paradox clone of entity {ToPrettyString(bodyToClone)}");
|
||||
return;
|
||||
}
|
||||
|
||||
var targetComp = EnsureComp<TargetOverrideComponent>(clone.Value);
|
||||
targetComp.Target = playerToClone.Owner; // set the kill target
|
||||
|
||||
var gibComp = EnsureComp<GibOnRoundEndComponent>(clone.Value);
|
||||
gibComp.SpawnProto = ent.Comp.GibProto;
|
||||
gibComp.PreventGibbingObjectives = new() { "ParadoxCloneKillObjective" }; // don't gib them if they killed the original.
|
||||
|
||||
args.Entity = clone;
|
||||
}
|
||||
}
|
||||
55
Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs
Normal file
55
Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Gibbing.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Content.Server.Body.Systems;
|
||||
|
||||
namespace Content.Server.Gibbing.Systems;
|
||||
public sealed class GibOnRoundEndSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly BodySystem _body = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
// this is raised after RoundEndTextAppendEvent, so they can successfully greentext before we gib them
|
||||
SubscribeLocalEvent<RoundEndMessageEvent>(OnRoundEnd);
|
||||
}
|
||||
|
||||
private void OnRoundEnd(RoundEndMessageEvent args)
|
||||
{
|
||||
var gibQuery = EntityQueryEnumerator<GibOnRoundEndComponent>();
|
||||
|
||||
// gib everyone with the component
|
||||
while (gibQuery.MoveNext(out var uid, out var gibComp))
|
||||
{
|
||||
var gib = false;
|
||||
// if they fulfill all objectives given in the component they are not gibbed
|
||||
if (_mind.TryGetMind(uid, out var mindId, out var mindComp))
|
||||
{
|
||||
foreach (var objectiveId in gibComp.PreventGibbingObjectives)
|
||||
{
|
||||
if (!_mind.TryFindObjective((mindId, mindComp), objectiveId, out var objective)
|
||||
|| !_objectives.IsCompleted(objective.Value, (mindId, mindComp)))
|
||||
{
|
||||
gib = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
gib = true;
|
||||
|
||||
if (!gib)
|
||||
continue;
|
||||
|
||||
if (gibComp.SpawnProto != null)
|
||||
SpawnAtPosition(gibComp.SpawnProto, Transform(uid).Coordinates);
|
||||
|
||||
_body.GibBody(uid, splatModifier: 5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Content.Server/Roles/ParadoxCloneRoleComponent.cs
Normal file
9
Content.Server/Roles/ParadoxCloneRoleComponent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind role entities to tag that they are a paradox clone.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ParadoxCloneRoleComponent : BaseMindRoleComponent;
|
||||
Reference in New Issue
Block a user