diff --git a/Content.Client/Ghost/GhostComponent.cs b/Content.Client/Ghost/GhostComponent.cs index efeea64923..5674a52960 100644 --- a/Content.Client/Ghost/GhostComponent.cs +++ b/Content.Client/Ghost/GhostComponent.cs @@ -1,143 +1,30 @@ -using System.Collections.Generic; -using Content.Client.Chat.Managers; -using Content.Client.HUD; using Content.Shared.Ghost; -using Robust.Client.GameObjects; using Robust.Client.Player; using Robust.Shared.GameObjects; using Robust.Shared.IoC; -using Robust.Shared.Network; -using Robust.Shared.Players; -using Robust.Shared.ViewVariables; namespace Content.Client.Ghost { [RegisterComponent] public class GhostComponent : SharedGhostComponent { - [Dependency] private readonly IGameHud _gameHud = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IComponentManager _componentManager = default!; - [Dependency] private readonly IChatManager _chatManager = default!; - public List WarpNames = new(); - public Dictionary PlayerNames = new(); - - private GhostGui? _gui ; - - [ViewVariables(VVAccess.ReadOnly)] public bool CanReturnToBody { get; private set; } = true; - - private bool _isAttached; - - public override void OnRemove() - { - base.OnRemove(); - - _gui?.Dispose(); - - // PlayerDetachedMsg might not fire due to deletion order so... - if (_isAttached) - { - SetGhostVisibility(false); - } - } - - private void SetGhostVisibility(bool visibility) - { - foreach (var ghost in _componentManager.GetAllComponents(typeof(GhostComponent), true)) - { - if (ghost.Owner.TryGetComponent(out SpriteComponent? component)) - { - component.Visible = visibility; - } - } - } - - public override void Initialize() - { - base.Initialize(); - - if (Owner.TryGetComponent(out SpriteComponent? component)) - { - component.Visible = - _playerManager.LocalPlayer?.ControlledEntity?.HasComponent() ?? false; - } - } - - public override void HandleMessage(ComponentMessage message, IComponent? component) - { - base.HandleMessage(message, component); - - switch (message) - { - case PlayerAttachedMsg _: - if (_gui == null) - { - _gui = new GhostGui(this); - } - else - { - _gui.Orphan(); - } - - _gameHud.HandsContainer.AddChild(_gui); - SetGhostVisibility(true); - _isAttached = true; - - break; - - case PlayerDetachedMsg _: - _gui!.Parent?.RemoveChild(_gui); - SetGhostVisibility(false); - _isAttached = false; - break; - } - } - - public void SendReturnToBodyMessage() => SendNetworkMessage(new ReturnToBodyComponentMessage()); - - public void SendGhostWarpRequestMessage(string warpName) => SendNetworkMessage(new GhostWarpToLocationRequestMessage(warpName)); - - public void SendGhostWarpRequestMessage(EntityUid target) => SendNetworkMessage(new GhostWarpToTargetRequestMessage(target)); - - public void GhostRequestWarpPoint() => SendNetworkMessage(new GhostRequestWarpPointData()); - - public void GhostRequestPlayerNames() => SendNetworkMessage(new GhostRequestPlayerNameData()); + public GhostGui? Gui { get; set; } + public bool IsAttached { get; set; } public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) { base.HandleComponentState(curState, nextState); - if (curState is not GhostComponentState state) return; - - CanReturnToBody = state.CanReturnToBody; - - if (Owner == _playerManager.LocalPlayer!.ControlledEntity) + if (curState is not GhostComponentState) { - _gui?.Update(); + return; } - } - public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null) - { - base.HandleNetworkMessage(message, netChannel, session); - - switch (message) + if (Owner == _playerManager.LocalPlayer?.ControlledEntity) { - case GhostReplyWarpPointData data: - WarpNames = new List(); - foreach (var names in data.WarpName) - { - WarpNames.Add(names); - } - break; - case GhostReplyPlayerNameData data: - PlayerNames = new Dictionary(); - foreach (var (key, value) in data.PlayerNames) - { - PlayerNames.Add(key,value); - } - break; + Gui?.Update(); } } } diff --git a/Content.Client/Ghost/GhostGui.cs b/Content.Client/Ghost/GhostGui.cs index cf0badffb9..8cac3d2e08 100644 --- a/Content.Client/Ghost/GhostGui.cs +++ b/Content.Client/Ghost/GhostGui.cs @@ -1,8 +1,12 @@ #nullable enable +using System.Collections.Generic; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Ghost; using Robust.Client.Console; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; @@ -17,19 +21,29 @@ namespace Content.Client.Ghost public GhostTargetWindow? TargetWindow { get; } - public GhostGui(GhostComponent owner) + public GhostGui(GhostComponent owner, IEntityNetworkManager eventBus) { IoCManager.InjectDependencies(this); _owner = owner; - TargetWindow = new GhostTargetWindow(owner); + TargetWindow = new GhostTargetWindow(owner, eventBus); MouseFilter = MouseFilterMode.Ignore; - _ghostWarp.OnPressed += _ => TargetWindow.Populate(); - _returnToBody.OnPressed += _ => owner.SendReturnToBodyMessage(); - _ghostRoles.OnPressed += _ => IoCManager.Resolve().RemoteExecuteCommand(null, "ghostroles"); + _ghostWarp.OnPressed += _ => + { + eventBus.SendSystemNetworkMessage(new GhostWarpsRequestEvent()); + TargetWindow.Populate(); + TargetWindow.OpenCentered(); + }; + _returnToBody.OnPressed += _ => + { + var msg = new GhostReturnToBodyRequest(); + eventBus.SendSystemNetworkMessage(msg); + }; + _ghostRoles.OnPressed += _ => IoCManager.Resolve() + .RemoteExecuteCommand(null, "ghostroles"); AddChild(new HBoxContainer { @@ -40,28 +54,42 @@ namespace Content.Client.Ghost _ghostRoles, } }); - - Update(); } public void Update() { _returnToBody.Disabled = !_owner.CanReturnToBody; + TargetWindow?.Populate(); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + TargetWindow?.Dispose(); + } } } public class GhostTargetWindow : SS14Window { private readonly GhostComponent _owner; + private readonly IEntityNetworkManager _netManager; + private readonly VBoxContainer _buttonContainer; - public GhostTargetWindow(GhostComponent owner) + public List Locations { get; set; } = new(); + + public Dictionary Players { get; set; } = new(); + + public GhostTargetWindow(GhostComponent owner, IEntityNetworkManager netManager) { MinSize = SetSize = (300, 450); Title = "Ghost Warp"; _owner = owner; - _owner.GhostRequestWarpPoint(); - _owner.GhostRequestPlayerNames(); + _netManager = netManager; _buttonContainer = new VBoxContainer() { @@ -86,12 +114,11 @@ namespace Content.Client.Ghost _buttonContainer.DisposeAllChildren(); AddButtonPlayers(); AddButtonLocations(); - OpenCentered(); } private void AddButtonPlayers() { - foreach (var (key, value) in _owner.PlayerNames) + foreach (var (key, value) in Players) { var currentButtonRef = new Button { @@ -106,7 +133,8 @@ namespace Content.Client.Ghost currentButtonRef.OnPressed += (_) => { - _owner.SendGhostWarpRequestMessage(key); + var msg = new GhostWarpToTargetRequestEvent(key); + _netManager.SendSystemNetworkMessage(msg); }; _buttonContainer.AddChild(currentButtonRef); @@ -115,7 +143,7 @@ namespace Content.Client.Ghost private void AddButtonLocations() { - foreach (var name in _owner.WarpNames) + foreach (var name in Locations) { var currentButtonRef = new Button { @@ -130,7 +158,8 @@ namespace Content.Client.Ghost currentButtonRef.OnPressed += (_) => { - _owner.SendGhostWarpRequestMessage(name); + var msg = new GhostWarpToLocationRequestEvent(name); + _netManager.SendSystemNetworkMessage(msg); }; _buttonContainer.AddChild(currentButtonRef); diff --git a/Content.Client/Ghost/GhostSystem.cs b/Content.Client/Ghost/GhostSystem.cs new file mode 100644 index 0000000000..b6c285e338 --- /dev/null +++ b/Content.Client/Ghost/GhostSystem.cs @@ -0,0 +1,117 @@ +using Content.Client.HUD; +using Content.Shared.Ghost; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Client.Ghost +{ + [UsedImplicitly] + public class GhostSystem : SharedGhostSystem + { + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IGameHud _gameHud = default!; + + private bool _ghostVisibility; + + private bool GhostVisibility + { + get => _ghostVisibility; + set + { + if (_ghostVisibility == value) + { + return; + } + + _ghostVisibility = value; + + foreach (var ghost in ComponentManager.GetAllComponents(typeof(GhostComponent), true)) + { + if (ghost.Owner.TryGetComponent(out SpriteComponent? sprite)) + { + sprite.Visible = value; + } + } + } + } + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGhostInit); + SubscribeLocalEvent(OnGhostRemove); + + SubscribeLocalEvent(OnGhostPlayerAttach); + SubscribeLocalEvent(OnGhostPlayerDetach); + + SubscribeNetworkEvent(OnGhostWarpsResponse); + } + + private void OnGhostInit(EntityUid uid, GhostComponent component, ComponentInit args) + { + if (component.Owner.TryGetComponent(out SpriteComponent? sprite)) + { + sprite.Visible = GhostVisibility; + } + } + + private void OnGhostRemove(EntityUid uid, GhostComponent component, ComponentRemove args) + { + component.Gui?.Dispose(); + + // PlayerDetachedMsg might not fire due to deletion order so... + if (component.IsAttached) + { + GhostVisibility = false; + } + } + + private void OnGhostPlayerAttach(EntityUid uid, GhostComponent component, PlayerAttachedEvent playerAttachedEvent) + { + if (component.Gui == null) + { + component.Gui = new GhostGui(component, EntityManager.EntityNetManager!); + component.Gui.Update(); + } + else + { + component.Gui.Orphan(); + } + + _gameHud.HandsContainer.AddChild(component.Gui); + GhostVisibility = true; + component.IsAttached = true; + } + + private void OnGhostPlayerDetach(EntityUid uid, GhostComponent component, PlayerDetachedEvent args) + { + component.Gui?.Parent?.RemoveChild(component.Gui); + GhostVisibility = false; + component.IsAttached = false; + } + + private void OnGhostWarpsResponse(GhostWarpsResponseEvent msg) + { + var entity = _playerManager.LocalPlayer?.ControlledEntity; + + if (entity == null || + !entity.TryGetComponent(out GhostComponent? ghost)) + { + return; + } + + var window = ghost.Gui?.TargetWindow; + + if (window != null) + { + window.Locations = msg.Locations; + window.Players = msg.Players; + window.Populate(); + } + } + } +} diff --git a/Content.Server/Ghost/Components/GhostComponent.cs b/Content.Server/Ghost/Components/GhostComponent.cs index 1fe64ad568..84d217b0c6 100644 --- a/Content.Server/Ghost/Components/GhostComponent.cs +++ b/Content.Server/Ghost/Components/GhostComponent.cs @@ -1,187 +1,13 @@ using System; -using System.Collections.Generic; -using System.Linq; -using Content.Server.Players; -using Content.Server.Visible; -using Content.Server.Warps; -using Content.Shared.Examine; using Content.Shared.Ghost; -using Robust.Server.GameObjects; -using Robust.Server.Player; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Localization; -using Robust.Shared.Log; -using Robust.Shared.Network; -using Robust.Shared.Players; -using Robust.Shared.Timing; -using Robust.Shared.Utility; -using Robust.Shared.ViewVariables; #nullable enable namespace Content.Server.Ghost.Components { [RegisterComponent] - public class GhostComponent : SharedGhostComponent, IExamine + public class GhostComponent : SharedGhostComponent { - private bool _canReturnToBody = true; - private TimeSpan _timeOfDeath = TimeSpan.Zero; - [Dependency] private readonly IPlayerManager _playerManager = default!; - [Dependency] private readonly IGameTiming _gameTimer = default!; - [ViewVariables(VVAccess.ReadWrite)] - public bool CanReturnToBody - { - get => _canReturnToBody; - set - { - _canReturnToBody = value; - Dirty(); - } - } - - /// - protected override void Startup() - { - base.Startup(); - - // Allow this entity to be seen by other ghosts. - Owner.EnsureComponent().Layer |= (int) VisibilityFlags.Ghost; - - // Allows this entity to see other ghosts. - Owner.EnsureComponent().VisibilityMask |= (uint) VisibilityFlags.Ghost; - - _timeOfDeath = _gameTimer.RealTime; - } - - /// - protected override void Shutdown() - { - //Perf: If the entity is deleting itself, no reason to change these back. - if(Owner.LifeStage < EntityLifeStage.Terminating) - { - // Entity can't be seen by ghosts anymore. - if (Owner.TryGetComponent(out var visComp)) - visComp.Layer &= ~(int) VisibilityFlags.Ghost; - - // Entity can't see ghosts anymore. - if (Owner.TryGetComponent(out var eyeComp)) - eyeComp.VisibilityMask &= ~(uint) VisibilityFlags.Ghost; - } - - base.Shutdown(); - } - - public override ComponentState GetComponentState(ICommonSession player) => new GhostComponentState(CanReturnToBody); - - public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null!) - { - base.HandleNetworkMessage(message, netChannel, session); - - switch (message) - { - case ReturnToBodyComponentMessage: - { - if (!Owner.TryGetComponent(out ActorComponent? actor) || - !CanReturnToBody) - { - break; - } - - if (netChannel == actor.PlayerSession.ConnectedClient) - { - var o = actor.PlayerSession.ContentData()!.Mind; - o?.UnVisit(); - } - break; - } - case GhostWarpToLocationRequestMessage warp: - { - if (session?.AttachedEntity != Owner) - { - break; - } - - foreach (var warpPoint in FindWaypoints()) - { - if (warp.Name == warpPoint.Location) - { - Owner.Transform.Coordinates = warpPoint.Owner.Transform.Coordinates; - break; - } - } - - Logger.Warning($"User {session.Name} tried to warp to an invalid warp: {warp.Name}"); - - break; - } - case GhostWarpToTargetRequestMessage target: - { - if (session?.AttachedEntity != Owner) - { - break; - } - - if (!Owner.TryGetComponent(out ActorComponent? actor)) - { - break; - } - - if (!Owner.EntityManager.TryGetEntity(target.Target, out var entity)) - { - Logger.Warning($"User {session.Name} tried to warp to an invalid entity id: {target.Target}"); - break; - } - - if (!_playerManager.TryGetSessionByChannel(actor.PlayerSession.ConnectedClient, out var player) || - player.AttachedEntity != entity) - { - break; - } - - Owner.Transform.Coordinates = entity.Transform.Coordinates; - break; - } - case GhostRequestPlayerNameData _: - var playerNames = new Dictionary(); - foreach (var names in _playerManager.GetAllPlayers()) - { - if (names.AttachedEntity != null && names.UserId != netChannel.UserId) - { - playerNames.Add(names.AttachedEntity.Uid,names.AttachedEntity.Name); - } - } - SendNetworkMessage(new GhostReplyPlayerNameData(playerNames)); - break; - case GhostRequestWarpPointData _: - var warpPoints = FindWaypoints(); - var warpName = new List(); - foreach (var point in warpPoints) - { - if (point.Location == null) - { - continue; - } - - warpName.Add(point.Location); - } - SendNetworkMessage(new GhostReplyWarpPointData(warpName)); - break; - } - } - - private List FindWaypoints() - { - var comp = IoCManager.Resolve(); - return comp.EntityQuery(true).ToList(); - } - - public void Examine(FormattedMessage message, bool inDetailsRange) - { - var timeSinceDeath = _gameTimer.RealTime.Subtract(_timeOfDeath); - //If we've been dead for longer than 1 minute use minutes, otherwise use seconds. Ignore the improper plurals. - var deathTimeInfo = timeSinceDeath.Minutes > 0 ? Loc.GetString($"{timeSinceDeath.Minutes} minutes ago") : Loc.GetString($"{timeSinceDeath.Seconds} seconds ago"); - - message.AddMarkup(Loc.GetString("Died [color=yellow]{0}[/color].", deathTimeInfo)); - } + public TimeSpan TimeOfDeath { get; set; } = TimeSpan.Zero; } } diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 8e070aa771..bf0ca1b695 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -1,19 +1,93 @@ +using System.Collections.Generic; +using System.Linq; using Content.Server.Ghost.Components; using Content.Server.Mind.Components; +using Content.Server.Players; +using Content.Server.Visible; +using Content.Server.Warps; +using Content.Shared.Examine; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Ghost; using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Server.Player; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Timing; namespace Content.Server.Ghost { [UsedImplicitly] - public class GhostSystem : EntitySystem + public class GhostSystem : SharedGhostSystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnGhostStartup); + SubscribeLocalEvent(OnGhostShutdown); + + SubscribeLocalEvent(OnGhostExamine); + SubscribeLocalEvent(OnMindRemovedMessage); SubscribeLocalEvent(OnMindUnvisitedMessage); + + SubscribeNetworkEvent(OnGhostWarpsRequest); + SubscribeNetworkEvent(OnGhostReturnToBodyRequest); + SubscribeNetworkEvent(OnGhostWarpToLocationRequest); + SubscribeNetworkEvent(OnGhostWarpToTargetRequest); + } + + private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentStartup args) + { + // Allow this entity to be seen by other ghosts. + if (component.Owner.TryGetComponent(out VisibilityComponent? visibility)) + { + visibility.Layer |= (int) VisibilityFlags.Ghost; + visibility.Layer &= ~(int) VisibilityFlags.Normal; + } + + if (component.Owner.TryGetComponent(out EyeComponent? eye)) + { + eye.VisibilityMask |= (uint) VisibilityFlags.Ghost; + } + + component.TimeOfDeath = _gameTiming.RealTime; + } + + private void OnGhostShutdown(EntityUid uid, GhostComponent component, ComponentShutdown args) + { + // Perf: If the entity is deleting itself, no reason to change these back. + if (component.Owner.LifeStage < EntityLifeStage.Terminating) + { + // Entity can't be seen by ghosts anymore. + if (component.Owner.TryGetComponent(out VisibilityComponent? visibility)) + { + visibility.Layer &= ~(int) VisibilityFlags.Ghost; + visibility.Layer |= (int) VisibilityFlags.Normal; + } + + // Entity can't see ghosts anymore. + if (component.Owner.TryGetComponent(out EyeComponent? eye)) + { + eye.VisibilityMask &= ~(uint) VisibilityFlags.Ghost; + } + } + } + + private void OnGhostExamine(EntityUid uid, GhostComponent component, ExaminedEvent args) + { + var timeSinceDeath = _gameTiming.RealTime.Subtract(component.TimeOfDeath); + var deathTimeInfo = timeSinceDeath.Minutes > 0 + ? Loc.GetString("comp-ghost-examine-time-minutes", ("minutes", timeSinceDeath.Minutes)) + : Loc.GetString("comp-ghost-examine-time-seconds", ("seconds", timeSinceDeath.Seconds)); + + args.Message.AddMarkup(deathTimeInfo); } private void OnMindRemovedMessage(EntityUid uid, GhostComponent component, MindRemovedMessage args) @@ -26,10 +100,76 @@ namespace Content.Server.Ghost DeleteEntity(uid); } + private void OnGhostWarpsRequest(GhostWarpsRequestEvent msg, EntitySessionEventArgs args) + { + var entity = args.SenderSession.AttachedEntity; + + if (entity == null || + !entity.HasComponent()) + { + Logger.Warning($"User {args.SenderSession.Name} sent a {nameof(GhostWarpsRequestEvent)} without being a ghost."); + return; + } + + var response = new GhostWarpsResponseEvent(GetLocationNames().ToList(), GetPlayerWarps(entity.Uid)); + RaiseNetworkEvent(response, args.SenderSession.ConnectedClient); + } + + private void OnGhostReturnToBodyRequest(GhostReturnToBodyRequest msg, EntitySessionEventArgs args) + { + var entity = args.SenderSession.AttachedEntity; + + if (entity == null || + !entity.TryGetComponent(out GhostComponent? ghost) || + !ghost.CanReturnToBody || + !entity.TryGetComponent(out ActorComponent? actor)) + { + Logger.Warning($"User {args.SenderSession.Name} sent an invalid {nameof(GhostReturnToBodyRequest)}"); + return; + } + + actor.PlayerSession.ContentData()!.Mind?.UnVisit(); + } + + private void OnGhostWarpToLocationRequest(GhostWarpToLocationRequestEvent msg, EntitySessionEventArgs args) + { + if (args.SenderSession.AttachedEntity == null || + !args.SenderSession.AttachedEntity.TryGetComponent(out GhostComponent? ghost)) + { + Logger.Warning($"User {args.SenderSession.Name} tried to warp to {msg.Name} without being a ghost."); + return; + } + + if (FindLocation(msg.Name) is { } warp) + { + ghost.Owner.Transform.Coordinates = warp.Owner.Transform.Coordinates; + } + + Logger.Warning($"User {args.SenderSession.Name} tried to warp to an invalid warp: {msg.Name}"); + } + + private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args) + { + if (args.SenderSession.AttachedEntity == null || + !args.SenderSession.AttachedEntity.TryGetComponent(out GhostComponent? ghost)) + { + Logger.Warning($"User {args.SenderSession.Name} tried to warp to {msg.Target} without being a ghost."); + return; + } + + if (!EntityManager.TryGetEntity(msg.Target, out var entity)) + { + Logger.Warning($"User {args.SenderSession.Name} tried to warp to an invalid entity id: {msg.Target}"); + return; + } + + ghost.Owner.Transform.Coordinates = entity.Transform.Coordinates; + } + private void DeleteEntity(EntityUid uid) { if (!EntityManager.TryGetEntity(uid, out var entity) - || entity.Deleted == true + || entity.Deleted || entity.LifeStage == EntityLifeStage.Terminating) return; @@ -37,5 +177,46 @@ namespace Content.Server.Ghost mind.GhostOnShutdown = false; entity.Delete(); } + + private IEnumerable GetLocationNames() + { + foreach (var warp in ComponentManager.EntityQuery()) + { + if (warp.Location != null) + { + yield return warp.Location; + } + } + } + + private WarpPointComponent? FindLocation(string name) + { + foreach (var warp in ComponentManager.EntityQuery(true)) + { + if (warp.Location == name) + { + return warp; + } + } + + return null; + } + + private Dictionary GetPlayerWarps(EntityUid except) + { + var players = new Dictionary(); + + foreach (var player in _playerManager.GetAllPlayers()) + { + if (player.AttachedEntity != null) + { + players.Add(player.AttachedEntity.Uid, player.AttachedEntity.Name); + } + } + + players.Remove(except); + + return players; + } } } diff --git a/Content.Shared/Ghost/GhostWarpToLocationRequestMessage.cs b/Content.Shared/Ghost/GhostWarpToLocationRequestMessage.cs deleted file mode 100644 index 9e928547a6..0000000000 --- a/Content.Shared/Ghost/GhostWarpToLocationRequestMessage.cs +++ /dev/null @@ -1,19 +0,0 @@ -#nullable enable -using System; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization; - -namespace Content.Shared.Ghost -{ - [Serializable, NetSerializable] - public class GhostWarpToLocationRequestMessage : ComponentMessage - { - public string Name { get; } - - public GhostWarpToLocationRequestMessage(string name) - { - Name = name; - Directed = true; - } - } -} diff --git a/Content.Shared/Ghost/GhostWarpToTargetRequestMessage.cs b/Content.Shared/Ghost/GhostWarpToTargetRequestMessage.cs deleted file mode 100644 index da0b161823..0000000000 --- a/Content.Shared/Ghost/GhostWarpToTargetRequestMessage.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization; - -namespace Content.Shared.Ghost -{ - [Serializable, NetSerializable] - public class GhostWarpToTargetRequestMessage : ComponentMessage - { - public EntityUid Target { get; } - - public GhostWarpToTargetRequestMessage(EntityUid target) - { - Target = target; - Directed = true; - } - } -} diff --git a/Content.Shared/Ghost/SharedGhostComponent.cs b/Content.Shared/Ghost/SharedGhostComponent.cs index 6195441b21..bc24fe9eca 100644 --- a/Content.Shared/Ghost/SharedGhostComponent.cs +++ b/Content.Shared/Ghost/SharedGhostComponent.cs @@ -3,7 +3,10 @@ using System.Collections.Generic; using Content.Shared.ActionBlocker; using Content.Shared.NetIDs; using Robust.Shared.GameObjects; +using Robust.Shared.Players; using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; namespace Content.Shared.Ghost { @@ -12,6 +15,30 @@ namespace Content.Shared.Ghost public override string Name => "Ghost"; public override uint? NetID => ContentNetIDs.GHOST; + /// + /// Changed by + /// + [DataField("canReturnToBody")] + [ViewVariables(VVAccess.ReadWrite)] + public bool CanReturnToBody { get; set; } + + public override ComponentState GetComponentState(ICommonSession player) + { + return new GhostComponentState(CanReturnToBody); + } + + public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) + { + base.HandleComponentState(curState, nextState); + + if (curState is not GhostComponentState state) + { + return; + } + + CanReturnToBody = state.CanReturnToBody; + } + public bool CanInteract() => false; public bool CanUse() => false; public bool CanThrow() => false; @@ -26,60 +53,19 @@ namespace Content.Shared.Ghost { public bool CanReturnToBody { get; } - public GhostComponentState(bool canReturnToBody) : base(ContentNetIDs.GHOST) + public HashSet? LocationWarps { get; } + + public Dictionary? PlayerWarps { get; } + + public GhostComponentState( + bool canReturnToBody, + HashSet? locationWarps = null, + Dictionary? playerWarps = null) + : base(ContentNetIDs.GHOST) { CanReturnToBody = canReturnToBody; - } - } - - [Serializable, NetSerializable] - public class ReturnToBodyComponentMessage : ComponentMessage - { - public ReturnToBodyComponentMessage() - { - Directed = true; - } - } - - [Serializable, NetSerializable] - public class GhostRequestWarpPointData : ComponentMessage - { - public GhostRequestWarpPointData() - { - Directed = true; - } - } - - [Serializable, NetSerializable] - public class GhostRequestPlayerNameData : ComponentMessage - { - public GhostRequestPlayerNameData() - { - Directed = true; - } - } - - [Serializable, NetSerializable] - public class GhostReplyWarpPointData : ComponentMessage - { - public List WarpName; - - public GhostReplyWarpPointData(List warpName) - { - WarpName = warpName; - Directed = true; - } - } - - [Serializable, NetSerializable] - public class GhostReplyPlayerNameData : ComponentMessage - { - public Dictionary PlayerNames; - - public GhostReplyPlayerNameData(Dictionary playerNameDict) - { - PlayerNames = playerNameDict; - Directed = true; + LocationWarps = locationWarps; + PlayerWarps = playerWarps; } } } diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs new file mode 100644 index 0000000000..0099d34f37 --- /dev/null +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.Ghost +{ + public abstract class SharedGhostSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGhostChangeCanReturnToBody); + } + + private void OnGhostChangeCanReturnToBody(EntityUid uid, SharedGhostComponent component, GhostChangeCanReturnToBodyEvent args) + { + if (component.CanReturnToBody == args.CanReturnToBody) + { + return; + } + + component.CanReturnToBody = args.CanReturnToBody; + component.Dirty(); + } + } + + /// + /// Raised to change the value of + /// + [Serializable, NetSerializable] + public class GhostChangeCanReturnToBodyEvent : EntityEventArgs + { + public GhostChangeCanReturnToBodyEvent(bool canReturnToBody) + { + CanReturnToBody = canReturnToBody; + } + + public bool CanReturnToBody { get; } + } + + [Serializable, NetSerializable] + public class GhostWarpsRequestEvent : EntityEventArgs + { + } + + [Serializable, NetSerializable] + public class GhostWarpsResponseEvent : EntityEventArgs + { + public GhostWarpsResponseEvent(List locations, Dictionary players) + { + Locations = locations; + Players = players; + } + + public List Locations { get; } + + public Dictionary Players { get; } + } + + [Serializable, NetSerializable] + public class GhostWarpToLocationRequestEvent : EntityEventArgs + { + public string Name { get; } + + public GhostWarpToLocationRequestEvent(string name) + { + Name = name; + } + } + + [Serializable, NetSerializable] + public class GhostWarpToTargetRequestEvent : EntityEventArgs + { + public EntityUid Target { get; } + + public GhostWarpToTargetRequestEvent(EntityUid target) + { + Target = target; + } + } + + [Serializable, NetSerializable] + public class GhostReturnToBodyRequest : EntityEventArgs + { + } +} diff --git a/Resources/Locale/en-US/components/ghost-component.ftl b/Resources/Locale/en-US/components/ghost-component.ftl new file mode 100644 index 0000000000..e691da8886 --- /dev/null +++ b/Resources/Locale/en-US/components/ghost-component.ftl @@ -0,0 +1,3 @@ +# Examine text +comp-ghost-examine-time-minutes = Died [color=yellow]{$minutes} minutes ago.[/color] +comp-ghost-examine-time-seconds = Died [color=yellow]{$seconds} seconds ago.[/color]