diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index c7693e641e..df256cbd9f 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -6,6 +6,7 @@ using Content.Client.Interfaces.Chat; using Content.Client.Interfaces.Parallax; using Content.Client.Parallax; using Content.Client.Sandbox; +using Content.Client.State; using Content.Client.UserInterface; using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components.Cargo; @@ -16,6 +17,7 @@ using Content.Shared.GameObjects.Components.VendingMachines; using Robust.Client.Interfaces; using Robust.Client.Interfaces.Graphics.Overlays; using Robust.Client.Interfaces.Input; +using Robust.Client.Interfaces.State; using Robust.Client.Interfaces.UserInterface; using Robust.Client.Player; using Robust.Shared.ContentPack; @@ -30,6 +32,8 @@ namespace Content.Client { #pragma warning disable 649 [Dependency] private readonly IPlayerManager _playerManager; + [Dependency] private readonly IBaseClient _baseClient; + [Dependency] private readonly IStateManager _stateManager; [Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner; #pragma warning restore 649 @@ -161,6 +165,11 @@ namespace Content.Client IoCManager.InjectDependencies(this); _escapeMenuOwner.Initialize(); + + _baseClient.PlayerJoinedGame += (sender, args) => + { + _stateManager.RequestStateChange(); + }; } /// diff --git a/Content.Client/EscapeMenuOwner.cs b/Content.Client/EscapeMenuOwner.cs index c4b8d89cf4..53cee94814 100644 --- a/Content.Client/EscapeMenuOwner.cs +++ b/Content.Client/EscapeMenuOwner.cs @@ -1,4 +1,5 @@ -using Content.Client.UserInterface; +using Content.Client.State; +using Content.Client.UserInterface; using Robust.Client.Console; using Robust.Client.Interfaces.Input; using Robust.Client.Interfaces.Placement; diff --git a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs index 01a76e40a4..0f652a146a 100644 --- a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using Content.Client.State; using Content.Shared.GameObjects; using Content.Shared.GameObjects.EntitySystemMessages; using Content.Shared.Input; diff --git a/Content.Client/State/GameScreen.cs b/Content.Client/State/GameScreen.cs new file mode 100644 index 0000000000..791af667ad --- /dev/null +++ b/Content.Client/State/GameScreen.cs @@ -0,0 +1,152 @@ +using System.Collections.Generic; +using System.Linq; +using Robust.Client.GameObjects.EntitySystems; +using Robust.Client.Interfaces.GameObjects; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Client.Interfaces.Graphics.ClientEye; +using Robust.Client.Interfaces.Input; +using Robust.Client.Interfaces.Placement; +using Robust.Client.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.Input; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.Timing; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Timing; + +namespace Content.Client.State +{ + // OH GOD. + // Ok actually it's fine. + // Instantiated dynamically through the StateManager, Dependencies will be resolved. + public sealed partial class GameScreen : Robust.Client.State.State + { +#pragma warning disable 649 + [Dependency] private readonly IClientEntityManager _entityManager; + [Dependency] private readonly IComponentManager _componentManager; + [Dependency] private readonly IInputManager inputManager; + [Dependency] private readonly IPlayerManager playerManager; + [Dependency] private readonly IPlacementManager placementManager; + [Dependency] private readonly IEyeManager eyeManager; + [Dependency] private readonly IEntitySystemManager entitySystemManager; + [Dependency] private readonly IGameTiming timing; + [Dependency] private readonly IMapManager _mapManager; +#pragma warning restore 649 + + private IEntity lastHoveredEntity; + + public override void Startup() + { + inputManager.KeyBindStateChanged += OnKeyBindStateChanged; + } + + public override void Shutdown() + { + playerManager.LocalPlayer.DetachEntity(); + + inputManager.KeyBindStateChanged -= OnKeyBindStateChanged; + } + + public override void Update(FrameEventArgs e) + { + _componentManager.CullRemovedComponents(); + _entityManager.Update(e.DeltaSeconds); + playerManager.Update(e.DeltaSeconds); + } + + public override void FrameUpdate(FrameEventArgs e) + { + placementManager.FrameUpdate(e); + _entityManager.FrameUpdate(e.DeltaSeconds); + + var mousePosWorld = eyeManager.ScreenToWorld(new ScreenCoordinates(inputManager.MouseScreenPosition)); + var entityToClick = GetEntityUnderPosition(mousePosWorld); + if (entityToClick == lastHoveredEntity) + { + return; + } + + if (lastHoveredEntity != null && !lastHoveredEntity.Deleted) + { + lastHoveredEntity.GetComponent().OnMouseLeave(); + } + + lastHoveredEntity = entityToClick; + + if (lastHoveredEntity != null) + { + lastHoveredEntity.GetComponent().OnMouseEnter(); + } + } + + public IEntity GetEntityUnderPosition(GridCoordinates coordinates) + { + var entitiesUnderPosition = GetEntitiesUnderPosition(coordinates); + return entitiesUnderPosition.Count > 0 ? entitiesUnderPosition[0] : null; + } + + public IList GetEntitiesUnderPosition(GridCoordinates coordinates) + { + // Find all the entities intersecting our click + var worldCoords = coordinates.ToWorld(_mapManager); + var entities = _entityManager.GetEntitiesIntersecting(_mapManager.GetGrid(coordinates.GridID).ParentMapId, worldCoords.Position); + + // Check the entities against whether or not we can click them + var foundEntities = new List<(IEntity clicked, int drawDepth)>(); + foreach (var entity in entities) + { + if (entity.TryGetComponent(out var component) + && entity.Transform.IsMapTransform + && component.CheckClick(coordinates.Position, out var drawDepthClicked)) + { + foundEntities.Add((entity, drawDepthClicked)); + } + } + + if (foundEntities.Count == 0) + return new List(); + + foundEntities.Sort(new ClickableEntityComparer()); + // 0 is the top element. + foundEntities.Reverse(); + return foundEntities.Select(a => a.clicked).ToList(); + } + + internal class ClickableEntityComparer : IComparer<(IEntity clicked, int depth)> + { + public int Compare((IEntity clicked, int depth) x, (IEntity clicked, int depth) y) + { + var val = x.depth.CompareTo(y.depth); + if (val != 0) + { + return val; + } + var transx = x.clicked.Transform; + var transy = y.clicked.Transform; + return transx.GridPosition.Y.CompareTo(transy.GridPosition.Y); + } + } + + /// + /// Converts a state change event from outside the simulation to inside the simulation. + /// + /// Event data values for a bound key state change. + private void OnKeyBindStateChanged(BoundKeyEventArgs args) + { + var inputSys = entitySystemManager.GetEntitySystem(); + + var func = args.Function; + var funcId = inputManager.NetworkBindMap.KeyFunctionID(func); + + var mousePosWorld = eyeManager.ScreenToWorld(args.PointerLocation); + var entityToClick = GetEntityUnderPosition(mousePosWorld); + var message = new FullInputCmdMessage(timing.CurTick, funcId, args.State, mousePosWorld, args.PointerLocation, entityToClick?.Uid ?? EntityUid.Invalid); + + // client side command handlers will always be sent the local player session. + var session = playerManager.LocalPlayer.Session; + inputSys.HandleInputCommand(session, func, message); + } + } +} diff --git a/RobustToolbox b/RobustToolbox index 6cfe6caae2..66ea13684b 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 6cfe6caae27a7e5300efa52688e161c868d5b529 +Subproject commit 66ea13684bc732513f260d32d604bf4656df6d09