using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events;
using Content.Shared.Movement.Systems;
+using Content.Shared.NameModifier.EntitySystems;
using Content.Shared.Popups;
using Content.Shared.Storage.Components;
using Content.Shared.Tag;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly TagSystem _tag = default!;
+ [Dependency] private readonly NameModifierSystem _nameMod = default!;
private EntityQuery<GhostComponent> _ghostQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
else
_minds.TransferTo(mind.Owner, ghost, mind: mind.Comp);
Log.Debug($"Spawned ghost \"{ToPrettyString(ghost)}\" for {mind.Comp.CharacterName}.");
+
+ // we changed the entity name above
+ // we have to call this after the mind has been transferred since some mind roles modify the ghost's name
+ _nameMod.RefreshNameModifiers(ghost);
return ghost;
}
/// Added to mind role entities to tag that they are a paradox clone.
/// </summary>
[RegisterComponent]
-public sealed partial class ParadoxCloneRoleComponent : BaseMindRoleComponent;
+public sealed partial class ParadoxCloneRoleComponent : BaseMindRoleComponent
+{
+ /// <summary>
+ /// Name modifer applied to the player when they turn into a ghost.
+ /// Needed to be able to keep the original and the clone apart in dead chat.
+ /// </summary>
+ [DataField]
+ public LocId? NameModifier = "paradox-clone-ghost-name-modifier";
+}
--- /dev/null
+using Content.Shared.Ghost;
+using Content.Shared.Mind;
+using Content.Shared.NameModifier.EntitySystems;
+using Content.Shared.Roles;
+
+namespace Content.Server.Roles;
+
+/// <summary>
+/// System responsible for giving a ghost of a paradox clone a name modifier.
+/// </summary>
+public sealed class ParadoxCloneRoleSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<ParadoxCloneRoleComponent, MindRelayedEvent<RefreshNameModifiersEvent>>(OnRefreshNameModifiers);
+ }
+
+ private void OnRefreshNameModifiers(Entity<ParadoxCloneRoleComponent> ent, ref MindRelayedEvent<RefreshNameModifiersEvent> args)
+ {
+ if (!TryComp<MindRoleComponent>(ent.Owner, out var roleComp))
+ return;
+
+ // only show for ghosts
+ if (!HasComp<GhostComponent>(roleComp.Mind.Comp.OwnedEntity))
+ return;
+
+ if (ent.Comp.NameModifier != null)
+ args.Args.AddModifier(ent.Comp.NameModifier.Value, 50);
+ }
+}
--- /dev/null
+using Content.Shared.NameModifier.EntitySystems;
+using Content.Shared.Mind.Components;
+
+namespace Content.Shared.Mind;
+
+/// <summary>
+/// Relays events raised on a mobs body to its mind and mind role entities.
+/// Useful for events that should be raised both on the body and the mind.
+/// </summary>
+public abstract partial class SharedMindSystem : EntitySystem
+{
+ public void InitializeRelay()
+ {
+ // for name modifiers that depend on certain mind roles
+ SubscribeLocalEvent<MindContainerComponent, RefreshNameModifiersEvent>(RelayRefToMind);
+ }
+
+ protected void RelayToMind<T>(EntityUid uid, MindContainerComponent component, T args) where T : class
+ {
+ var ev = new MindRelayedEvent<T>(args);
+
+ if (TryGetMind(uid, out var mindId, out var mindComp, component))
+ {
+ RaiseLocalEvent(mindId, ref ev);
+
+ foreach (var role in mindComp.MindRoles)
+ RaiseLocalEvent(role, ref ev);
+ }
+ }
+
+ protected void RelayRefToMind<T>(EntityUid uid, MindContainerComponent component, ref T args) where T : class
+ {
+ var ev = new MindRelayedEvent<T>(args);
+
+ if (TryGetMind(uid, out var mindId, out var mindComp, component))
+ {
+ RaiseLocalEvent(mindId, ref ev);
+
+ foreach (var role in mindComp.MindRoles)
+ RaiseLocalEvent(role, ref ev);
+ }
+
+ args = ev.Args;
+ }
+}
+
+[ByRefEvent]
+public record struct MindRelayedEvent<TEvent>(TEvent Args);
namespace Content.Shared.Mind;
-public abstract class SharedMindSystem : EntitySystem
+public abstract partial class SharedMindSystem : EntitySystem
{
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly INetManager _net = default!;
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnReset);
SubscribeLocalEvent<MindComponent, ComponentStartup>(OnMindStartup);
SubscribeLocalEvent<MindComponent, EntityRenamedEvent>(OnRenamed);
+
+ InitializeRelay();
}
public override void Shutdown()
objective-issuer-paradox = [color=lightblue]Paradox[/color]
paradox-clone-role-greeting = A freak space-time anomaly has teleported you into another reality! Now you have to find your counterpart and kill and replace them. Only one of you two can survive.
+
+paradox-clone-ghost-name-modifier = {$baseName} (clone)