From b4ea6857cd15d8a5db6862f5a80e1c7831a60fb0 Mon Sep 17 00:00:00 2001 From: NuclearWinter Date: Fri, 16 Oct 2020 13:36:20 -0500 Subject: [PATCH] Ghost Teleporting (#2071) * Fix? Nuked everything and put my code back in, hope everything works * Nullable fix? * nullable fix electric boogaloo * Haha nullable error go brrr send help * Cleanup and fix not clearing the button list * Remove unnecessary brackets and parentheses Co-authored-by: DrSmugleaf --- .../Components/Observer/GhostComponent.cs | 55 ++++++-- Content.Client/UserInterface/GhostGui.cs | 120 +++++++++++++++++- .../Components/Mobs/MindComponent.cs | 5 +- .../Components/Observer/GhostComponent.cs | 88 ++++++++++--- .../Observer/SharedGhostComponent.cs | 65 +++++++++- 5 files changed, 298 insertions(+), 35 deletions(-) diff --git a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs index f25c709136..ba79966eab 100644 --- a/Content.Client/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Client/GameObjects/Components/Observer/GhostComponent.cs @@ -1,12 +1,16 @@ +using System.Collections.Generic; using Content.Client.UserInterface; using Content.Shared.GameObjects.Components.Observer; using Robust.Client.GameObjects; using Robust.Client.Player; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; +using Robust.Shared.Players; using Robust.Shared.ViewVariables; +#nullable enable namespace Content.Client.GameObjects.Components.Observer { [RegisterComponent] @@ -15,8 +19,10 @@ namespace Content.Client.GameObjects.Components.Observer [Dependency] private readonly IGameHud _gameHud = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IComponentManager _componentManager = default!; + public List WarpNames = new List(); + public Dictionary PlayerNames = new Dictionary(); - private GhostGui _gui; + private GhostGui? _gui ; [ViewVariables(VVAccess.ReadOnly)] public bool CanReturnToBody { get; private set; } = true; @@ -40,8 +46,10 @@ namespace Content.Client.GameObjects.Components.Observer { foreach (var ghost in _componentManager.GetAllComponents(typeof(GhostComponent))) { - if (ghost.Owner.TryGetComponent(out SpriteComponent component)) + if (ghost.Owner.TryGetComponent(out SpriteComponent? component)) + { component.Visible = visibility; + } } } @@ -49,12 +57,14 @@ namespace Content.Client.GameObjects.Components.Observer { base.Initialize(); - if (Owner.TryGetComponent(out SpriteComponent component)) + if (Owner.TryGetComponent(out SpriteComponent? component)) + { component.Visible = - _playerManager.LocalPlayer.ControlledEntity?.HasComponent() ?? false; + _playerManager.LocalPlayer?.ControlledEntity?.HasComponent() ?? false; + } } - public override void HandleMessage(ComponentMessage message, IComponent component) + public override void HandleMessage(ComponentMessage message, IComponent? component) { base.HandleMessage(message, component); @@ -77,7 +87,7 @@ namespace Content.Client.GameObjects.Components.Observer break; case PlayerDetachedMsg _: - _gui.Parent?.RemoveChild(_gui); + _gui!.Parent?.RemoveChild(_gui); SetGhostVisibility(false); _isAttached = false; break; @@ -86,7 +96,13 @@ namespace Content.Client.GameObjects.Components.Observer public void SendReturnToBodyMessage() => SendNetworkMessage(new ReturnToBodyComponentMessage()); - public override void HandleComponentState(ComponentState curState, ComponentState nextState) + public void SendGhostWarpRequestMessage(EntityUid target = default, string warpName = default!) => SendNetworkMessage(new GhostWarpRequestMessage(target, warpName)); + + public void GhostRequestWarpPoint() => SendNetworkMessage(new GhostRequestWarpPointData()); + + public void GhostRequestPlayerNames() => SendNetworkMessage(new GhostRequestPlayerNameData()); + + public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) { base.HandleComponentState(curState, nextState); @@ -94,10 +110,33 @@ namespace Content.Client.GameObjects.Components.Observer CanReturnToBody = state.CanReturnToBody; - if (Owner == _playerManager.LocalPlayer.ControlledEntity) + if (Owner == _playerManager.LocalPlayer!.ControlledEntity) { _gui?.Update(); } } + + public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null) + { + base.HandleNetworkMessage(message, netChannel, session); + + switch (message) + { + 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; + } + } } } diff --git a/Content.Client/UserInterface/GhostGui.cs b/Content.Client/UserInterface/GhostGui.cs index b998597790..d2fbc68556 100644 --- a/Content.Client/UserInterface/GhostGui.cs +++ b/Content.Client/UserInterface/GhostGui.cs @@ -1,16 +1,18 @@ using Content.Client.GameObjects.Components.Observer; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; using Robust.Shared.IoC; +using Vector2 = Robust.Shared.Maths.Vector2; using Robust.Shared.Localization; namespace Content.Client.UserInterface { public class GhostGui : Control { - - public readonly Button ReturnToBody = new Button() {Text = Loc.GetString("Return to body")}; - private GhostComponent _owner; + private readonly Button _returnToBody = new Button() {Text = Loc.GetString("Return to body")}; + private readonly Button _ghostWarp = new Button() {Text = Loc.GetString("Ghost Warp")}; + private readonly GhostComponent _owner; public GhostGui(GhostComponent owner) { @@ -18,18 +20,124 @@ namespace Content.Client.UserInterface _owner = owner; + var targetMenu = new GhostTargetWindow(owner); + MouseFilter = MouseFilterMode.Ignore; - ReturnToBody.OnPressed += (args) => { owner.SendReturnToBodyMessage(); }; + _ghostWarp.OnPressed += args => targetMenu.Populate(); + _returnToBody.OnPressed += args => owner.SendReturnToBodyMessage(); - AddChild(ReturnToBody); + AddChild(new HBoxContainer + { + Children = + { + _returnToBody, + _ghostWarp + } + }); Update(); } public void Update() { - ReturnToBody.Disabled = !_owner.CanReturnToBody; + _returnToBody.Disabled = !_owner.CanReturnToBody; + } + } + + internal class GhostTargetWindow : SS14Window + { + protected override Vector2? CustomSize => (300, 450); + private readonly GhostComponent _owner; + private readonly VBoxContainer _buttonContainer; + + public GhostTargetWindow(GhostComponent owner) + { + Title = "Ghost Warp"; + _owner = owner; + _owner.GhostRequestWarpPoint(); + _owner.GhostRequestPlayerNames(); + + var margin = new MarginContainer() + { + SizeFlagsVertical = SizeFlags.FillExpand, + SizeFlagsHorizontal = SizeFlags.FillExpand, + }; + + _buttonContainer = new VBoxContainer() + { + SizeFlagsVertical = SizeFlags.FillExpand, + SizeFlagsHorizontal = SizeFlags.Fill, + SeparationOverride = 5, + + }; + + var scrollBarContainer = new ScrollContainer() + { + SizeFlagsVertical = SizeFlags.FillExpand, + SizeFlagsHorizontal = SizeFlags.FillExpand + }; + + margin.AddChild(scrollBarContainer); + scrollBarContainer.AddChild(_buttonContainer); + + Contents.AddChild(margin); + } + + public void Populate() + { + _buttonContainer.DisposeAllChildren(); + AddButtonPlayers(); + AddButtonLocations(); + OpenCentered(); + } + + private void AddButtonPlayers() + { + foreach (var (key, value) in _owner.PlayerNames) + { + var currentButtonRef = new Button + { + Text = value, + TextAlign = Label.AlignMode.Right, + SizeFlagsHorizontal = SizeFlags.ShrinkCenter, + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SizeFlagsStretchRatio = 1, + CustomMinimumSize = (230, 20), + ClipText = true, + }; + + currentButtonRef.OnPressed += (args) => + { + _owner.SendGhostWarpRequestMessage(key); + }; + + _buttonContainer.AddChild(currentButtonRef); + } + } + + private void AddButtonLocations() + { + foreach (var name in _owner.WarpNames) + { + var currentButtonRef = new Button + { + Text = $"Warp: {name}", + TextAlign = Label.AlignMode.Right, + SizeFlagsHorizontal = SizeFlags.ShrinkCenter, + SizeFlagsVertical = SizeFlags.ShrinkCenter, + SizeFlagsStretchRatio = 1, + CustomMinimumSize = (230,20), + ClipText = true, + }; + + currentButtonRef.OnPressed += (args) => + { + _owner.SendGhostWarpRequestMessage(default,name); + }; + + _buttonContainer.AddChild(currentButtonRef); + } } } } diff --git a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs index df8b8b94e3..05c9ec8fbe 100644 --- a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs @@ -83,7 +83,10 @@ namespace Content.Server.GameObjects.Components.Mobs private void OnUiAcceptCloningMessage(ServerBoundUserInterfaceMessage obj) { if (!(obj.Message is SharedAcceptCloningComponent.UiButtonPressedMessage message)) return; - Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new GhostComponent.GhostReturnMessage(Mind)); + if (Mind != null) + { + Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new GhostComponent.GhostReturnMessage(Mind)); + } } public override void OnRemove() diff --git a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs index 10f3e8a6c6..43d22038f7 100644 --- a/Content.Server/GameObjects/Components/Observer/GhostComponent.cs +++ b/Content.Server/GameObjects/Components/Observer/GhostComponent.cs @@ -1,23 +1,29 @@ -using Content.Server.GameObjects.Components.Mobs; -using Content.Server.Mobs; +using System.Collections.Generic; +using System.Linq; +using Content.Server.GameObjects.Components.Markers; using Content.Server.Players; +using Content.Server.GameObjects.Components.Mobs; +using Content.Server.Mobs; using Content.Shared.GameObjects.Components.Observer; using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components; using Robust.Server.Interfaces.GameObjects; +using Robust.Server.Interfaces.Player; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; +using Robust.Shared.IoC; using Robust.Shared.Players; using Robust.Shared.ViewVariables; +#nullable enable namespace Content.Server.GameObjects.Components.Observer { [RegisterComponent] public class GhostComponent : SharedGhostComponent { private bool _canReturnToBody = true; - + [Dependency] private readonly IPlayerManager _playerManager = default!; [ViewVariables(VVAccess.ReadWrite)] public bool CanReturnToBody { @@ -38,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Observer public override ComponentState GetComponentState() => new GhostComponentState(CanReturnToBody); - public override void HandleMessage(ComponentMessage message, IComponent component) + public override void HandleMessage(ComponentMessage message, IComponent? component) { base.HandleMessage(message, component); @@ -51,40 +57,88 @@ namespace Content.Server.GameObjects.Components.Observer case PlayerDetachedMsg msg: msg.OldPlayer.VisibilityMask &= ~(int) VisibilityFlags.Ghost; break; - default: - break; } } - public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, - ICommonSession session = null) + public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null!) { base.HandleNetworkMessage(message, netChannel, session); switch (message) { - case ReturnToBodyComponentMessage reenter: - if (!Owner.TryGetComponent(out IActorComponent actor) || !CanReturnToBody) break; - if (netChannel == null || netChannel == actor.playerSession.ConnectedClient) + case ReturnToBodyComponentMessage _: + if (!Owner.TryGetComponent(out IActorComponent? actor) || + !CanReturnToBody) { - actor.playerSession.ContentData().Mind.UnVisit(); - Owner.Delete(); + break; } + if (netChannel == actor.playerSession.ConnectedClient) + { + var o = actor.playerSession.ContentData()!.Mind; + o?.UnVisit(); + Owner.Delete(); + } break; - case ReturnToCloneComponentMessage reenter: + case ReturnToCloneComponentMessage _: - if (Owner.TryGetComponent(out VisitingMindComponent mind)) + if (Owner.TryGetComponent(out VisitingMindComponent? mind)) { Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new GhostReturnMessage(mind.Mind)); } - break; - default: + case GhostWarpRequestMessage warp: + if (warp.PlayerTarget != default) + { + foreach (var player in _playerManager.GetAllPlayers()) + { + if (player.AttachedEntity != null && warp.PlayerTarget == player.AttachedEntity.Uid) + { + session?.AttachedEntity!.Transform.Coordinates = + player.AttachedEntity.Transform.Coordinates; + } + } + } + else + { + foreach (var warpPoint in FindWaypoints()) + { + if (warp.WarpName == warpPoint.Location) + { + session?.AttachedEntity!.Transform.Coordinates = warpPoint.Owner.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) + { + warpName.Add(point.Location); + } + SendNetworkMessage(new GhostReplyWarpPointData(warpName)); break; } } + private List FindWaypoints() + { + var comp = IoCManager.Resolve(); + return comp.EntityQuery().ToList(); + } + public class GhostReturnMessage : EntitySystemMessage { public GhostReturnMessage(Mind sender) diff --git a/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs b/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs index 797a1ac08d..9d17d1258e 100644 --- a/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs +++ b/Content.Shared/GameObjects/Components/Observer/SharedGhostComponent.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Content.Shared.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; @@ -17,8 +18,6 @@ namespace Content.Shared.GameObjects.Components.Observer public bool CanPickup() => false; public bool CanEmote() => false; public bool CanAttack() => false; - public bool CanShiver() => false; - public bool CanSweat() => false; } [Serializable, NetSerializable] @@ -35,7 +34,65 @@ namespace Content.Shared.GameObjects.Components.Observer [Serializable, NetSerializable] public class ReturnToBodyComponentMessage : ComponentMessage { - public ReturnToBodyComponentMessage() => Directed = true; + public ReturnToBodyComponentMessage() + { + Directed = true; + } + } + + [Serializable, NetSerializable] + public class GhostWarpRequestMessage : ComponentMessage + { + public EntityUid PlayerTarget; + public string WarpName; + public GhostWarpRequestMessage(EntityUid playerTarget = default, string warpTarget = default) + { + WarpName = warpTarget; + PlayerTarget = playerTarget; + 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; + } } @@ -45,3 +102,5 @@ namespace Content.Shared.GameObjects.Components.Observer public ReturnToCloneComponentMessage() => Directed = true; } } + +