diff --git a/Content.Client/Observer/GhostComponent.cs b/Content.Client/Observer/GhostComponent.cs index 31f45d84b8..f1b05fcc29 100644 --- a/Content.Client/Observer/GhostComponent.cs +++ b/Content.Client/Observer/GhostComponent.cs @@ -5,6 +5,8 @@ using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.ViewVariables; namespace Content.Client.Observer { @@ -12,6 +14,14 @@ namespace Content.Client.Observer public class GhostComponent : SharedGhostComponent { private GhostGui _gui; + private bool _canReturnToBody = true; + + [ViewVariables(VVAccess.ReadOnly)] + public override bool CanReturnToBody + { + get => _canReturnToBody; + set {} + } #pragma warning disable 649 [Dependency] private readonly IGameHud _gameHud; @@ -34,7 +44,7 @@ namespace Content.Client.Observer case PlayerAttachedMsg _: if (_gui == null) { - _gui = new GhostGui(); + _gui = new GhostGui(this); } else { @@ -49,5 +59,18 @@ namespace Content.Client.Observer break; } } + + public void SendReturnToBodyMessage() => SendNetworkMessage(new ReturnToBodyComponentMessage()); + + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + base.HandleComponentState(curState, nextState); + + if (!(curState is GhostComponentState state)) return; + + _canReturnToBody = state.CanReturnToBody; + if (_gui == null) return; + _gui.ReturnToBody.Disabled = !_canReturnToBody; + } } } diff --git a/Content.Client/UserInterface/GhostGui.cs b/Content.Client/UserInterface/GhostGui.cs index 63f7a870ba..2a73c6f0fd 100644 --- a/Content.Client/UserInterface/GhostGui.cs +++ b/Content.Client/UserInterface/GhostGui.cs @@ -1,3 +1,4 @@ +using Content.Client.Observer; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.IoC; @@ -6,13 +7,20 @@ namespace Content.Client.UserInterface { public class GhostGui : Control { - public GhostGui() + public Button ReturnToBody = new Button(){Text = "Return to body"}; + private GhostComponent _owner; + + public GhostGui(GhostComponent owner) { IoCManager.InjectDependencies(this); + _owner = owner; + MouseFilter = MouseFilterMode.Ignore; - AddChild(new Label(){Text = "YES THIS IS GHOST WHOOOOOO"}); + ReturnToBody.OnPressed += (args) => { owner.SendReturnToBodyMessage(); }; + + AddChild(ReturnToBody); } } } diff --git a/Content.Server/Observer/GhostComponent.cs b/Content.Server/Observer/GhostComponent.cs new file mode 100644 index 0000000000..d3ea271c52 --- /dev/null +++ b/Content.Server/Observer/GhostComponent.cs @@ -0,0 +1,56 @@ +using System.Threading; +using Content.Server.Players; +using Content.Shared.Observer; +using Robust.Server.GameObjects; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Network; +using Robust.Shared.Log; +using Robust.Shared.ViewVariables; +using Timer = Robust.Shared.Timers.Timer; + + +namespace Content.Server.Observer +{ + [RegisterComponent] + public class GhostComponent : SharedGhostComponent + { + private bool _canReturnToBody = true; + + [ViewVariables(VVAccess.ReadWrite)] + public override bool CanReturnToBody + { + get => _canReturnToBody; + set + { + _canReturnToBody = value; + Dirty(); + } + } + + public override ComponentState GetComponentState() => new GhostComponentState(CanReturnToBody); + + public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, + IComponent component = null) + { + base.HandleMessage(message, netChannel, component); + + switch (message) + { + case ReturnToBodyComponentMessage reenter: + if (!Owner.TryGetComponent(out IActorComponent actor) || !CanReturnToBody) break; + if (netChannel == null || netChannel == actor.playerSession.ConnectedClient) + { + actor.playerSession.ContentData().Mind.UnVisit(); + } + break; + case PlayerDetachedMsg _: + Timer.Spawn(100, Owner.Delete); + break; + default: + break; + } + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index eeecefda41..ef69972f4d 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -41,5 +41,6 @@ public const uint HANDHELD_LIGHT = 1036; public const uint PAPER = 1037; public const uint REAGENT_INJECTOR = 1038; + public const uint GHOST = 1039; } } diff --git a/Content.Shared/Observer/SharedGhostComponent.cs b/Content.Shared/Observer/SharedGhostComponent.cs index dca348b285..b768b1e7a5 100644 --- a/Content.Shared/Observer/SharedGhostComponent.cs +++ b/Content.Shared/Observer/SharedGhostComponent.cs @@ -1,9 +1,32 @@ +using System; +using Content.Shared.GameObjects; using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; namespace Content.Shared.Observer { public class SharedGhostComponent : Component { public override string Name => "Ghost"; + public override uint? NetID => ContentNetIDs.GHOST; + + public virtual bool CanReturnToBody { get; set; } = true; + } + + [Serializable, NetSerializable] + public class GhostComponentState : ComponentState + { + public bool CanReturnToBody { get; } + + public GhostComponentState(bool canReturnToBody) : base(ContentNetIDs.GHOST) + { + CanReturnToBody = canReturnToBody; + } + } + + [Serializable, NetSerializable] + public class ReturnToBodyComponentMessage : ComponentMessage + { + public ReturnToBodyComponentMessage() => Directed = true; } }