diff --git a/OpenDiablo2.Common/Enums/eMessageFrameType.cs b/OpenDiablo2.Common/Enums/eMessageFrameType.cs index e602a422..b0d7bf35 100644 --- a/OpenDiablo2.Common/Enums/eMessageFrameType.cs +++ b/OpenDiablo2.Common/Enums/eMessageFrameType.cs @@ -9,6 +9,8 @@ LocatePlayers = 0x03, PlayerInfo = 0x04, FocusOnPlayer = 0x05, + MoveRequest = 0x06, + PlayerMove = 0x07, MAX = 0xFF, // NOTE: diff --git a/OpenDiablo2.Common/Enums/eMovementType.cs b/OpenDiablo2.Common/Enums/eMovementType.cs new file mode 100644 index 00000000..f0e666e8 --- /dev/null +++ b/OpenDiablo2.Common/Enums/eMovementType.cs @@ -0,0 +1,9 @@ +namespace OpenDiablo2.Common.Enums +{ + public enum eMovementType + { + Stopped, + Walking, + Running + } +} diff --git a/OpenDiablo2.Common/Interfaces/MessageBus/ISessionEventProvider.cs b/OpenDiablo2.Common/Interfaces/MessageBus/ISessionEventProvider.cs index a960dc7c..69763e34 100644 --- a/OpenDiablo2.Common/Interfaces/MessageBus/ISessionEventProvider.cs +++ b/OpenDiablo2.Common/Interfaces/MessageBus/ISessionEventProvider.cs @@ -9,6 +9,7 @@ namespace OpenDiablo2.Common.Interfaces public delegate void OnLocatePlayersEvent(int clientHash, IEnumerable playerLocationDetails); public delegate void OnPlayerInfoEvent(int clientHash, IEnumerable playerInfo); public delegate void OnFocusOnPlayer(int clientHash, int playerId); + public delegate void OnMoveRequest(int clientHash, int direction, eMovementType movementType); public interface ISessionEventProvider { @@ -17,5 +18,6 @@ namespace OpenDiablo2.Common.Interfaces OnLocatePlayersEvent OnLocatePlayers { get; set; } OnPlayerInfoEvent OnPlayerInfo { get; set; } OnFocusOnPlayer OnFocusOnPlayer { get; set; } + OnMoveRequest OnMoveRequest { get; set; } } } diff --git a/OpenDiablo2.Common/Interfaces/MessageBus/ISessionManager.cs b/OpenDiablo2.Common/Interfaces/MessageBus/ISessionManager.cs index 9d0dfc02..0b663913 100644 --- a/OpenDiablo2.Common/Interfaces/MessageBus/ISessionManager.cs +++ b/OpenDiablo2.Common/Interfaces/MessageBus/ISessionManager.cs @@ -16,5 +16,6 @@ namespace OpenDiablo2.Common.Interfaces void Stop(); void JoinGame(string playerName, eHero heroType); + void MoveRequest(int direction, eMovementType movementType); } } diff --git a/OpenDiablo2.Common/Interfaces/UI/IButton.cs b/OpenDiablo2.Common/Interfaces/UI/IButton.cs index bac8ee11..4b4196b5 100644 --- a/OpenDiablo2.Common/Interfaces/UI/IButton.cs +++ b/OpenDiablo2.Common/Interfaces/UI/IButton.cs @@ -30,6 +30,11 @@ namespace OpenDiablo2.Common.Interfaces /// bool Enabled { get; set; } + /// + /// If true, the button is pushed down, false otherwise. Only valid for toggle buttons. + /// + bool Toggled { get; } + /// /// The position of the button on the screen. /// diff --git a/OpenDiablo2.Common/OpenDiablo2.Common.csproj b/OpenDiablo2.Common/OpenDiablo2.Common.csproj index 85c5d22e..d68ee558 100644 --- a/OpenDiablo2.Common/OpenDiablo2.Common.csproj +++ b/OpenDiablo2.Common/OpenDiablo2.Common.csproj @@ -56,6 +56,7 @@ + diff --git a/OpenDiablo2.Core/Map Engine/MapEngine.cs b/OpenDiablo2.Core/Map Engine/MapEngine.cs index 67948b42..57ef8e4b 100644 --- a/OpenDiablo2.Core/Map Engine/MapEngine.cs +++ b/OpenDiablo2.Core/Map Engine/MapEngine.cs @@ -55,6 +55,8 @@ namespace OpenDiablo2.Core.Map_Engine public void Render() { + var xOffset = (gameState.ShowInventoryPanel ? -200 : 0) + (gameState.ShowCharacterPanel ? 200 : 0); + var cx = -(cameraLocation.X - Math.Truncate(cameraLocation.X)); var cy = -(cameraLocation.Y - Math.Truncate(cameraLocation.Y)); @@ -73,16 +75,16 @@ namespace OpenDiablo2.Core.Map_Engine foreach (var cellInfo in gameState.GetMapCellInfo((int)ax, (int)ay, eRenderCellType.Floor)) - renderWindow.DrawMapCell(cellInfo, 320 + (int)px + (int)ox, 210 + (int)py + (int)oy); + renderWindow.DrawMapCell(cellInfo, 320 + (int)px + (int)ox + xOffset, 210 + (int)py + (int)oy); foreach (var cellInfo in gameState.GetMapCellInfo((int)ax, (int)ay, eRenderCellType.WallLower)) - renderWindow.DrawMapCell(cellInfo, 320 + (int)px + (int)ox, 210 + (int)py + (int)oy); + renderWindow.DrawMapCell(cellInfo, 320 + (int)px + (int)ox + xOffset, 210 + (int)py + (int)oy); foreach (var cellInfo in gameState.GetMapCellInfo((int)ax, (int)ay, eRenderCellType.WallUpper)) - renderWindow.DrawMapCell(cellInfo, 320 + (int)px + (int)ox, 210 + (int)py + (int)oy); + renderWindow.DrawMapCell(cellInfo, 320 + (int)px + (int)ox + xOffset, 210 + (int)py + (int)oy); foreach (var cellInfo in gameState.GetMapCellInfo((int)ax, (int)ay, eRenderCellType.Roof)) - renderWindow.DrawMapCell(cellInfo, 320 + (int)px + (int)ox, 210 + (int)py + (int)oy); + renderWindow.DrawMapCell(cellInfo, 320 + (int)px + (int)ox + xOffset, 210 + (int)py + (int)oy); } } diff --git a/OpenDiablo2.Core/UI/Button.cs b/OpenDiablo2.Core/UI/Button.cs index 998d9428..5d6c2195 100644 --- a/OpenDiablo2.Core/UI/Button.cs +++ b/OpenDiablo2.Core/UI/Button.cs @@ -39,7 +39,7 @@ namespace OpenDiablo2.Core.UI private bool pressed = false; private bool active = false; // When true, button is actively being focus pressed private bool activeLock = false; // When true, we have locked the mouse from everything else - private bool toggled = false; + public bool Toggled { get; private set; } = false; private Point labelOffset = new Point(); @@ -100,20 +100,20 @@ namespace OpenDiablo2.Core.UI public bool Toggle() { - toggled = !toggled; + Toggled = !Toggled; - OnToggle?.Invoke(toggled); + OnToggle?.Invoke(Toggled); - return toggled; + return Toggled; } public bool Toggle(bool isToggled) { - if(toggled != isToggled) + if(Toggled != isToggled) { OnToggle?.Invoke(isToggled); - toggled = isToggled; + Toggled = isToggled; } return isToggled; @@ -177,7 +177,7 @@ namespace OpenDiablo2.Core.UI { var frame = buttonLayout.BaseFrame; - if(toggled && pressed) + if(Toggled && pressed) { frame = buttonLayout.BaseFrame + 3; } @@ -185,7 +185,7 @@ namespace OpenDiablo2.Core.UI { frame = buttonLayout.BaseFrame + 1; } - else if(toggled) + else if(Toggled) { frame = buttonLayout.BaseFrame + 2; } diff --git a/OpenDiablo2.Scenes/Game.cs b/OpenDiablo2.Scenes/Game.cs index 75a8700e..fe16c3ca 100644 --- a/OpenDiablo2.Scenes/Game.cs +++ b/OpenDiablo2.Scenes/Game.cs @@ -15,7 +15,9 @@ namespace OpenDiablo2.Scenes private readonly IRenderWindow renderWindow; private readonly IResourceManager resourceManager; private readonly IMapEngine mapEngine; + private readonly IMouseInfoProvider mouseInfoProvider; private readonly IGameState gameState; + private readonly ISessionManager sessionManager; private readonly IKeyboardInfoProvider keyboardInfoProvider; //private ISprite[] testSprite; @@ -28,13 +30,20 @@ namespace OpenDiablo2.Scenes private bool showMinipanel = false; private IButton runButton, menuButton; + private eMovementType lastMovementType = eMovementType.Stopped; + private int lastDirection = -1; + + const double Rad2Deg = 180.0 / Math.PI; + const double Deg2Rad = Math.PI / 180.0; public Game( IRenderWindow renderWindow, IResourceManager resourceManager, IMapEngine mapEngine, IGameState gameState, + IMouseInfoProvider mouseInfoProvider, IKeyboardInfoProvider keyboardInfoProvider, + ISessionManager sessionManager, Func createButton, Func createMiniPanel, Func createCharacterPanel, @@ -45,7 +54,9 @@ namespace OpenDiablo2.Scenes this.resourceManager = resourceManager; this.mapEngine = mapEngine; this.gameState = gameState; + this.mouseInfoProvider = mouseInfoProvider; this.keyboardInfoProvider = keyboardInfoProvider; + this.sessionManager = sessionManager; panelSprite = renderWindow.LoadSprite(ResourcePaths.GamePanels, Palettes.Act1); @@ -130,6 +141,8 @@ namespace OpenDiablo2.Scenes public void Update(long ms) { + var seconds = ms / 1000f; + if(showMinipanel) { minipanel.Update(); @@ -140,40 +153,72 @@ namespace OpenDiablo2.Scenes runButton.Update(); menuButton.Update(); - var seconds = (float)ms / 1000f; - var xMod = 0f; - var yMod = 0f; + HandleMovement(); + //var xMod = 0f; + //var yMod = 0f; - if (keyboardInfoProvider.KeyIsPressed(80 /*left*/)) - { - xMod = -8f * seconds; - } - if (keyboardInfoProvider.KeyIsPressed(79 /*right*/)) - { - xMod = 8f * seconds; - } + //if (keyboardInfoProvider.KeyIsPressed(80 /*left*/)) + //{ + // xMod = -8f * seconds; + //} - if (keyboardInfoProvider.KeyIsPressed(81 /*down*/)) - { - yMod = 10f * seconds; - } + //if (keyboardInfoProvider.KeyIsPressed(79 /*right*/)) + //{ + // xMod = 8f * seconds; + //} - if (keyboardInfoProvider.KeyIsPressed(82 /*up*/)) - { - yMod = -10f * seconds; - } + //if (keyboardInfoProvider.KeyIsPressed(81 /*down*/)) + //{ + // yMod = 10f * seconds; + //} - if (xMod != 0f || yMod != 0f) - { - xMod *= .5f; - yMod *= .5f; - mapEngine.CameraLocation = new PointF(mapEngine.CameraLocation.X + xMod, mapEngine.CameraLocation.Y + yMod); - } + //if (keyboardInfoProvider.KeyIsPressed(82 /*up*/)) + //{ + // yMod = -10f * seconds; + //} + + //if (xMod != 0f || yMod != 0f) + //{ + // xMod *= .5f; + // yMod *= .5f; + // mapEngine.CameraLocation = new PointF(mapEngine.CameraLocation.X + xMod, mapEngine.CameraLocation.Y + yMod); + //} mapEngine.Update(ms); } + private void HandleMovement() + { + if (mouseInfoProvider.MouseY > 530) // 550 is what it should be, but the minipanel check needs to happent oo + return; + + if (gameState.ShowInventoryPanel && mouseInfoProvider.MouseX >= 400) + return; + + if (gameState.ShowCharacterPanel && mouseInfoProvider.MouseX < 400) + return; + + + // TODO: Filter movement for inventory panel + var xOffset = (gameState.ShowInventoryPanel ? -200 : 0) + (gameState.ShowCharacterPanel ? 200 : 0); + + var cursorDirection = (int)Math.Round(((Math.Atan2(300 - mouseInfoProvider.MouseY, mouseInfoProvider.MouseX - (400 + xOffset)) * Rad2Deg) + 180) / 32); + + if (mouseInfoProvider.LeftMouseDown && (lastMovementType == eMovementType.Stopped || lastDirection != cursorDirection)) + { + lastDirection = cursorDirection; + lastMovementType = runButton.Toggled ? eMovementType.Running : eMovementType.Walking; + sessionManager.MoveRequest(cursorDirection, lastMovementType); + } + else if (!mouseInfoProvider.LeftMouseDown && lastMovementType != eMovementType.Stopped) + { + lastDirection = cursorDirection; + lastMovementType = eMovementType.Stopped; + sessionManager.MoveRequest(cursorDirection, lastMovementType); + } + } + public void Dispose() { diff --git a/OpenDiablo2.ServiceBus/Message Frames/Client/MFMoveRequest.cs b/OpenDiablo2.ServiceBus/Message Frames/Client/MFMoveRequest.cs new file mode 100644 index 00000000..6813a156 --- /dev/null +++ b/OpenDiablo2.ServiceBus/Message Frames/Client/MFMoveRequest.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenDiablo2.Common.Attributes; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces; + +namespace OpenDiablo2.ServiceBus.Message_Frames.Client +{ + [MessageFrame(eMessageFrameType.MoveRequest)] + public sealed class MFMoveRequest : IMessageFrame + { + public int Direction { get; set; } = 0; + public eMovementType MovementType { get; set; } = eMovementType.Stopped; + + public byte[] Data + { + get => new byte[] { (byte)Direction, (byte)MovementType }; + set + { + Direction = value[0]; + MovementType = (eMovementType)value[1]; + } + } + + public MFMoveRequest() { } + + public MFMoveRequest(int direction, eMovementType movementType) + { + this.Direction = direction; + this.MovementType = movementType; + } + + public void Process(int clientHash, ISessionEventProvider sessionEventProvider) + => sessionEventProvider.OnMoveRequest(clientHash, Direction, MovementType); + } +} diff --git a/OpenDiablo2.ServiceBus/OpenDiablo2.ServiceBus.csproj b/OpenDiablo2.ServiceBus/OpenDiablo2.ServiceBus.csproj index 262105fa..0a3d0d36 100644 --- a/OpenDiablo2.ServiceBus/OpenDiablo2.ServiceBus.csproj +++ b/OpenDiablo2.ServiceBus/OpenDiablo2.ServiceBus.csproj @@ -59,6 +59,7 @@ + diff --git a/OpenDiablo2.ServiceBus/SessionManager.cs b/OpenDiablo2.ServiceBus/SessionManager.cs index 6e90a3c6..80a8c56c 100644 --- a/OpenDiablo2.ServiceBus/SessionManager.cs +++ b/OpenDiablo2.ServiceBus/SessionManager.cs @@ -32,10 +32,11 @@ namespace OpenDiablo2.ServiceBus public OnLocatePlayersEvent OnLocatePlayers { get; set; } public OnPlayerInfoEvent OnPlayerInfo { get; set; } public OnFocusOnPlayer OnFocusOnPlayer { get; set; } + public OnMoveRequest OnMoveRequest { get; set; } public SessionManager( - eSessionType sessionType, - Func getSessionServer, + eSessionType sessionType, + Func getSessionServer, Func getMessageFrame, Func getGameState ) @@ -75,7 +76,7 @@ namespace OpenDiablo2.ServiceBus default: throw new ApplicationException("This session type is currently unsupported."); } - + running = true; resetEvent.WaitOne(); running = false; @@ -100,10 +101,10 @@ namespace OpenDiablo2.ServiceBus Stop(); } - public void Send(IMessageFrame messageFrame) + public void Send(IMessageFrame messageFrame, bool more = false) { var attr = messageFrame.GetType().GetCustomAttributes(true).First(x => typeof(MessageFrameAttribute).IsAssignableFrom(x.GetType())) as MessageFrameAttribute; - requestSocket.SendFrame(new byte[] { (byte)attr.FrameType }.Concat(messageFrame.Data).ToArray()); + requestSocket.SendFrame(new byte[] { (byte)attr.FrameType }.Concat(messageFrame.Data).ToArray(), more); } private void ProcessMessageFrame() where T : IMessageFrame, new() @@ -113,6 +114,7 @@ namespace OpenDiablo2.ServiceBus var bytes = requestSocket.ReceiveFrameBytes(); var frameType = (eMessageFrameType)bytes[0]; + var frameData = bytes.Skip(1).ToArray(); // TODO: Can we maybe use pointers? This seems wasteful var messageFrame = getMessageFrame(frameType); if (messageFrame.GetType() != typeof(T)) @@ -124,6 +126,13 @@ namespace OpenDiablo2.ServiceBus } } + private void NoOp() + { + var bytes = requestSocket.ReceiveFrameBytes(); + if ((eMessageFrameType)bytes[0] != eMessageFrameType.None) + throw new ApplicationException("Excepted a NoOp but got a command instead!"); + } + public void JoinGame(string playerName, eHero heroType) { Task.Run(() => @@ -135,5 +144,12 @@ namespace OpenDiablo2.ServiceBus ProcessMessageFrame(); }); } + + public void MoveRequest(int direction, eMovementType movementType) + => Task.Run(() => + { + Send(new MFMoveRequest(direction, movementType)); + NoOp(); + }); } } diff --git a/OpenDiablo2.ServiceBus/SessionServer.cs b/OpenDiablo2.ServiceBus/SessionServer.cs index 7c5b0496..07c15b72 100644 --- a/OpenDiablo2.ServiceBus/SessionServer.cs +++ b/OpenDiablo2.ServiceBus/SessionServer.cs @@ -25,8 +25,11 @@ namespace OpenDiablo2.ServiceBus private bool running = false; private ResponseSocket responseSocket; - public OnSetSeedEvent OnSetSeed { get; set; } public OnJoinGameEvent OnJoinGame { get; set; } + public OnMoveRequest OnMoveRequest { get; set; } + + // TODO: Fix interface so we don't need this in the session server + public OnSetSeedEvent OnSetSeed { get; set; } public OnLocatePlayersEvent OnLocatePlayers { get; set; } public OnPlayerInfoEvent OnPlayerInfo { get; set; } public OnFocusOnPlayer OnFocusOnPlayer { get; set; } @@ -67,6 +70,7 @@ namespace OpenDiablo2.ServiceBus } OnJoinGame += OnJoinGameHandler; + OnMoveRequest += OnMovementRequestHandler; var proactor = new NetMQProactor(responseSocket, (socket, message) => { @@ -86,6 +90,16 @@ namespace OpenDiablo2.ServiceBus log.Info("Session server has stopped."); } + private void OnMovementRequestHandler(int clientHash, int direction, eMovementType movementType) + { + // TODO: Actually move the player .... + var player = gameServer.Players.FirstOrDefault(x => x.ClientHash == clientHash); + if (player == null) + return; + log.Info($"Player {player.Id} requested to move in {direction} direction: {movementType.ToString()}"); + + NoOp(); + } public void Stop() { @@ -100,6 +114,11 @@ namespace OpenDiablo2.ServiceBus Stop(); } + private void NoOp() + { + responseSocket.SendFrame(new byte[] { (byte)eMessageFrameType.None }); + } + private void Send(IMessageFrame messageFrame, bool more = false) { var attr = messageFrame.GetType().GetCustomAttributes(true).First(x => typeof(MessageFrameAttribute).IsAssignableFrom(x.GetType())) as MessageFrameAttribute; diff --git a/OpenDiablo2/OpenDiablo2.csproj b/OpenDiablo2/OpenDiablo2.csproj index 1ea685e4..50e8adb8 100644 --- a/OpenDiablo2/OpenDiablo2.csproj +++ b/OpenDiablo2/OpenDiablo2.csproj @@ -5,7 +5,7 @@ Debug AnyCPU {2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD} - WinExe + Exe OpenDiablo2 OpenDiablo2 v4.6.1