diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 2073fe3c49..78d59aa0b6 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Content.Server.GameTicking; using Content.Server.Ghost.Components; +using Content.Server.Mind; using Content.Server.Mind.Components; using Content.Server.Players; using Content.Server.Visible; @@ -27,6 +28,7 @@ namespace Content.Server.Ghost [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly GameTicker _ticker = default!; + [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; public override void Initialize() @@ -185,7 +187,7 @@ namespace Content.Server.Ghost return; if (EntityManager.TryGetComponent(uid, out var mind)) - mind.GhostOnShutdown = false; + _mindSystem.SetGhostOnShutdown(uid, false, mind); EntityManager.DeleteEntity(uid); } diff --git a/Content.Server/Mind/Components/MindComponent.cs b/Content.Server/Mind/Components/MindComponent.cs index 6c12df00d7..d03481be51 100644 --- a/Content.Server/Mind/Components/MindComponent.cs +++ b/Content.Server/Mind/Components/MindComponent.cs @@ -3,6 +3,7 @@ using Content.Server.Ghost.Components; using Content.Shared.Examine; using Content.Shared.Ghost; using Content.Shared.MobState.Components; +using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; @@ -17,21 +18,14 @@ namespace Content.Server.Mind.Components /// /// Stores a on a mob. /// - [RegisterComponent] -#pragma warning disable 618 - public class MindComponent : Component, IExamine -#pragma warning restore 618 + [RegisterComponent, ComponentProtoName("Mind"), Friend(typeof(MindSystem))] + public class MindComponent : Component { - [Dependency] private readonly IEntityManager _entMan = default!; - - /// - public override string Name => "Mind"; - /// /// The mind controlling this mob. Can be null. /// [ViewVariables] - public Mind? Mind { get; private set; } + public Mind? Mind { get; set; } /// /// True if we have a mind, false otherwise. @@ -52,109 +46,6 @@ namespace Content.Server.Mind.Components [ViewVariables(VVAccess.ReadWrite)] [DataField("ghostOnShutdown")] public bool GhostOnShutdown { get; set; } = true; - - /// - /// Don't call this unless you know what the hell you're doing. - /// Use instead. - /// If that doesn't cover it, make something to cover it. - /// - public void InternalEjectMind() - { - if (!Deleted) - _entMan.EventBus.RaiseLocalEvent(Owner, new MindRemovedMessage()); - Mind = null; - } - - /// - /// Don't call this unless you know what the hell you're doing. - /// Use instead. - /// If that doesn't cover it, make something to cover it. - /// - public void InternalAssignMind(Mind value) - { - Mind = value; - _entMan.EventBus.RaiseLocalEvent(Owner, new MindAddedMessage()); - } - - protected override void Shutdown() - { - base.Shutdown(); - - // Let's not create ghosts if not in the middle of the round. - if (EntitySystem.Get().RunLevel != GameRunLevel.InRound) - return; - - if (HasMind) - { - if (Mind?.VisitingEntity is {Valid: true} visiting) - { - if (_entMan.TryGetComponent(visiting, out GhostComponent? ghost)) - { - EntitySystem.Get().SetCanReturnToBody(ghost, false); - } - - Mind!.TransferTo(visiting); - } - else if (GhostOnShutdown) - { - var spawnPosition = _entMan.GetComponent(Owner).Coordinates; - // Use a regular timer here because the entity has probably been deleted. - Timer.Spawn(0, () => - { - // Async this so that we don't throw if the grid we're on is being deleted. - var mapMan = IoCManager.Resolve(); - - var gridId = spawnPosition.GetGridId(_entMan); - if (gridId == GridId.Invalid || !mapMan.GridExists(gridId)) - { - spawnPosition = EntitySystem.Get().GetObserverSpawnPoint(); - } - - var ghost = _entMan.SpawnEntity("MobObserver", spawnPosition); - var ghostComponent = _entMan.GetComponent(ghost); - EntitySystem.Get().SetCanReturnToBody(ghostComponent, false); - - if (Mind != null) - { - string? val = Mind.CharacterName ?? string.Empty; - _entMan.GetComponent(ghost).EntityName = val; - Mind.TransferTo(ghost); - } - }); - } - } - } - - public void Examine(FormattedMessage message, bool inDetailsRange) - { - if (!ShowExamineInfo || !inDetailsRange) - { - return; - } - - var dead = - _entMan.TryGetComponent(Owner, out var state) && - state.IsDead(); - - if (dead) - { - if (Mind?.Session == null) { - // Player has no session attached and dead - message.AddMarkup($"[color=yellow]{Loc.GetString("mind-component-no-mind-and-dead-text", ("ent", Owner))}[/color]"); - } else { - // Player is dead with session - message.AddMarkup($"[color=red]{Loc.GetString("comp-mind-examined-dead", ("ent", Owner))}[/color]"); - } - } - else if (!HasMind) - { - message.AddMarkup($"[color=mediumpurple]{Loc.GetString("comp-mind-examined-catatonic", ("ent", Owner))}[/color]"); - } - else if (Mind?.Session == null) - { - message.AddMarkup($"[color=yellow]{Loc.GetString("comp-mind-examined-ssd", ("ent", Owner))}[/color]"); - } - } } public class MindRemovedMessage : EntityEventArgs diff --git a/Content.Server/Mind/Mind.cs b/Content.Server/Mind/Mind.cs index 22fb9ea7e9..bdf7d29d45 100644 --- a/Content.Server/Mind/Mind.cs +++ b/Content.Server/Mind/Mind.cs @@ -291,15 +291,19 @@ namespace Content.Server.Mind } } - OwnedComponent?.InternalEjectMind(); + var mindSystem = EntitySystem.Get(); + + if(OwnedComponent != null) + mindSystem.InternalEjectMind(OwnedComponent.Owner, OwnedComponent); OwnedComponent = component; - OwnedComponent?.InternalAssignMind(this); + if(OwnedComponent != null) + mindSystem.InternalAssignMind(OwnedComponent.Owner, this, OwnedComponent); if (VisitingEntity != null && (ghostCheckOverride // to force mind transfer, for example from ControlMobVerb - || !entMan.TryGetComponent(VisitingEntity!, out GhostComponent? ghostComponent) // visiting entity is not a Ghost - || !ghostComponent.CanReturnToBody)) // it is a ghost, but cannot return to body anyway, so it's okay + || !entMan.TryGetComponent(VisitingEntity!, out GhostComponent? ghostComponent) // visiting entity is not a Ghost + || !ghostComponent.CanReturnToBody)) // it is a ghost, but cannot return to body anyway, so it's okay { VisitingEntity = default; } diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs new file mode 100644 index 0000000000..ed6801095d --- /dev/null +++ b/Content.Server/Mind/MindSystem.cs @@ -0,0 +1,142 @@ +using System; +using Content.Server.GameTicking; +using Content.Server.Ghost; +using Content.Server.Ghost.Components; +using Content.Server.Mind.Components; +using Content.Shared.Examine; +using Content.Shared.Ghost; +using Content.Shared.MobState.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Map; +using Robust.Shared.Timing; + +namespace Content.Server.Mind; + +public class MindSystem : EntitySystem +{ + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly GhostSystem _ghostSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnExamined); + } + + public void SetGhostOnShutdown(EntityUid uid, bool value, MindComponent? mind = null) + { + if (!Resolve(uid, ref mind)) + return; + + mind.GhostOnShutdown = value; + } + + /// + /// Don't call this unless you know what the hell you're doing. + /// Use instead. + /// If that doesn't cover it, make something to cover it. + /// + public void InternalAssignMind(EntityUid uid, Mind value, MindComponent? mind = null) + { + if (!Resolve(uid, ref mind)) + return; + + mind.Mind = value; + RaiseLocalEvent(uid, new MindAddedMessage()); + } + + /// + /// Don't call this unless you know what the hell you're doing. + /// Use instead. + /// If that doesn't cover it, make something to cover it. + /// + public void InternalEjectMind(EntityUid uid, MindComponent? mind = null) + { + if (!Resolve(uid, ref mind)) + return; + + if (!Deleted(uid)) + RaiseLocalEvent(uid, new MindRemovedMessage()); + + mind.Mind = null; + } + + private void OnShutdown(EntityUid uid, MindComponent mind, ComponentShutdown args) + { + // Let's not create ghosts if not in the middle of the round. + if (_gameTicker.RunLevel != GameRunLevel.InRound) + return; + + if (mind.HasMind) + { + if (mind.Mind?.VisitingEntity is {Valid: true} visiting) + { + if (TryComp(visiting, out GhostComponent? ghost)) + { + _ghostSystem.SetCanReturnToBody(ghost, false); + } + + mind.Mind!.TransferTo(visiting); + } + else if (mind.GhostOnShutdown) + { + var spawnPosition = Transform(uid).Coordinates; + // Use a regular timer here because the entity has probably been deleted. + Timer.Spawn(0, () => + { + // Async this so that we don't throw if the grid we're on is being deleted. + var gridId = spawnPosition.GetGridId(EntityManager); + if (gridId == GridId.Invalid || !_mapManager.GridExists(gridId)) + { + spawnPosition = EntitySystem.Get().GetObserverSpawnPoint(); + } + + var ghost = Spawn("MobObserver", spawnPosition); + var ghostComponent = Comp(ghost); + _ghostSystem.SetCanReturnToBody(ghostComponent, false); + + if (mind.Mind == null) + return; + + var val = mind.Mind.CharacterName ?? string.Empty; + MetaData(ghost).EntityName = val; + mind.Mind.TransferTo(ghost); + }); + } + } + } + + private void OnExamined(EntityUid uid, MindComponent mind, ExaminedEvent args) + { + if (!mind.ShowExamineInfo || !args.IsInDetailsRange) + { + return; + } + + var dead = TryComp(uid, out var state) && state.IsDead(); + + if (dead) + { + if (mind.Mind?.Session == null) { + // Player has no session attached and dead + args.PushMarkup($"[color=yellow]{Loc.GetString("mind-component-no-mind-and-dead-text", ("ent", uid))}[/color]"); + } else { + // Player is dead with session + args.PushMarkup($"[color=red]{Loc.GetString("comp-mind-examined-dead", ("ent", uid))}[/color]"); + } + } + else if (!mind.HasMind) + { + args.PushMarkup($"[color=mediumpurple]{Loc.GetString("comp-mind-examined-catatonic", ("ent", uid))}[/color]"); + } + else if (mind.Mind?.Session == null) + { + args.PushMarkup($"[color=yellow]{Loc.GetString("comp-mind-examined-ssd", ("ent", uid))}[/color]"); + } + } +}