mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-10-03 00:28:40 -04:00
Restructured how movement works. Restructured network protocols. Player ID is now GUID based.
This commit is contained in:
parent
c85b2bc605
commit
d911a76cad
@ -8,10 +8,6 @@ namespace OpenDiablo2.Common.Interfaces.Drawing
|
||||
public interface ICharacterRenderer : IDisposable
|
||||
{
|
||||
Guid UID { get; set; }
|
||||
PlayerLocationDetails LocationDetails { get; set; }
|
||||
eHero Hero { get; set; }
|
||||
PlayerEquipment Equipment { get; set; }
|
||||
eMobMode MobMode { get; set; }
|
||||
|
||||
void Update(long ms);
|
||||
void Render(int pixelOffsetX, int pixelOffsetY);
|
||||
|
@ -19,8 +19,6 @@ namespace OpenDiablo2.Common.Interfaces
|
||||
ItemInstance SelectedItem { get; }
|
||||
void SelectItem(ItemInstance item);
|
||||
|
||||
int CameraOffset { get; set; }
|
||||
|
||||
void Initialize(string characterName, eHero hero, eSessionType sessionType);
|
||||
void Update(long ms);
|
||||
IEnumerable<MapCellInfo> GetMapCellInfo(int cellX, int cellY, eRenderCellType renderCellType);
|
||||
|
@ -1,12 +0,0 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public interface IMapEngine
|
||||
{
|
||||
int FocusedPlayerId { get; set; }
|
||||
PointF CameraLocation { get; set; }
|
||||
void Update(long ms);
|
||||
void Render();
|
||||
}
|
||||
}
|
15
OpenDiablo2.Common/Interfaces/IMapRenderer.cs
Normal file
15
OpenDiablo2.Common/Interfaces/IMapRenderer.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public interface IMapRenderer
|
||||
{
|
||||
Guid FocusedPlayerId { get; set; }
|
||||
int CameraOffset { get; set; }
|
||||
PointF CameraLocation { get; set; }
|
||||
void Update(long ms);
|
||||
void Render();
|
||||
PointF GetCellPositionAt(int x, int y);
|
||||
}
|
||||
}
|
46
OpenDiablo2.Common/Interfaces/IMobLocation.cs
Normal file
46
OpenDiablo2.Common/Interfaces/IMobLocation.cs
Normal file
@ -0,0 +1,46 @@
|
||||
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public interface IMobLocation
|
||||
{
|
||||
float X { get; set; }
|
||||
float Y { get; set; }
|
||||
float MovementSpeed { get; set; }
|
||||
int MovementDirection { get; set; }
|
||||
List<PointF> Waypoints { get; set; }
|
||||
eMovementType MovementType { get; set; }
|
||||
}
|
||||
|
||||
public static class MobLocationHelper
|
||||
{
|
||||
public static void CopyMobLocationDetailsTo(this IMobLocation source, IMobLocation dest)
|
||||
{
|
||||
dest.X = source.X;
|
||||
dest.Y = source.Y;
|
||||
dest.MovementSpeed = source.MovementSpeed;
|
||||
dest.MovementDirection = source.MovementDirection;
|
||||
dest.Waypoints = source.Waypoints; // TODO: do we need to do a literaly copy here?
|
||||
dest.MovementType = source.MovementType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -8,7 +9,8 @@ namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public interface IMessageFrame
|
||||
{
|
||||
byte[] Data { get; set; }
|
||||
void LoadFrom(BinaryReader br);
|
||||
void WriteTo(BinaryWriter bw);
|
||||
void Process(int clientHash, ISessionEventProvider sessionEventProvider);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
@ -6,10 +8,10 @@ namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
public delegate void OnSetSeedEvent(int clientHash, int seed);
|
||||
public delegate void OnJoinGameEvent(int clientHash, eHero heroType, string playerName);
|
||||
public delegate void OnLocatePlayersEvent(int clientHash, IEnumerable<PlayerLocationDetails> playerLocationDetails);
|
||||
public delegate void OnLocatePlayersEvent(int clientHash, IEnumerable<LocationDetails> playerLocationDetails);
|
||||
public delegate void OnPlayerInfoEvent(int clientHash, IEnumerable<PlayerInfo> playerInfo);
|
||||
public delegate void OnFocusOnPlayer(int clientHash, int playerId);
|
||||
public delegate void OnMoveRequest(int clientHash, byte direction, eMovementType movementType);
|
||||
public delegate void OnFocusOnPlayer(int clientHash, Guid playerId);
|
||||
public delegate void OnMoveRequest(int clientHash, PointF targetCell, eMovementType movementType);
|
||||
|
||||
public interface ISessionEventProvider
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -14,6 +15,6 @@ namespace OpenDiablo2.Common.Interfaces
|
||||
void Stop();
|
||||
|
||||
void JoinGame(string playerName, eHero heroType);
|
||||
void MoveRequest(byte direction, eMovementType movementType);
|
||||
void MoveRequest(PointF targetCell, eMovementType movementType);
|
||||
}
|
||||
}
|
||||
|
96
OpenDiablo2.Common/Models/LocationDetails.cs
Normal file
96
OpenDiablo2.Common/Models/LocationDetails.cs
Normal file
@ -0,0 +1,96 @@
|
||||
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Models.Mobs;
|
||||
|
||||
namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class LocationDetails : IMobLocation
|
||||
{
|
||||
public Guid UID { get; set; }
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public eMovementType MovementType { get; set; }
|
||||
public int MovementDirection { get; set; }
|
||||
public float MovementSpeed { get; set; }
|
||||
public List<PointF> Waypoints { get; set; } = new List<PointF>();
|
||||
// TODO: They may not be on the same 'anchor map'...
|
||||
|
||||
public void WriteBytes(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(UID.ToByteArray());
|
||||
bw.Write((Single)X);
|
||||
bw.Write((Single)Y);
|
||||
bw.Write((Int32)MovementDirection);
|
||||
bw.Write((byte)MovementType);
|
||||
bw.Write((Single)MovementSpeed);
|
||||
bw.Write((Int16)(Waypoints?.Count ?? 0));
|
||||
for (var i = 0; i < Waypoints.Count; i++)
|
||||
{
|
||||
bw.Write((Single)Waypoints[i].X);
|
||||
bw.Write((Single)Waypoints[i].Y);
|
||||
}
|
||||
}
|
||||
|
||||
public static LocationDetails FromBytes(BinaryReader br)
|
||||
{
|
||||
var result = new LocationDetails
|
||||
{
|
||||
UID = new Guid(br.ReadBytes(16)),
|
||||
X = br.ReadSingle(),
|
||||
Y = br.ReadSingle(),
|
||||
MovementDirection = br.ReadInt32(),
|
||||
MovementType = (eMovementType)br.ReadByte(),
|
||||
MovementSpeed = br.ReadSingle()
|
||||
};
|
||||
var numWaypoints = br.ReadInt16();
|
||||
result.Waypoints = new List<PointF>(numWaypoints);
|
||||
for(var i = 0; i < numWaypoints; i++)
|
||||
{
|
||||
result.Waypoints.Add(new PointF
|
||||
{
|
||||
X = br.ReadSingle(),
|
||||
Y = br.ReadSingle()
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerLocationDetailsExtensions
|
||||
{
|
||||
public static LocationDetails ToPlayerLocationDetails(this PlayerState source)
|
||||
{
|
||||
var result = new LocationDetails
|
||||
{
|
||||
UID = source.UID,
|
||||
X = source.GetPosition().X,
|
||||
Y = source.GetPosition().Y,
|
||||
Waypoints = source.Waypoints,
|
||||
MovementType = source.MovementType,
|
||||
MovementSpeed = (source.MovementType == eMovementType.Running ? source.GetRunVelocity() : source.GetWalkVeloicty()) / 4f
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -11,9 +11,18 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
public readonly int Id;
|
||||
public bool Alive { get; protected set; } = true;
|
||||
|
||||
/// <summary>The X tile location of the mob</summary>
|
||||
public float X { get; set; } = 0;
|
||||
|
||||
/// <summary>The Y tile location of the mob</summary>
|
||||
public float Y { get; set; } = 0;
|
||||
|
||||
/// <summary>The speed of the mob (in units per second)</summary>
|
||||
public float MovementSpeed { get; set; }
|
||||
|
||||
/// <summary>Represents the movement direction of the mob (16 angular segments)</summary>
|
||||
public int MovementDirection { get; set; }
|
||||
|
||||
protected Stat Health;
|
||||
|
||||
protected Dictionary<eDamageTypes, StatDouble> Resistances = new Dictionary<eDamageTypes, StatDouble>();
|
||||
|
@ -16,13 +16,15 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Enums.Mobs;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Mobs;
|
||||
|
||||
namespace OpenDiablo2.Common.Models.Mobs
|
||||
{
|
||||
public class PlayerState : MobState
|
||||
public class PlayerState : MobState, IMobLocation
|
||||
{
|
||||
private readonly IHeroTypeConfig HeroTypeConfig;
|
||||
private readonly ILevelExperienceConfig ExperienceConfig;
|
||||
@ -30,8 +32,8 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
public Guid UID { get; protected set; } = Guid.NewGuid();
|
||||
public eHero HeroType { get; protected set; }
|
||||
public int ClientHash { get; protected set; }
|
||||
public byte MovementDirection { get; set; } = 0;
|
||||
public eMovementType MovementType { get; set; } = eMovementType.Stopped; // TODO: This needs to mess with MobMode somehow
|
||||
public List<PointF> Waypoints { get; set; } = new List<PointF>();
|
||||
public eMovementType MovementType { get; set; } = eMovementType.Stopped;
|
||||
public eMobMode MobMode { get; set; } = eMobMode.PlayerTownWalk;
|
||||
public PlayerEquipment Equipment { get; set; } = new PlayerEquipment();
|
||||
|
||||
|
@ -16,60 +16,75 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Models.Mobs;
|
||||
|
||||
namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class PlayerInfo
|
||||
public sealed class PlayerInfo : IMobLocation
|
||||
{
|
||||
public Guid UID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public eHero Hero { get; set; }
|
||||
public eMobMode MobMode { get; set; }
|
||||
public PlayerLocationDetails LocationDetails { get; set; }
|
||||
public PlayerEquipment Equipment { get; set; }
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public float MovementSpeed { get; set; }
|
||||
public int MovementDirection { get; set; }
|
||||
public List<PointF> Waypoints { get; set; }
|
||||
public eMovementType MovementType { get; set; }
|
||||
|
||||
public byte[] GetBytes()
|
||||
public void WriteBytes(BinaryWriter writer)
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(stream))
|
||||
writer.Write(UID.ToByteArray());
|
||||
writer.Write(Name);
|
||||
writer.Write((byte)Hero);
|
||||
writer.Write((byte)MobMode);
|
||||
writer.Write(Equipment);
|
||||
writer.Write((Single)X);
|
||||
writer.Write((Single)Y);
|
||||
writer.Write((Single)MovementSpeed);
|
||||
writer.Write((byte)MovementDirection);
|
||||
writer.Write((byte)MovementType);
|
||||
writer.Write((UInt16)Waypoints.Count);
|
||||
foreach (var waypoint in Waypoints)
|
||||
{
|
||||
writer.Write((byte)Hero);
|
||||
writer.Write((byte)MobMode);
|
||||
writer.Write(Name);
|
||||
writer.Write(Equipment);
|
||||
writer.Write(LocationDetails.GetBytes());
|
||||
writer.Write(UID.ToByteArray());
|
||||
|
||||
return stream.ToArray();
|
||||
writer.Write((Single)waypoint.X);
|
||||
writer.Write((Single)waypoint.Y);
|
||||
}
|
||||
}
|
||||
|
||||
public static PlayerInfo FromBytes(byte[] data, int offset = 0)
|
||||
public static PlayerInfo FromBytes(BinaryReader br)
|
||||
{
|
||||
using (var stream = new MemoryStream(data))
|
||||
using (var reader = new BinaryReader(stream))
|
||||
var result = new PlayerInfo
|
||||
{
|
||||
reader.ReadBytes(offset); // Skip
|
||||
UID = new Guid(br.ReadBytes(16)),
|
||||
Name = br.ReadString(),
|
||||
Hero = (eHero)br.ReadByte(),
|
||||
MobMode = (eMobMode)br.ReadByte(),
|
||||
Equipment = br.ReadPlayerEquipment(),
|
||||
X = br.ReadSingle(),
|
||||
Y = br.ReadSingle(),
|
||||
MovementSpeed = br.ReadSingle(),
|
||||
MovementDirection = br.ReadByte(),
|
||||
MovementType = (eMovementType)br.ReadByte()
|
||||
};
|
||||
|
||||
var result = new PlayerInfo
|
||||
var numWaypoints = br.ReadUInt16();
|
||||
result.Waypoints = new List<PointF>();
|
||||
for (var i = 0; i < numWaypoints; i++)
|
||||
result.Waypoints.Add(new PointF
|
||||
{
|
||||
Hero = (eHero)reader.ReadByte(),
|
||||
MobMode = (eMobMode)reader.ReadByte(),
|
||||
Name = reader.ReadString(),
|
||||
Equipment = reader.ReadPlayerEquipment(),
|
||||
LocationDetails = PlayerLocationDetails.FromBytes(reader.ReadBytes(PlayerLocationDetails.SizeInBytes)),
|
||||
UID = new Guid(reader.ReadBytes(16))
|
||||
};
|
||||
X = br.ReadSingle(),
|
||||
Y= br.ReadSingle()
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int SizeInBytes => 8 + Encoding.UTF8.GetByteCount(Name) + PlayerLocationDetails.SizeInBytes + 16;
|
||||
}
|
||||
|
||||
|
||||
@ -80,16 +95,16 @@ namespace OpenDiablo2.Common.Models
|
||||
=> new PlayerInfo
|
||||
{
|
||||
UID = source.UID,
|
||||
Hero = source.HeroType,
|
||||
LocationDetails = new PlayerLocationDetails
|
||||
{
|
||||
PlayerId = source.Id,
|
||||
PlayerX = source.GetPosition().X,
|
||||
PlayerY = source.GetPosition().Y,
|
||||
},
|
||||
Name = source.Name,
|
||||
Hero = source.HeroType,
|
||||
MobMode = source.MobMode,
|
||||
Equipment = source.Equipment
|
||||
Equipment = source.Equipment,
|
||||
X = source.X,
|
||||
Y = source.Y,
|
||||
MovementSpeed = source.MovementSpeed,
|
||||
MovementDirection = source.MovementDirection,
|
||||
MovementType = source.MovementType,
|
||||
Waypoints = source.Waypoints
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,78 +0,0 @@
|
||||
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Models.Mobs;
|
||||
|
||||
namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class PlayerLocationDetails
|
||||
{
|
||||
public int PlayerId { get; set; }
|
||||
public float PlayerX { get; set; }
|
||||
public float PlayerY { get; set; }
|
||||
public eMovementType MovementType { get; set; }
|
||||
public int MovementDirection { get; set; }
|
||||
public float MovementSpeed { get; set; }
|
||||
// TODO: They may not be on the same 'anchor map'...
|
||||
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
var result = new List<byte>();
|
||||
result.AddRange(BitConverter.GetBytes(PlayerId));
|
||||
result.AddRange(BitConverter.GetBytes(PlayerX));
|
||||
result.AddRange(BitConverter.GetBytes(PlayerY));
|
||||
result.AddRange(BitConverter.GetBytes(MovementDirection));
|
||||
result.AddRange(BitConverter.GetBytes((byte)MovementType));
|
||||
result.AddRange(BitConverter.GetBytes(MovementSpeed));
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static PlayerLocationDetails FromBytes(byte[] data, int offset = 0)
|
||||
{
|
||||
var result = new PlayerLocationDetails
|
||||
{
|
||||
PlayerId = BitConverter.ToInt32(data, offset + 0),
|
||||
PlayerX = BitConverter.ToSingle(data, offset + 4),
|
||||
PlayerY = BitConverter.ToSingle(data, offset + 8),
|
||||
MovementDirection = BitConverter.ToInt32(data, offset + 12),
|
||||
MovementType = (eMovementType)data[offset + 16],
|
||||
MovementSpeed = BitConverter.ToSingle(data, offset + 18)
|
||||
};
|
||||
return result;
|
||||
}
|
||||
public static int SizeInBytes => 22;
|
||||
}
|
||||
|
||||
public static class PlayerLocationDetailsExtensions
|
||||
{
|
||||
public static PlayerLocationDetails ToPlayerLocationDetails(this PlayerState source)
|
||||
{
|
||||
var result = new PlayerLocationDetails
|
||||
{
|
||||
PlayerId = source.Id,
|
||||
PlayerX = source.GetPosition().X,
|
||||
PlayerY = source.GetPosition().Y,
|
||||
MovementType = source.MovementType,
|
||||
MovementDirection = source.MovementDirection,
|
||||
MovementSpeed = (source.MovementType == eMovementType.Running ? source.GetRunVelocity() : source.GetWalkVeloicty()) / 4f
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -94,6 +94,7 @@
|
||||
<Compile Include="Interfaces\IItemManager.cs" />
|
||||
<Compile Include="Extensions\MobManagerExtensions.cs" />
|
||||
<Compile Include="Interfaces\IGameServer.cs" />
|
||||
<Compile Include="Interfaces\IMobLocation.cs" />
|
||||
<Compile Include="Interfaces\IRandomizedMapGenerator.cs" />
|
||||
<Compile Include="Interfaces\MessageBus\ISessionEventProvider.cs" />
|
||||
<Compile Include="Interfaces\MessageBus\IMessageFrame.cs" />
|
||||
@ -123,7 +124,7 @@
|
||||
<Compile Include="Models\ObjectInfo.cs" />
|
||||
<Compile Include="Models\ObjectTypeInfo.cs" />
|
||||
<Compile Include="Models\PlayerInfo.cs" />
|
||||
<Compile Include="Models\PlayerLocationDetails.cs" />
|
||||
<Compile Include="Models\LocationDetails.cs" />
|
||||
<Compile Include="Interfaces\UI\IButton.cs" />
|
||||
<Compile Include="Interfaces\UI\IItemContainer.cs" />
|
||||
<Compile Include="Interfaces\UI\IPanelFrame.cs" />
|
||||
@ -134,7 +135,7 @@
|
||||
<Compile Include="Interfaces\IGameState.cs" />
|
||||
<Compile Include="Interfaces\System\IKeyboardInfoProvider.cs" />
|
||||
<Compile Include="Interfaces\UI\ILabel.cs" />
|
||||
<Compile Include="Interfaces\IMapEngine.cs" />
|
||||
<Compile Include="Interfaces\IMapRenderer.cs" />
|
||||
<Compile Include="Interfaces\UI\IMiniPanel.cs" />
|
||||
<Compile Include="Interfaces\UI\ICharacterPanel.cs" />
|
||||
<Compile Include="Interfaces\System\IMouseCursor.cs" />
|
||||
@ -187,6 +188,7 @@
|
||||
<Compile Include="Palettes.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ResourcePaths.cs" />
|
||||
<Compile Include="Services\MobMovementService.cs" />
|
||||
<Compile Include="StringUtils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
104
OpenDiablo2.Common/Services/MobMovementService.cs
Normal file
104
OpenDiablo2.Common/Services/MobMovementService.cs
Normal file
@ -0,0 +1,104 @@
|
||||
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
|
||||
namespace OpenDiablo2.Common.Services
|
||||
{
|
||||
public sealed class MobMovementService
|
||||
{
|
||||
const double Rad2Deg = 180.0 / Math.PI;
|
||||
IMobLocation mobLocation;
|
||||
|
||||
public MobMovementService(IMobLocation mobLocation)
|
||||
{
|
||||
this.mobLocation = mobLocation;
|
||||
}
|
||||
|
||||
/// <summary>Moves the mob along the set of waypoints</summary>
|
||||
/// <param name="seconds">The amount of time to process</param>
|
||||
public void CalculateMovement(float seconds)
|
||||
{
|
||||
if (mobLocation.Waypoints.Count == 0)
|
||||
return;
|
||||
|
||||
if (!mobLocation.Waypoints.Any())
|
||||
return;
|
||||
|
||||
// Determine how far we can move this round
|
||||
var moveRate = seconds * mobLocation.MovementSpeed;
|
||||
|
||||
// Keep iterating waypoints while we can
|
||||
while (moveRate > 0 && mobLocation.Waypoints.Any())
|
||||
{
|
||||
// Determine the next target location
|
||||
var targetX = mobLocation.Waypoints.First().X;
|
||||
var targetY = mobLocation.Waypoints.First().Y;
|
||||
|
||||
// If we're already on this spot, we're done
|
||||
if (targetX == mobLocation.X && targetY == mobLocation.Y)
|
||||
{
|
||||
mobLocation.Waypoints.RemoveAt(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate our direction
|
||||
var cursorDirection = Math.Round(Math.Atan2(targetY - mobLocation.Y, targetX - mobLocation.X) * Rad2Deg);
|
||||
if (cursorDirection < 0)
|
||||
cursorDirection += 360;
|
||||
var actualDirection = (byte)(cursorDirection / 22);
|
||||
if (actualDirection >= 16)
|
||||
actualDirection -= 16;
|
||||
mobLocation.MovementDirection = actualDirection;
|
||||
|
||||
// Determine how far we are away from the waypoint
|
||||
var maxDistance = (float)Math.Sqrt(Math.Pow((targetX - mobLocation.X), 2) + Math.Pow((targetY - mobLocation.Y), 2));
|
||||
|
||||
// If we can move beyond the distance we need to to hit the final waypoint, we are doing moving
|
||||
if (moveRate >= maxDistance && mobLocation.Waypoints.Count == 1)
|
||||
{
|
||||
mobLocation.X = mobLocation.Waypoints.First().X;
|
||||
mobLocation.Y = mobLocation.Waypoints.First().Y;
|
||||
mobLocation.Waypoints.Clear();
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate how far we're actually going to move
|
||||
var distance = (float)Math.Min(maxDistance, moveRate);
|
||||
|
||||
// Calculate the point to land on between source and target locations
|
||||
float tx = targetX - mobLocation.X;
|
||||
float ty = targetY - mobLocation.Y;
|
||||
mobLocation.X = (mobLocation.X + distance * tx / maxDistance);
|
||||
mobLocation.Y = (mobLocation.Y + distance * ty / maxDistance);
|
||||
|
||||
// Subtract out the distance we moved (in case we need to move through another node)
|
||||
moveRate = (float)Math.Max(0, moveRate - distance);
|
||||
|
||||
// If we are on the waypoint, remove it from the list
|
||||
if (targetX == mobLocation.X && targetY == mobLocation.Y)
|
||||
mobLocation.Waypoints.RemoveAt(0);
|
||||
}
|
||||
|
||||
// Stop walking if we have run out of waypoints
|
||||
if (mobLocation.Waypoints.Count == 0)
|
||||
mobLocation.MovementType = eMovementType.Stopped;
|
||||
}
|
||||
}
|
||||
}
|
@ -37,8 +37,8 @@ namespace OpenDiablo2.Core
|
||||
builder.RegisterType<ItemManager>().As<IItemManager>().SingleInstance();
|
||||
builder.RegisterType<GameEngine>().AsImplementedInterfaces().SingleInstance();
|
||||
builder.RegisterType<GameState>().As<IGameState>().SingleInstance();
|
||||
builder.RegisterType<MapEngine>().As<IMapEngine>().SingleInstance();
|
||||
builder.RegisterType<GameHUD>().As<IGameHUD>().InstancePerDependency();
|
||||
builder.RegisterType<MapRenderer>().As<IMapRenderer>().SingleInstance();
|
||||
builder.RegisterType<GameHUD>().As<IGameHUD>().SingleInstance();
|
||||
builder.RegisterType<MiniPanel>().As<IMiniPanel>().InstancePerDependency();
|
||||
builder.RegisterType<PanelFrame>().As<IPanelFrame>().InstancePerDependency();
|
||||
builder.RegisterType<CharacterPanel>().AsImplementedInterfaces().InstancePerDependency();
|
||||
|
@ -1,4 +1,20 @@
|
||||
using System;
|
||||
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
@ -7,6 +23,7 @@ using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Exceptions;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Models;
|
||||
using OpenDiablo2.Common.Services;
|
||||
using OpenDiablo2.Core.Map_Engine;
|
||||
|
||||
namespace OpenDiablo2.Core.GameState_
|
||||
@ -22,10 +39,13 @@ namespace OpenDiablo2.Core.GameState_
|
||||
private readonly IRenderWindow renderWindow;
|
||||
private readonly ISoundProvider soundProvider;
|
||||
private readonly IMPQProvider mpqProvider;
|
||||
private readonly Func<IMapEngine> getMapEngine;
|
||||
private readonly Func<IMapRenderer> getMapEngine;
|
||||
private readonly Func<eSessionType, ISessionManager> getSessionManager;
|
||||
private readonly Func<string, IRandomizedMapGenerator> getRandomizedMapGenerator;
|
||||
|
||||
readonly private IMouseCursor originalMouseCursor;
|
||||
|
||||
|
||||
private float animationTime;
|
||||
private List<IMapInfo> mapInfo;
|
||||
private readonly List<MapCellInfo> mapDataLookup;
|
||||
@ -35,14 +55,9 @@ namespace OpenDiablo2.Core.GameState_
|
||||
public string MapName { get; private set; }
|
||||
public Palette CurrentPalette => paletteProvider.PaletteTable[$"ACT{Act}"];
|
||||
public List<PlayerInfo> PlayerInfos { get; private set; }
|
||||
|
||||
readonly private IMouseCursor originalMouseCursor;
|
||||
|
||||
public int Seed { get; internal set; }
|
||||
|
||||
public ItemInstance SelectedItem { get; internal set; }
|
||||
public object ThreadLocker { get; } = new object();
|
||||
|
||||
public int CameraOffset { get; set; } = 0;
|
||||
|
||||
IEnumerable<PlayerInfo> IGameState.PlayerInfos => PlayerInfos;
|
||||
@ -57,7 +72,7 @@ namespace OpenDiablo2.Core.GameState_
|
||||
IRenderWindow renderWindow,
|
||||
ISoundProvider soundProvider,
|
||||
IMPQProvider mpqProvider,
|
||||
Func<IMapEngine> getMapEngine,
|
||||
Func<IMapRenderer> getMapEngine,
|
||||
Func<eSessionType, ISessionManager> getSessionManager,
|
||||
Func<string, IRandomizedMapGenerator> getRandomizedMapGenerator
|
||||
)
|
||||
@ -89,26 +104,28 @@ namespace OpenDiablo2.Core.GameState_
|
||||
sessionManager.OnFocusOnPlayer += OnFocusOnPlayer;
|
||||
|
||||
mapInfo = new List<IMapInfo>();
|
||||
|
||||
|
||||
sceneManager.ChangeScene(eSceneType.Game);
|
||||
sessionManager.JoinGame(characterName, hero);
|
||||
}
|
||||
|
||||
private void OnFocusOnPlayer(int clientHash, int playerId)
|
||||
private void OnFocusOnPlayer(int clientHash, Guid playerId)
|
||||
=> getMapEngine().FocusedPlayerId = playerId;
|
||||
|
||||
private void OnPlayerInfo(int clientHash, IEnumerable<PlayerInfo> playerInfo)
|
||||
=> PlayerInfos = playerInfo.ToList();
|
||||
|
||||
private void OnLocatePlayers(int clientHash, IEnumerable<PlayerLocationDetails> playerLocationDetails)
|
||||
private void OnLocatePlayers(int clientHash, IEnumerable<LocationDetails> playerLocationDetails)
|
||||
{
|
||||
foreach (var player in PlayerInfos)
|
||||
{
|
||||
var details = playerLocationDetails.FirstOrDefault(x => x.PlayerId == player.LocationDetails.PlayerId);
|
||||
|
||||
var details = playerLocationDetails.FirstOrDefault(x => x.UID == player.UID);
|
||||
|
||||
if (details == null)
|
||||
continue;
|
||||
|
||||
player.LocationDetails = details;
|
||||
details.CopyMobLocationDetailsTo(player);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,7 +137,7 @@ namespace OpenDiablo2.Core.GameState_
|
||||
}
|
||||
|
||||
public int HasMap(int cellX, int cellY)
|
||||
=> mapInfo.Count(z => (cellX >= z.TileLocation.Left) && (cellX < z.TileLocation.Right)
|
||||
=> mapInfo.Count(z => (cellX >= z.TileLocation.Left) && (cellX < z.TileLocation.Right)
|
||||
&& (cellY >= z.TileLocation.Top) && (cellY < z.TileLocation.Bottom));
|
||||
|
||||
public IEnumerable<Size> GetMapSizes(int cellX, int cellY)
|
||||
@ -157,7 +174,7 @@ namespace OpenDiablo2.Core.GameState_
|
||||
CellInfo = new Dictionary<eRenderCellType, MapCellInfo[]>(),
|
||||
TileLocation = new Rectangle(origin, new Size(fileData.Width - 1, fileData.Height - 1))
|
||||
};
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -283,7 +300,7 @@ namespace OpenDiablo2.Core.GameState_
|
||||
mi = mapInfo[i];
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (mi == null)
|
||||
return null;
|
||||
@ -471,16 +488,8 @@ namespace OpenDiablo2.Core.GameState_
|
||||
|
||||
private void UpdatePlayer(PlayerInfo player, float seconds)
|
||||
{
|
||||
if (player.LocationDetails.MovementType == eMovementType.Stopped)
|
||||
return;
|
||||
(new MobMovementService(player)).CalculateMovement(seconds);
|
||||
|
||||
var rads = (float)player.LocationDetails.MovementDirection * 22 * (float)Deg2Rad;
|
||||
|
||||
var moveX = (float)Math.Cos(rads) * seconds * player.LocationDetails.MovementSpeed;
|
||||
var moveY = (float)Math.Sin(rads) * seconds * player.LocationDetails.MovementSpeed;
|
||||
|
||||
player.LocationDetails.PlayerX += moveX;
|
||||
player.LocationDetails.PlayerY += moveY;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -1,184 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Drawing;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
namespace OpenDiablo2.Core.Map_Engine
|
||||
{
|
||||
public sealed class MapEngine : IMapEngine
|
||||
{
|
||||
private readonly IGameState _gameState;
|
||||
private readonly IRenderWindow _renderWindow;
|
||||
|
||||
private readonly List<ICharacterRenderer> _characterRenderers = new List<ICharacterRenderer>();
|
||||
|
||||
public int FocusedPlayerId { get; set; } = 0;
|
||||
|
||||
private PointF _cameraLocation = new PointF();
|
||||
public PointF CameraLocation
|
||||
{
|
||||
get => _cameraLocation;
|
||||
set
|
||||
{
|
||||
// ReSharper disable once RedundantCheckBeforeAssignment (This is a false positive)
|
||||
if (_cameraLocation == value)
|
||||
return;
|
||||
|
||||
_cameraLocation = value;
|
||||
}
|
||||
}
|
||||
|
||||
private const int
|
||||
CellSizeX = 160,
|
||||
CellSizeY = 80,
|
||||
CellSizeXHalf = 80,
|
||||
CellSizeYHalf = 40;
|
||||
|
||||
public MapEngine(
|
||||
IGameState gameState,
|
||||
IRenderWindow renderWindow,
|
||||
ISessionManager sessionManager
|
||||
)
|
||||
{
|
||||
_gameState = gameState;
|
||||
_renderWindow = renderWindow;
|
||||
|
||||
sessionManager.OnPlayerInfo += OnPlayerInfo;
|
||||
sessionManager.OnLocatePlayers += OnLocatePlayers;
|
||||
}
|
||||
|
||||
private void OnLocatePlayers(int clientHash, IEnumerable<PlayerLocationDetails> playerLocationDetails)
|
||||
{
|
||||
foreach (var loc in playerLocationDetails)
|
||||
{
|
||||
var cr = _characterRenderers.FirstOrDefault(x => x.LocationDetails.PlayerId == loc.PlayerId);
|
||||
if (cr == null)
|
||||
{
|
||||
// TODO: Should we log this?
|
||||
continue;
|
||||
}
|
||||
var newDirection = loc.MovementDirection != cr.LocationDetails.MovementDirection;
|
||||
var stanceChanged = loc.MovementType != cr.LocationDetails.MovementType;
|
||||
cr.LocationDetails = loc;
|
||||
if (newDirection || stanceChanged)
|
||||
cr.ResetAnimationData();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerInfo(int clientHash, IEnumerable<PlayerInfo> playerInfo)
|
||||
{
|
||||
// Remove character renderers for players that no longer exist...
|
||||
_characterRenderers.RemoveAll(x => playerInfo.Any(z => z.UID == x.UID));
|
||||
|
||||
// Update existing character renderers
|
||||
foreach (var cr in _characterRenderers)
|
||||
{
|
||||
var info = playerInfo.FirstOrDefault(x => x.UID == cr.UID);
|
||||
if (info == null)
|
||||
continue;
|
||||
|
||||
// TODO: This shouldn't be necessary...
|
||||
cr.LocationDetails = info.LocationDetails;
|
||||
cr.MobMode = info.MobMode;
|
||||
cr.Hero = info.Hero;
|
||||
cr.Equipment = info.Equipment;
|
||||
|
||||
}
|
||||
|
||||
// Add character renderers for characters that now exist
|
||||
foreach (var info in playerInfo.Where(x => _characterRenderers.All(z => x.UID != z.UID)).ToArray())
|
||||
{
|
||||
var cr = _renderWindow.CreateCharacterRenderer();
|
||||
cr.UID = info.UID;
|
||||
cr.LocationDetails = info.LocationDetails;
|
||||
cr.MobMode = info.MobMode;
|
||||
cr.Equipment = info.Equipment;
|
||||
cr.Hero = info.Hero;
|
||||
_characterRenderers.Add(cr);
|
||||
cr.ResetAnimationData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private const int SkewX = 400;
|
||||
private const int SkewY = 300;
|
||||
|
||||
public void Render()
|
||||
{
|
||||
var xOffset = _gameState.CameraOffset;
|
||||
|
||||
var cx = -(_cameraLocation.X - Math.Truncate(_cameraLocation.X));
|
||||
var cy = -(_cameraLocation.Y - Math.Truncate(_cameraLocation.Y));
|
||||
|
||||
for (var ty = -7; ty <= 9; ty++)
|
||||
{
|
||||
for (var tx = -8; tx <= 8; tx++)
|
||||
{
|
||||
var ax = (int)(tx + Math.Truncate(_cameraLocation.X));
|
||||
var ay = (int)(ty + Math.Truncate(_cameraLocation.Y));
|
||||
|
||||
var px = (tx - ty) * CellSizeXHalf;
|
||||
var py = (tx + ty) * CellSizeYHalf;
|
||||
|
||||
var ox = (cx - cy) * CellSizeXHalf;
|
||||
var oy = (cx + cy) * CellSizeYHalf;
|
||||
|
||||
|
||||
foreach (var cellInfo in _gameState.GetMapCellInfo(ax, ay, eRenderCellType.WallLower))
|
||||
_renderWindow.DrawMapCell(cellInfo, SkewX + px + (int)ox + xOffset, SkewY + py + (int)oy + 80);
|
||||
|
||||
foreach (var cellInfo in _gameState.GetMapCellInfo(ax, ay, eRenderCellType.Floor))
|
||||
_renderWindow.DrawMapCell(cellInfo, SkewX + px + (int)ox + xOffset, SkewY + py + (int)oy);
|
||||
|
||||
|
||||
foreach (var cellInfo in _gameState.GetMapCellInfo(ax, ay, eRenderCellType.WallNormal))
|
||||
_renderWindow.DrawMapCell(cellInfo, SkewX + px + (int)ox + xOffset, SkewY + py + (int)oy + 80);
|
||||
|
||||
foreach (var character in _characterRenderers.Where(x =>
|
||||
(int)Math.Truncate(x.LocationDetails.PlayerX) == ax &&
|
||||
(int)Math.Truncate(x.LocationDetails.PlayerY) == ay)
|
||||
)
|
||||
{
|
||||
var ptx = character.LocationDetails.PlayerX - Math.Truncate(_cameraLocation.X);
|
||||
var pty = character.LocationDetails.PlayerY - Math.Truncate(_cameraLocation.Y);
|
||||
|
||||
var ppx = (int)((ptx - pty) * CellSizeXHalf);
|
||||
var ppy = (int)((ptx + pty) * CellSizeYHalf);
|
||||
|
||||
character.Render(SkewX + (int)ppx + (int)ox + xOffset, SkewY + (int)ppy + (int)oy);
|
||||
}
|
||||
|
||||
foreach (var cellInfo in _gameState.GetMapCellInfo(ax, ay, eRenderCellType.Roof))
|
||||
_renderWindow.DrawMapCell(cellInfo, SkewX + px + (int)ox + xOffset, SkewY + py + (int)oy - 80);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void Update(long ms)
|
||||
{
|
||||
foreach (var character in _characterRenderers)
|
||||
character.Update(ms);
|
||||
|
||||
if (FocusedPlayerId == 0)
|
||||
return;
|
||||
|
||||
var player = _gameState.PlayerInfos.FirstOrDefault(x => x.LocationDetails.PlayerId == FocusedPlayerId);
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
CameraLocation = new PointF(player.LocationDetails.PlayerX, player.LocationDetails.PlayerY);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
193
OpenDiablo2.Core/Map Engine/MapRenderer.cs
Normal file
193
OpenDiablo2.Core/Map Engine/MapRenderer.cs
Normal file
@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Drawing;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
namespace OpenDiablo2.Core.Map_Engine
|
||||
{
|
||||
public sealed class MapRenderer : IMapRenderer
|
||||
{
|
||||
private readonly IGameState gameState;
|
||||
private readonly IRenderWindow renderWindow;
|
||||
private readonly Func<IGameHUD> getGameHud;
|
||||
|
||||
private readonly List<ICharacterRenderer> _characterRenderers = new List<ICharacterRenderer>();
|
||||
|
||||
public Guid FocusedPlayerId { get; set; } = Guid.Empty;
|
||||
public int CameraOffset { get; set; }
|
||||
|
||||
private PointF _cameraLocation = new PointF();
|
||||
public PointF CameraLocation
|
||||
{
|
||||
get => _cameraLocation;
|
||||
set
|
||||
{
|
||||
// ReSharper disable once RedundantCheckBeforeAssignment (This is a false positive)
|
||||
if (_cameraLocation == value)
|
||||
return;
|
||||
|
||||
_cameraLocation = value;
|
||||
}
|
||||
}
|
||||
|
||||
private const int
|
||||
CellSizeX = 160,
|
||||
CellSizeY = 80,
|
||||
CellSizeXHalf = CellSizeX / 2,
|
||||
CellSizeYHalf = CellSizeY / 2;
|
||||
|
||||
public MapRenderer(
|
||||
IGameState gameState,
|
||||
IRenderWindow renderWindow,
|
||||
ISessionManager sessionManager,
|
||||
Func<IGameHUD> getGameHud
|
||||
)
|
||||
{
|
||||
this.gameState = gameState;
|
||||
this.renderWindow = renderWindow;
|
||||
this.getGameHud = getGameHud;
|
||||
|
||||
sessionManager.OnPlayerInfo += OnPlayerInfo;
|
||||
sessionManager.OnLocatePlayers += OnLocatePlayers;
|
||||
}
|
||||
|
||||
private void OnLocatePlayers(int clientHash, IEnumerable<LocationDetails> locationDetails)
|
||||
{
|
||||
foreach (var locationDetail in locationDetails)
|
||||
{
|
||||
var characterRenderer = _characterRenderers.FirstOrDefault(x => x.UID == locationDetail.UID);
|
||||
var player = gameState.PlayerInfos.FirstOrDefault(x => x.UID == locationDetail.UID);
|
||||
if (characterRenderer == null)
|
||||
{
|
||||
// TODO: Should we log this?
|
||||
continue;
|
||||
}
|
||||
var newDirection = locationDetail.MovementDirection != player.MovementDirection;
|
||||
var stanceChanged = locationDetail.MovementType != player.MovementType;
|
||||
|
||||
locationDetail.CopyMobLocationDetailsTo(player);
|
||||
|
||||
if (newDirection || stanceChanged)
|
||||
characterRenderer.ResetAnimationData();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerInfo(int clientHash, IEnumerable<PlayerInfo> playerInfo)
|
||||
{
|
||||
// Remove character renderers for players that no longer exist...
|
||||
_characterRenderers.RemoveAll(x => playerInfo.Any(z => z.UID == x.UID));
|
||||
|
||||
// Add character renderers for characters that now exist
|
||||
foreach (var info in playerInfo.Where(x => _characterRenderers.All(z => x.UID != z.UID)).ToArray())
|
||||
{
|
||||
var cr = renderWindow.CreateCharacterRenderer();
|
||||
cr.UID = info.UID;
|
||||
_characterRenderers.Add(cr);
|
||||
cr.ResetAnimationData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private const int SkewX = 400;
|
||||
private const int SkewY = 300;
|
||||
|
||||
public void Render()
|
||||
{
|
||||
var xOffset = (getGameHud().IsRightPanelVisible ? -200 : 0) + (getGameHud().IsLeftPanelVisible ? 200 : 0);
|
||||
|
||||
var cx = -(_cameraLocation.X - Math.Truncate(_cameraLocation.X));
|
||||
var cy = -(_cameraLocation.Y - Math.Truncate(_cameraLocation.Y));
|
||||
|
||||
for (var ty = -7; ty <= 9; ty++)
|
||||
{
|
||||
for (var tx = -8; tx <= 8; tx++)
|
||||
{
|
||||
var ax = (int)(tx + Math.Truncate(_cameraLocation.X));
|
||||
var ay = (int)(ty + Math.Truncate(_cameraLocation.Y));
|
||||
|
||||
var px = (tx - ty) * CellSizeXHalf;
|
||||
var py = (tx + ty) * CellSizeYHalf;
|
||||
|
||||
var ox = (cx - cy) * CellSizeXHalf;
|
||||
var oy = (cx + cy) * CellSizeYHalf;
|
||||
|
||||
|
||||
foreach (var cellInfo in gameState.GetMapCellInfo(ax, ay, eRenderCellType.WallLower))
|
||||
renderWindow.DrawMapCell(cellInfo, SkewX + px + (int)ox + xOffset, SkewY + py + (int)oy + 80);
|
||||
|
||||
foreach (var cellInfo in gameState.GetMapCellInfo(ax, ay, eRenderCellType.Floor))
|
||||
renderWindow.DrawMapCell(cellInfo, SkewX + px + (int)ox + xOffset, SkewY + py + (int)oy);
|
||||
|
||||
|
||||
foreach (var cellInfo in gameState.GetMapCellInfo(ax, ay, eRenderCellType.WallNormal))
|
||||
renderWindow.DrawMapCell(cellInfo, SkewX + px + (int)ox + xOffset, SkewY + py + (int)oy + 80);
|
||||
|
||||
foreach (var player in gameState.PlayerInfos.Where(x =>
|
||||
(int)Math.Truncate(x.X) == ax &&
|
||||
(int)Math.Truncate(x.Y) == ay)
|
||||
)
|
||||
{
|
||||
var ptx = player.X - Math.Truncate(_cameraLocation.X);
|
||||
var pty = player.Y - Math.Truncate(_cameraLocation.Y);
|
||||
|
||||
var ppx = (int)((ptx - pty) * CellSizeXHalf);
|
||||
var ppy = (int)((ptx + pty) * CellSizeYHalf);
|
||||
|
||||
_characterRenderers.FirstOrDefault(x => x.UID == player.UID)
|
||||
.Render(SkewX + (int)ppx + (int)ox + xOffset, SkewY + (int)ppy + (int)oy);
|
||||
}
|
||||
|
||||
foreach (var cellInfo in gameState.GetMapCellInfo(ax, ay, eRenderCellType.Roof))
|
||||
renderWindow.DrawMapCell(cellInfo, SkewX + px + (int)ox + xOffset, SkewY + py + (int)oy - 80);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void Update(long ms)
|
||||
{
|
||||
foreach (var character in _characterRenderers)
|
||||
character.Update(ms);
|
||||
|
||||
if (FocusedPlayerId == Guid.Empty)
|
||||
return;
|
||||
|
||||
var player = gameState.PlayerInfos.FirstOrDefault(x => x.UID == FocusedPlayerId);
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
CameraLocation = new PointF(player.X, player.Y);
|
||||
|
||||
}
|
||||
|
||||
public PointF GetCellPositionAt(int x, int y)
|
||||
{
|
||||
var xOffset = (getGameHud().IsRightPanelVisible ? -200 : 0) + (getGameHud().IsLeftPanelVisible ? 200 : 0);
|
||||
var mx = x - 400 - xOffset;
|
||||
var my = y - 300;
|
||||
return new PointF
|
||||
{
|
||||
X = (float) Math.Round((mx / (float) CellSizeXHalf) + (my / (float) CellSizeYHalf) / 2f, 1) + _cameraLocation.X,
|
||||
Y = (float) Math.Round((my / (float) CellSizeYHalf) - (mx / (float) CellSizeXHalf) / 2f, 1) + _cameraLocation.Y
|
||||
};
|
||||
}
|
||||
/*
|
||||
var mx = x - 400 - gameState.CameraOffset;
|
||||
var my = y - 300;
|
||||
|
||||
var tx = (mx / 60f + my / 40f) / 2f;
|
||||
var ty = (my / 40f - (mx / 60f)) / 2f;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@
|
||||
<Compile Include="GameEngine.cs" />
|
||||
<Compile Include="GameState\GameState.cs" />
|
||||
<Compile Include="GameState\MobManager.cs" />
|
||||
<Compile Include="Map Engine\MapEngine.cs" />
|
||||
<Compile Include="Map Engine\MapRenderer.cs" />
|
||||
<Compile Include="Map Engine\MapGenerator.cs" />
|
||||
<Compile Include="MPQProvider.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
@ -28,7 +28,6 @@ namespace OpenDiablo2.Core.UI
|
||||
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private readonly IRenderWindow renderWindow;
|
||||
private readonly IGameState gameState;
|
||||
private readonly IMouseInfoProvider mouseInfoProvider;
|
||||
private readonly IMiniPanel minipanel;
|
||||
|
||||
@ -42,14 +41,12 @@ namespace OpenDiablo2.Core.UI
|
||||
|
||||
public GameHUD(
|
||||
IRenderWindow renderWindow,
|
||||
IGameState gameState,
|
||||
IMouseInfoProvider mouseInfoProvider,
|
||||
Func<IMiniPanel> createMiniPanel,
|
||||
Func<eButtonType, IButton> createButton,
|
||||
Func<ePanelFrameType, IPanelFrame> createPanelFrame)
|
||||
{
|
||||
this.renderWindow = renderWindow;
|
||||
this.gameState = gameState;
|
||||
this.mouseInfoProvider = mouseInfoProvider;
|
||||
minipanel = createMiniPanel();
|
||||
minipanel.OnPanelToggled += TogglePanel;
|
||||
@ -89,6 +86,8 @@ namespace OpenDiablo2.Core.UI
|
||||
public bool IsRightPanelVisible => RightPanel != null;
|
||||
public bool IsRunningEnabled => runButton.Toggled;
|
||||
|
||||
private bool needsCameraUpdate = true;
|
||||
|
||||
public void TogglePanel(ePanelType panelType)
|
||||
{
|
||||
TogglePanel(minipanel.GetPanel(panelType));
|
||||
@ -193,6 +192,12 @@ namespace OpenDiablo2.Core.UI
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (needsCameraUpdate)
|
||||
{
|
||||
needsCameraUpdate = false;
|
||||
UpdateCameraOffset();
|
||||
}
|
||||
|
||||
runButton.Update();
|
||||
menuButton.Update();
|
||||
addStatButton.Update();
|
||||
@ -215,14 +220,9 @@ namespace OpenDiablo2.Core.UI
|
||||
}
|
||||
|
||||
private void UpdateCameraOffset()
|
||||
{
|
||||
gameState.CameraOffset = (IsRightPanelVisible ? -200 : 0) + (IsLeftPanelVisible ? 200 : 0);
|
||||
minipanel.UpdatePanelLocation();
|
||||
}
|
||||
=> minipanel.UpdatePanelLocation();
|
||||
|
||||
private void OnRunToggle(bool isToggled)
|
||||
{
|
||||
log.Debug("Run Toggle: " + isToggled);
|
||||
}
|
||||
=> log.Debug("Run Toggle: " + isToggled);
|
||||
}
|
||||
}
|
||||
|
@ -32,24 +32,24 @@ namespace OpenDiablo2.Core.UI
|
||||
|
||||
private readonly IRenderWindow renderWindow;
|
||||
private readonly IMouseInfoProvider mouseInfoProvider;
|
||||
private readonly IGameState gameState;
|
||||
private readonly ISprite sprite;
|
||||
private readonly IReadOnlyList<IButton> buttons;
|
||||
private readonly IEnumerable<IPanel> panels;
|
||||
private readonly Func<IGameHUD> _getGameHud;
|
||||
|
||||
private bool isPanelVisible;
|
||||
|
||||
public event OnPanelToggledEvent OnPanelToggled;
|
||||
|
||||
public MiniPanel(IRenderWindow renderWindow,
|
||||
IGameState gameState,
|
||||
public MiniPanel(IRenderWindow renderWindow,
|
||||
Func<IGameHUD> getGameHud,
|
||||
IMouseInfoProvider mouseInfoProvider,
|
||||
IEnumerable<IPanel> panels,
|
||||
Func<eButtonType, IButton> createButton)
|
||||
{
|
||||
this.renderWindow = renderWindow;
|
||||
this.mouseInfoProvider = mouseInfoProvider;
|
||||
this.gameState = gameState;
|
||||
this._getGameHud = getGameHud;
|
||||
this.panels = panels;
|
||||
|
||||
sprite = renderWindow.LoadSprite(ResourcePaths.MinipanelSmall, Palettes.Units, true);
|
||||
@ -66,8 +66,6 @@ namespace OpenDiablo2.Core.UI
|
||||
}
|
||||
return newBtn;
|
||||
}).ToList().AsReadOnly();
|
||||
|
||||
UpdatePanelLocation();
|
||||
}
|
||||
|
||||
public void OnMenuToggle(bool isToggled) => isPanelVisible = isToggled;
|
||||
@ -117,7 +115,8 @@ namespace OpenDiablo2.Core.UI
|
||||
|
||||
public void UpdatePanelLocation()
|
||||
{
|
||||
sprite.Location = new Point((800 - sprite.LocalFrameSize.Width + (int)(gameState.CameraOffset * 1.3f)) / 2,
|
||||
var cameraOffset = (_getGameHud().IsRightPanelVisible ? -200 : 0) + (_getGameHud().IsLeftPanelVisible ? 200 : 0);
|
||||
sprite.Location = new Point((800 - sprite.LocalFrameSize.Width + (int)(cameraOffset * 1.3f)) / 2,
|
||||
526 + sprite.LocalFrameSize.Height);
|
||||
|
||||
for (int i = 0; i < buttons.Count; i++)
|
||||
|
@ -32,8 +32,9 @@ namespace OpenDiablo2.Core.UI
|
||||
public sealed class InventoryPanel : IInventoryPanel
|
||||
{
|
||||
private readonly IRenderWindow renderWindow;
|
||||
private readonly IMapEngine mapEngine;
|
||||
private readonly IMapRenderer mapRenderer;
|
||||
private readonly ISprite panelSprite;
|
||||
private readonly IGameState gameState;
|
||||
|
||||
public IItemContainer headContainer, torsoContainer, beltContainer, gloveContainer, bootsContainer,
|
||||
leftHandContainer, rightHandContainer, secondaryLeftHandContainer, secondaryRightHandContainer,
|
||||
@ -45,14 +46,17 @@ namespace OpenDiablo2.Core.UI
|
||||
|
||||
public InventoryPanel(IRenderWindow renderWindow,
|
||||
IItemManager itemManager,
|
||||
IMapEngine mapEngine,
|
||||
IMapRenderer mapRenderer,
|
||||
ISessionManager sessionManager,
|
||||
Func<eItemContainerType, IItemContainer> createItemContainer,
|
||||
IGameState gameState,
|
||||
Func<eButtonType, IButton> createButton)
|
||||
{
|
||||
this.renderWindow = renderWindow;
|
||||
this.mapEngine = mapEngine;
|
||||
this.mapRenderer = mapRenderer;
|
||||
this.gameState = gameState;
|
||||
|
||||
sessionManager.OnFocusOnPlayer += OnFocusOnPlayer;
|
||||
sessionManager.OnPlayerInfo += OnPlayerInfo;
|
||||
|
||||
panelSprite = renderWindow.LoadSprite(ResourcePaths.InventoryCharacterPanel, Palettes.Units, FrameType.GetOffset(), true);
|
||||
@ -110,28 +114,38 @@ namespace OpenDiablo2.Core.UI
|
||||
bootsContainer.Location = panelSprite.Location + new Size(251, 178);
|
||||
}
|
||||
|
||||
private void OnPlayerInfo(int clientHash, IEnumerable<PlayerInfo> playerInfo)
|
||||
{
|
||||
var currentPlayer = gameState.PlayerInfos.FirstOrDefault(x => x.UID == mapRenderer.FocusedPlayerId);
|
||||
if (currentPlayer != null)
|
||||
UpdateInventoryPanel(currentPlayer);
|
||||
}
|
||||
|
||||
private void OnFocusOnPlayer(int clientHash, Guid playerId)
|
||||
{
|
||||
var currentPlayer = gameState.PlayerInfos.FirstOrDefault(x => x.UID == playerId);
|
||||
if (currentPlayer != null)
|
||||
UpdateInventoryPanel(currentPlayer);
|
||||
}
|
||||
|
||||
private void UpdateInventoryPanel(PlayerInfo currentPlayer)
|
||||
{
|
||||
leftHandContainer.SetContainedItem(currentPlayer.Equipment.LeftArm);
|
||||
rightHandContainer.SetContainedItem(currentPlayer.Equipment.RightArm);
|
||||
torsoContainer.SetContainedItem(currentPlayer.Equipment.Torso);
|
||||
headContainer.SetContainedItem(currentPlayer.Equipment.Head);
|
||||
ringLeftContainer.SetContainedItem(currentPlayer.Equipment.LeftRing);
|
||||
ringRightContainer.SetContainedItem(currentPlayer.Equipment.RightRing);
|
||||
beltContainer.SetContainedItem(currentPlayer.Equipment.Belt);
|
||||
neckContainer.SetContainedItem(currentPlayer.Equipment.Neck);
|
||||
gloveContainer.SetContainedItem(currentPlayer.Equipment.Gloves);
|
||||
}
|
||||
|
||||
public ePanelType PanelType => ePanelType.Inventory;
|
||||
public ePanelFrameType FrameType => ePanelFrameType.Right;
|
||||
|
||||
public bool IsSecondaryEquipped { get; private set; }
|
||||
|
||||
public void OnPlayerInfo(int clientHash, IEnumerable<PlayerInfo> playerInfos)
|
||||
{
|
||||
// TODO: Ugly hack. Update when we can look up by GUID
|
||||
var currentPLayer = playerInfos.ToArray()[mapEngine.FocusedPlayerId];
|
||||
|
||||
leftHandContainer.SetContainedItem(currentPLayer.Equipment.LeftArm);
|
||||
rightHandContainer.SetContainedItem(currentPLayer.Equipment.RightArm);
|
||||
torsoContainer.SetContainedItem(currentPLayer.Equipment.Torso);
|
||||
headContainer.SetContainedItem(currentPLayer.Equipment.Head);
|
||||
ringLeftContainer.SetContainedItem(currentPLayer.Equipment.LeftRing);
|
||||
ringRightContainer.SetContainedItem(currentPLayer.Equipment.RightRing);
|
||||
beltContainer.SetContainedItem(currentPLayer.Equipment.Belt);
|
||||
neckContainer.SetContainedItem(currentPLayer.Equipment.Neck);
|
||||
gloveContainer.SetContainedItem(currentPLayer.Equipment.Gloves);
|
||||
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (IsSecondaryEquipped)
|
||||
|
@ -1,9 +1,26 @@
|
||||
using System;
|
||||
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Mobs;
|
||||
using OpenDiablo2.Common.Models.Mobs;
|
||||
using OpenDiablo2.Common.Services;
|
||||
|
||||
namespace OpenDiablo2.GameServer_
|
||||
{
|
||||
@ -63,7 +80,7 @@ namespace OpenDiablo2.GameServer_
|
||||
// ... we should probably just fail here
|
||||
}
|
||||
|
||||
var newPlayer = new PlayerState(clientHash, playerName, mobManager.GetNextAvailableMobId(), 1, 20.0f, 20.0f, 10, 10, 10, 10, 0, heroType,
|
||||
var newPlayer = new PlayerState(clientHash, playerName, mobManager.GetNextAvailableMobId(), 1, 20.5f, 20.5f, 10, 10, 10, 10, 0, heroType,
|
||||
heroConfig, expConfig);
|
||||
|
||||
// This is probably not the right place to do this.
|
||||
@ -92,19 +109,10 @@ namespace OpenDiablo2.GameServer_
|
||||
|
||||
private void UpdatePlayerMovement(PlayerState player, float seconds)
|
||||
{
|
||||
// TODO: We need to do collision detection here...
|
||||
if (player.MovementType == eMovementType.Stopped)
|
||||
if (player.Waypoints.Count == 0)
|
||||
return;
|
||||
|
||||
var rads = (float)player.MovementDirection * 22 * (float)Deg2Rad;
|
||||
|
||||
var speed = (player.MovementType == eMovementType.Running ? player.GetRunVelocity() : player.GetWalkVeloicty()) / 4f;
|
||||
|
||||
var moveX = (float)Math.Cos(rads) * seconds * speed;
|
||||
var moveY = (float)Math.Sin(rads) * seconds * speed;
|
||||
|
||||
player.X += moveX;
|
||||
player.Y += moveY;
|
||||
(new MobMovementService(player)).CalculateMovement(seconds);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -43,6 +43,7 @@
|
||||
<HintPath>..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
@ -8,6 +8,7 @@ using System.Linq;
|
||||
|
||||
namespace OpenDiablo2.MapGenerators
|
||||
{
|
||||
// TODO: Different difficulties have different sizes. We need to read this from levels.txt
|
||||
[RandomizedMap("Blood Moor")]
|
||||
public sealed class BloodMoor : IRandomizedMapGenerator
|
||||
{
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenDiablo2.Common;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
@ -44,13 +45,29 @@ namespace OpenDiablo2.SDL2_
|
||||
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public Guid UID { get; set; }
|
||||
public PlayerLocationDetails LocationDetails { get; set; }
|
||||
public eHero Hero { get; set; }
|
||||
public PlayerEquipment Equipment { get; set; }
|
||||
public eMobMode MobMode { get; set; }
|
||||
public string ShieldCode { get; set; }
|
||||
public string WeaponCode { get; set; }
|
||||
|
||||
private PlayerInfo currentPlayer = null;
|
||||
private PlayerInfo GetThisMob()
|
||||
{
|
||||
if (currentPlayer == null)
|
||||
currentPlayer = gameState.PlayerInfos.First(x => x.UID == UID);
|
||||
return currentPlayer;
|
||||
}
|
||||
|
||||
|
||||
private eMobMode MobMode
|
||||
{
|
||||
get => GetThisMob().MobMode;
|
||||
set => GetThisMob().MobMode = value;
|
||||
}
|
||||
private IMobLocation MobLocation => GetThisMob();
|
||||
private eHero Hero => GetThisMob().Hero;
|
||||
private PlayerEquipment Equipment => GetThisMob().Equipment;
|
||||
|
||||
|
||||
|
||||
private readonly IntPtr renderer;
|
||||
|
||||
private readonly List<DirectionCacheItem> directionCache = new List<DirectionCacheItem>();
|
||||
@ -60,16 +77,20 @@ namespace OpenDiablo2.SDL2_
|
||||
|
||||
private readonly IResourceManager resourceManager;
|
||||
private readonly IPaletteProvider paletteProvider;
|
||||
private readonly IGameState gameState;
|
||||
private int lastDirection = -1;
|
||||
private eMovementType lastMovementType = eMovementType.Stopped;
|
||||
|
||||
private MPQCOF animationData;
|
||||
|
||||
static readonly byte[] directionConversion = new byte[] { 3, 15, 4, 8, 0, 9, 5, 10, 1, 11, 6, 12, 2, 13, 7, 14 };
|
||||
|
||||
public SDL2CharacterRenderer(IntPtr renderer, IResourceManager resourceManager, IPaletteProvider paletteProvider)
|
||||
public SDL2CharacterRenderer(IntPtr renderer, IResourceManager resourceManager, IPaletteProvider paletteProvider, IGameState gameState)
|
||||
{
|
||||
this.resourceManager = resourceManager;
|
||||
this.paletteProvider = paletteProvider;
|
||||
this.renderer = renderer;
|
||||
this.gameState = gameState;
|
||||
}
|
||||
|
||||
public void Render(int pixelOffsetX, int pixelOffsetY)
|
||||
@ -93,6 +114,14 @@ namespace OpenDiablo2.SDL2_
|
||||
if (currentDirectionCache == null)
|
||||
return;
|
||||
|
||||
|
||||
if ((lastDirection != MobLocation.MovementDirection) || (lastMovementType != MobLocation.MovementType))
|
||||
{
|
||||
lastMovementType = MobLocation.MovementType;
|
||||
lastDirection = MobLocation.MovementDirection;
|
||||
ResetAnimationData();
|
||||
}
|
||||
|
||||
seconds += ms / 1000f;
|
||||
var animationSeg = 15f / currentDirectionCache.AnimationSpeed;
|
||||
while (seconds >= animationSeg)
|
||||
@ -111,8 +140,9 @@ namespace OpenDiablo2.SDL2_
|
||||
|
||||
public void ResetAnimationData()
|
||||
{
|
||||
|
||||
var lastMobMode = MobMode;
|
||||
switch (LocationDetails.MovementType)
|
||||
switch (MobLocation.MovementType)
|
||||
{
|
||||
case eMovementType.Stopped:
|
||||
MobMode = eMobMode.PlayerTownNeutral;
|
||||
@ -130,7 +160,7 @@ namespace OpenDiablo2.SDL2_
|
||||
if (lastMobMode != MobMode)
|
||||
renderFrameIndex = 0;
|
||||
|
||||
currentDirectionCache = directionCache.FirstOrDefault(x => x.MobMode == MobMode && x.Direction == directionConversion[LocationDetails.MovementDirection]);
|
||||
currentDirectionCache = directionCache.FirstOrDefault(x => x.MobMode == MobMode && x.Direction == directionConversion[MobLocation.MovementDirection]);
|
||||
if (currentDirectionCache != null)
|
||||
return;
|
||||
|
||||
@ -147,7 +177,7 @@ namespace OpenDiablo2.SDL2_
|
||||
var directionCache = new DirectionCacheItem
|
||||
{
|
||||
MobMode = MobMode,
|
||||
Direction = directionConversion[LocationDetails.MovementDirection]
|
||||
Direction = directionConversion[MobLocation.MovementDirection]
|
||||
};
|
||||
|
||||
var palette = paletteProvider.PaletteTable[Palettes.Units];
|
||||
@ -170,10 +200,10 @@ namespace OpenDiablo2.SDL2_
|
||||
continue;
|
||||
}
|
||||
|
||||
minX = Math.Min(minX, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Left);
|
||||
minY = Math.Min(minY, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Top);
|
||||
maxX = Math.Max(maxX, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Right);
|
||||
maxY = Math.Max(maxY, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Bottom);
|
||||
minX = Math.Min(minX, layer.Directions[directionConversion[MobLocation.MovementDirection]].Box.Left);
|
||||
minY = Math.Min(minY, layer.Directions[directionConversion[MobLocation.MovementDirection]].Box.Top);
|
||||
maxX = Math.Max(maxX, layer.Directions[directionConversion[MobLocation.MovementDirection]].Box.Right);
|
||||
maxY = Math.Max(maxY, layer.Directions[directionConversion[MobLocation.MovementDirection]].Box.Bottom);
|
||||
}
|
||||
|
||||
if (layersIgnored > 0)
|
||||
@ -192,7 +222,7 @@ namespace OpenDiablo2.SDL2_
|
||||
SDL.SDL_LockTexture(texture, IntPtr.Zero, out var pixels, out var pitch);
|
||||
var data = (UInt32*)pixels;
|
||||
|
||||
var priorityBase = (directionConversion[LocationDetails.MovementDirection] * animationData.FramesPerDirection * animationData.NumberOfLayers)
|
||||
var priorityBase = (directionConversion[MobLocation.MovementDirection] * animationData.FramesPerDirection * animationData.NumberOfLayers)
|
||||
+ (frameIndex * animationData.NumberOfLayers);
|
||||
for (var i = 0; i < animationData.NumberOfLayers; i++)
|
||||
{
|
||||
@ -205,7 +235,7 @@ namespace OpenDiablo2.SDL2_
|
||||
if (layer == null)
|
||||
continue; // TODO: This is most likely not ok
|
||||
|
||||
var direction = layer.Directions[directionConversion[LocationDetails.MovementDirection]];
|
||||
var direction = layer.Directions[directionConversion[MobLocation.MovementDirection]];
|
||||
var frame = direction.Frames[frameIndex];
|
||||
|
||||
|
||||
|
@ -61,7 +61,7 @@ namespace OpenDiablo2.SDL2_
|
||||
private readonly IResourceManager resourceManager;
|
||||
private readonly GlobalConfiguration globalConfig;
|
||||
private readonly Func<IGameState> getGameState;
|
||||
private readonly Func<IMapEngine> getMapEngine;
|
||||
private readonly Func<IMapRenderer> getMapRenderer;
|
||||
|
||||
public SDL2RenderWindow(
|
||||
GlobalConfiguration globalConfig,
|
||||
@ -69,7 +69,7 @@ namespace OpenDiablo2.SDL2_
|
||||
IPaletteProvider paletteProvider,
|
||||
IResourceManager resourceManager,
|
||||
Func<IGameState> getGameState,
|
||||
Func<IMapEngine> getMapEngine
|
||||
Func<IMapRenderer> getMapEngine
|
||||
)
|
||||
{
|
||||
this.globalConfig = globalConfig;
|
||||
@ -77,7 +77,7 @@ namespace OpenDiablo2.SDL2_
|
||||
this.paletteProvider = paletteProvider;
|
||||
this.resourceManager = resourceManager;
|
||||
this.getGameState = getGameState;
|
||||
this.getMapEngine = getMapEngine;
|
||||
this.getMapRenderer = getMapEngine;
|
||||
this.fullscreen = globalConfig.FullScreen;
|
||||
|
||||
SDL.SDL_Init(SDL.SDL_INIT_EVERYTHING);
|
||||
@ -482,6 +482,6 @@ namespace OpenDiablo2.SDL2_
|
||||
public uint GetTicks() => SDL.SDL_GetTicks();
|
||||
|
||||
public ICharacterRenderer CreateCharacterRenderer()
|
||||
=> new SDL2CharacterRenderer(this.renderer, resourceManager, paletteProvider);
|
||||
=> new SDL2CharacterRenderer(this.renderer, resourceManager, paletteProvider, getGameState());
|
||||
}
|
||||
}
|
||||
|
@ -19,14 +19,17 @@ using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenDiablo2.Scenes
|
||||
{
|
||||
[Scene(eSceneType.Game)]
|
||||
public sealed class Game : IScene
|
||||
{
|
||||
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private readonly IRenderWindow renderWindow;
|
||||
private readonly IMapEngine mapEngine;
|
||||
private readonly IMapRenderer _mapRenderer;
|
||||
private readonly IMouseInfoProvider mouseInfoProvider;
|
||||
private readonly IGameState gameState;
|
||||
private readonly ISessionManager sessionManager;
|
||||
@ -35,12 +38,12 @@ namespace OpenDiablo2.Scenes
|
||||
private eMovementType lastMovementType = eMovementType.Stopped;
|
||||
private byte lastDirection = 255;
|
||||
private bool clickedOnHud = false;
|
||||
|
||||
const double Rad2Deg = 180.0 / Math.PI;
|
||||
private float lastMoveSend = 0f;
|
||||
private float holdMoveTime = 0f;
|
||||
|
||||
public Game(
|
||||
IRenderWindow renderWindow,
|
||||
IMapEngine mapEngine,
|
||||
IMapRenderer mapRenderer,
|
||||
IGameState gameState,
|
||||
IMouseInfoProvider mouseInfoProvider,
|
||||
IItemManager itemManager,
|
||||
@ -51,7 +54,7 @@ namespace OpenDiablo2.Scenes
|
||||
)
|
||||
{
|
||||
this.renderWindow = renderWindow;
|
||||
this.mapEngine = mapEngine;
|
||||
this._mapRenderer = mapRenderer;
|
||||
this.gameState = gameState;
|
||||
this.mouseInfoProvider = mouseInfoProvider;
|
||||
this.sessionManager = sessionManager;
|
||||
@ -63,23 +66,26 @@ namespace OpenDiablo2.Scenes
|
||||
public void Render()
|
||||
{
|
||||
// TODO: Maybe show some sort of connecting/loading message?
|
||||
if (mapEngine.FocusedPlayerId == 0)
|
||||
if (_mapRenderer.FocusedPlayerId == Guid.Empty)
|
||||
return;
|
||||
|
||||
mapEngine.Render();
|
||||
_mapRenderer.Render();
|
||||
gameHUD.Render();
|
||||
}
|
||||
|
||||
public void Update(long ms)
|
||||
{
|
||||
HandleMovement();
|
||||
HandleMovement(ms);
|
||||
|
||||
mapEngine.Update(ms);
|
||||
_mapRenderer.Update(ms);
|
||||
gameHUD.Update();
|
||||
}
|
||||
|
||||
private void HandleMovement()
|
||||
private void HandleMovement(long ms)
|
||||
{
|
||||
if (mouseInfoProvider.ReserveMouse)
|
||||
return;
|
||||
|
||||
if(gameHUD.IsMouseOver() && lastMovementType == eMovementType.Stopped)
|
||||
clickedOnHud = true;
|
||||
else if (!mouseInfoProvider.LeftMouseDown)
|
||||
@ -88,11 +94,40 @@ namespace OpenDiablo2.Scenes
|
||||
if (clickedOnHud)
|
||||
return;
|
||||
|
||||
/*
|
||||
var mx = mouseInfoProvider.MouseX - 400 - gameState.CameraOffset;
|
||||
var my = mouseInfoProvider.MouseY - 300;
|
||||
|
||||
var tx = (mx / 60f + my / 40f) / 2f;
|
||||
var ty = (my / 40f - (mx / 60f)) / 2f;
|
||||
*/
|
||||
|
||||
if (mouseInfoProvider.LeftMouseDown)
|
||||
{
|
||||
lastMoveSend += (ms / 1000f);
|
||||
holdMoveTime += (ms / 1000f);
|
||||
if (lastMoveSend < .25f)
|
||||
return;
|
||||
lastMoveSend = 0f;
|
||||
var selectedCell = _mapRenderer.GetCellPositionAt(mouseInfoProvider.MouseX, mouseInfoProvider.MouseY);
|
||||
#if DEBUG
|
||||
log.Debug($"Move to cell: ({selectedCell.X}, {selectedCell.Y})");
|
||||
#endif
|
||||
sessionManager.MoveRequest(selectedCell, gameHUD.IsRunningEnabled ? eMovementType.Running : eMovementType.Walking);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastMoveSend = 1f; // Next click will always send a mouse move request (TODO: Should this be limited as well?)
|
||||
|
||||
if (holdMoveTime > 0.2f)
|
||||
{
|
||||
var player = gameState.PlayerInfos.First(x => x.UID == _mapRenderer.FocusedPlayerId);
|
||||
sessionManager.MoveRequest(new System.Drawing.PointF(player.X, player.Y), eMovementType.Stopped);
|
||||
}
|
||||
holdMoveTime = 0f;
|
||||
}
|
||||
|
||||
/*
|
||||
var cursorDirection = (int)Math.Round(Math.Atan2(ty, tx) * Rad2Deg);
|
||||
if (cursorDirection < 0)
|
||||
cursorDirection += 360;
|
||||
@ -112,6 +147,7 @@ namespace OpenDiablo2.Scenes
|
||||
lastMovementType = eMovementType.Stopped;
|
||||
sessionManager.MoveRequest(actualDirection, lastMovementType);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -1,13 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Exceptions;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenDiablo2.ServiceBus.Message_Frames.Client
|
||||
{
|
||||
@ -17,28 +11,16 @@ namespace OpenDiablo2.ServiceBus.Message_Frames.Client
|
||||
public string PlayerName { get; set; }
|
||||
public eHero HeroType { get; set; }
|
||||
|
||||
public byte[] Data
|
||||
public void WriteTo(BinaryWriter bw)
|
||||
{
|
||||
get
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new BinaryWriter(stream)) {
|
||||
writer.Write((byte)HeroType);
|
||||
writer.Write(PlayerName);
|
||||
bw.Write((byte)HeroType);
|
||||
bw.Write(PlayerName);
|
||||
}
|
||||
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
using(var stream = new MemoryStream(value))
|
||||
using(var reader = new BinaryReader(stream))
|
||||
{
|
||||
HeroType = (eHero)reader.ReadByte();
|
||||
PlayerName = reader.ReadString();
|
||||
}
|
||||
}
|
||||
public void LoadFrom(BinaryReader br)
|
||||
{
|
||||
HeroType = (eHero)br.ReadByte();
|
||||
PlayerName = br.ReadString();
|
||||
}
|
||||
|
||||
public MFJoinGame() { }
|
||||
|
@ -1,34 +1,44 @@
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenDiablo2.ServiceBus.Message_Frames.Client
|
||||
{
|
||||
[MessageFrame(eMessageFrameType.MoveRequest)]
|
||||
public sealed class MFMoveRequest : IMessageFrame
|
||||
{
|
||||
public byte Direction { get; set; } = 0;
|
||||
public PointF Target { get; set; }
|
||||
public eMovementType MovementType { get; set; } = eMovementType.Stopped;
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get => new byte[] { Direction, (byte)MovementType };
|
||||
set
|
||||
{
|
||||
Direction = value[0];
|
||||
MovementType = (eMovementType)value[1];
|
||||
}
|
||||
}
|
||||
|
||||
public MFMoveRequest() { }
|
||||
|
||||
public MFMoveRequest(byte direction, eMovementType movementType)
|
||||
public MFMoveRequest(PointF targetCell, eMovementType movementType)
|
||||
{
|
||||
this.Direction = direction;
|
||||
this.Target = targetCell;
|
||||
this.MovementType = movementType;
|
||||
}
|
||||
|
||||
public void LoadFrom(BinaryReader br)
|
||||
{
|
||||
MovementType = (eMovementType)br.ReadByte();
|
||||
Target = new PointF
|
||||
{
|
||||
X = br.ReadSingle(),
|
||||
Y = br.ReadSingle()
|
||||
};
|
||||
}
|
||||
|
||||
public void WriteTo(BinaryWriter bw)
|
||||
{
|
||||
bw.Write((byte)MovementType);
|
||||
bw.Write((Single)Target.X);
|
||||
bw.Write((Single)Target.Y);
|
||||
}
|
||||
|
||||
public void Process(int clientHash, ISessionEventProvider sessionEventProvider)
|
||||
=> sessionEventProvider.OnMoveRequest(clientHash, Direction, MovementType);
|
||||
=> sessionEventProvider.OnMoveRequest(clientHash, Target, MovementType);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
@ -8,20 +9,24 @@ namespace OpenDiablo2.ServiceBus.Message_Frames.Server
|
||||
[MessageFrame(eMessageFrameType.FocusOnPlayer)]
|
||||
public sealed class MFFocusOnPlayer : IMessageFrame
|
||||
{
|
||||
public int PlayerToFocusOn { get; set; } = 0;
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get => BitConverter.GetBytes(PlayerToFocusOn);
|
||||
set => PlayerToFocusOn = BitConverter.ToInt32(value, 0);
|
||||
}
|
||||
public Guid PlayerToFocusOn { get; set; } = Guid.Empty;
|
||||
|
||||
public MFFocusOnPlayer() { }
|
||||
public MFFocusOnPlayer(int playerId)
|
||||
public MFFocusOnPlayer(Guid playerId)
|
||||
{
|
||||
this.PlayerToFocusOn = playerId;
|
||||
}
|
||||
|
||||
public void LoadFrom(BinaryReader br)
|
||||
{
|
||||
PlayerToFocusOn = new Guid(br.ReadBytes(16));
|
||||
}
|
||||
|
||||
public void WriteTo(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(PlayerToFocusOn.ToByteArray());
|
||||
}
|
||||
|
||||
public void Process(int clientHash, ISessionEventProvider sessionEventProvider)
|
||||
=> sessionEventProvider.OnFocusOnPlayer(clientHash, PlayerToFocusOn);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
@ -11,34 +12,30 @@ namespace OpenDiablo2.ServiceBus.Message_Frames.Server
|
||||
[MessageFrame(eMessageFrameType.LocatePlayers)]
|
||||
public sealed class MFLocatePlayers : IMessageFrame
|
||||
{
|
||||
public IEnumerable<PlayerLocationDetails> LocationDetails { get; set; }
|
||||
public IEnumerable<LocationDetails> LocationDetails { get; set; }
|
||||
|
||||
public byte[] Data
|
||||
public void LoadFrom(BinaryReader br)
|
||||
{
|
||||
get
|
||||
var count = br.ReadUInt16();
|
||||
var result = new List<LocationDetails>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var result = new List<byte>();
|
||||
result.AddRange(BitConverter.GetBytes((UInt16)LocationDetails.Count()));
|
||||
result.AddRange(LocationDetails.SelectMany(x => x.GetBytes()));
|
||||
return result.ToArray();
|
||||
result.Add(Common.Models.LocationDetails.FromBytes(br));
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
var count = BitConverter.ToUInt16(value, 0);
|
||||
var result = new List<PlayerLocationDetails>();
|
||||
|
||||
for(var i = 0; i < count; i++)
|
||||
{
|
||||
result.Add(PlayerLocationDetails.FromBytes(value, 2 + (i * PlayerLocationDetails.SizeInBytes)));
|
||||
}
|
||||
LocationDetails = result;
|
||||
}
|
||||
|
||||
LocationDetails = result;
|
||||
}
|
||||
public void WriteTo(BinaryWriter bw)
|
||||
{
|
||||
bw.Write((UInt16)LocationDetails.Count());
|
||||
foreach (var locationDetail in LocationDetails)
|
||||
locationDetail.WriteBytes(bw);
|
||||
}
|
||||
|
||||
public MFLocatePlayers() { }
|
||||
public MFLocatePlayers(IEnumerable<PlayerLocationDetails> locationDetails)
|
||||
public MFLocatePlayers(IEnumerable<LocationDetails> locationDetails)
|
||||
{
|
||||
this.LocationDetails = locationDetails;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
@ -13,32 +14,24 @@ namespace OpenDiablo2.ServiceBus.Message_Frames.Server
|
||||
{
|
||||
public IEnumerable<PlayerInfo> PlayerInfos { get; set; } = new List<PlayerInfo>();
|
||||
|
||||
public byte[] Data
|
||||
public void LoadFrom(BinaryReader br)
|
||||
{
|
||||
get
|
||||
{
|
||||
var result = BitConverter.GetBytes(PlayerInfos.Count())
|
||||
.Concat(PlayerInfos.SelectMany(x => x.GetBytes()))
|
||||
.ToArray();
|
||||
return result;
|
||||
}
|
||||
var count = br.ReadUInt16();
|
||||
var playerInfos = new PlayerInfo[count];
|
||||
for (var i = 0; i < count; i++)
|
||||
playerInfos[i] = PlayerInfo.FromBytes(br);
|
||||
|
||||
set
|
||||
{
|
||||
var count = BitConverter.ToInt32(value, 0);
|
||||
var playerInfos = new List<PlayerInfo>();
|
||||
var offset = 4;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var playerInfo = PlayerInfo.FromBytes(value, offset);
|
||||
playerInfos.Add(playerInfo);
|
||||
offset += playerInfo.SizeInBytes;
|
||||
}
|
||||
|
||||
PlayerInfos = playerInfos;
|
||||
}
|
||||
PlayerInfos = playerInfos;
|
||||
}
|
||||
|
||||
public void WriteTo(BinaryWriter bw)
|
||||
{
|
||||
bw.Write((UInt16)PlayerInfos.Count());
|
||||
foreach (var playerInfo in PlayerInfos)
|
||||
playerInfo.WriteBytes(bw);
|
||||
}
|
||||
|
||||
|
||||
public MFPlayerInfo() { }
|
||||
public MFPlayerInfo(IEnumerable<PlayerInfo> playerInfo)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
@ -15,6 +16,16 @@ namespace OpenDiablo2.ServiceBus.Message_Frames.Server
|
||||
set => Seed = BitConverter.ToInt32(value, 0);
|
||||
}
|
||||
|
||||
public void LoadFrom(BinaryReader br)
|
||||
{
|
||||
Seed = br.ReadInt32();
|
||||
}
|
||||
|
||||
public void WriteTo(BinaryWriter bw)
|
||||
{
|
||||
bw.Write((Int32)Seed);
|
||||
}
|
||||
|
||||
public Int32 Seed { get; private set; }
|
||||
|
||||
public MFSetSeed()
|
||||
|
@ -48,6 +48,7 @@
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -120,7 +122,13 @@ namespace OpenDiablo2.ServiceBus
|
||||
public void Send(IMessageFrame messageFrame, bool more = false)
|
||||
{
|
||||
var attr = messageFrame.GetType().GetCustomAttributes(true).First(x => (x is MessageFrameAttribute)) as MessageFrameAttribute;
|
||||
requestSocket.SendFrame(new byte[] { (byte)attr.FrameType }.Concat(messageFrame.Data).ToArray(), more);
|
||||
using (var ms = new MemoryStream())
|
||||
using (var bw = new BinaryWriter(ms))
|
||||
{
|
||||
bw.Write((byte)attr.FrameType);
|
||||
messageFrame.WriteTo(bw);
|
||||
requestSocket.SendFrame(ms.ToArray(), more);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessMessageFrame<T>() where T : IMessageFrame, new()
|
||||
@ -128,17 +136,18 @@ namespace OpenDiablo2.ServiceBus
|
||||
if (!running)
|
||||
throw new OpenDiablo2Exception("You have made a terrible mistake. Cannot get a message frame if you are not connected.");
|
||||
|
||||
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))
|
||||
throw new OpenDiablo2Exception("Recieved unexpected message frame!");
|
||||
messageFrame.Data = frameData;
|
||||
lock (getGameState().ThreadLocker)
|
||||
using (var ms = new MemoryStream(requestSocket.ReceiveFrameBytes()))
|
||||
using (var br = new BinaryReader(ms))
|
||||
{
|
||||
messageFrame.Process(requestSocket.GetHashCode(), this);
|
||||
var messageFrame = getMessageFrame((eMessageFrameType)br.ReadByte());
|
||||
|
||||
if (messageFrame.GetType() != typeof(T))
|
||||
throw new OpenDiablo2Exception("Recieved unexpected message frame!");
|
||||
|
||||
messageFrame.LoadFrom(br);
|
||||
|
||||
lock (getGameState().ThreadLocker)
|
||||
messageFrame.Process(requestSocket.GetHashCode(), this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,10 +170,10 @@ namespace OpenDiablo2.ServiceBus
|
||||
});
|
||||
}
|
||||
|
||||
public void MoveRequest(byte direction, eMovementType movementType)
|
||||
public void MoveRequest(PointF targetCell, eMovementType movementType)
|
||||
=> Task.Run(() =>
|
||||
{
|
||||
Send(new MFMoveRequest(direction, movementType));
|
||||
Send(new MFMoveRequest(targetCell, movementType));
|
||||
ProcessMessageFrame<MFLocatePlayers>();
|
||||
});
|
||||
}
|
||||
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -25,6 +28,7 @@ using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Exceptions;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Models;
|
||||
using OpenDiablo2.Common.Models.Mobs;
|
||||
using OpenDiablo2.ServiceBus.Message_Frames.Server;
|
||||
|
||||
namespace OpenDiablo2.ServiceBus
|
||||
@ -54,7 +58,7 @@ namespace OpenDiablo2.ServiceBus
|
||||
const int serverUpdateRate = 30;
|
||||
|
||||
public SessionServer(
|
||||
eSessionType sessionType,
|
||||
eSessionType sessionType,
|
||||
IGameServer gameServer,
|
||||
Func<eMessageFrameType, IMessageFrame> getMessageFrame
|
||||
)
|
||||
@ -93,19 +97,23 @@ namespace OpenDiablo2.ServiceBus
|
||||
|
||||
var proactor = new NetMQProactor(responseSocket, (socket, message) =>
|
||||
{
|
||||
var bytes = message.First().ToByteArray();
|
||||
var frameType = (eMessageFrameType)bytes[0];
|
||||
var frameData = bytes.Skip(1).ToArray(); // TODO: Can we maybe use pointers? This seems wasteful
|
||||
var messageFrame = getMessageFrame(frameType);
|
||||
messageFrame.Data = frameData;
|
||||
messageFrame.Process(socket.GetHashCode(), this);
|
||||
foreach (var msg in message)
|
||||
{
|
||||
using (var ms = new MemoryStream(msg.ToByteArray()))
|
||||
using (var br = new BinaryReader(ms))
|
||||
{
|
||||
var messageFrame = getMessageFrame((eMessageFrameType)br.ReadByte());
|
||||
messageFrame.LoadFrom(br);
|
||||
messageFrame.Process(socket.GetHashCode(), this);
|
||||
}
|
||||
}
|
||||
});
|
||||
running = true;
|
||||
WaitServerStartEvent.Set();
|
||||
Task.Run(() =>
|
||||
{
|
||||
var lastRun = DateTime.Now;
|
||||
while(running)
|
||||
while (running)
|
||||
{
|
||||
var newTime = DateTime.Now;
|
||||
var timeDiff = (newTime - lastRun).TotalMilliseconds;
|
||||
@ -122,22 +130,64 @@ namespace OpenDiablo2.ServiceBus
|
||||
responseSocket.Dispose();
|
||||
log.Info("Session server has stopped.");
|
||||
}
|
||||
|
||||
|
||||
private void OnMovementRequestHandler(int clientHash, byte direction, eMovementType movementType)
|
||||
|
||||
private void OnMovementRequestHandler(int clientHash, PointF targetCell, eMovementType movementType)
|
||||
{
|
||||
var player = gameServer.Players.FirstOrDefault(x => x.ClientHash == clientHash);
|
||||
if (player == null)
|
||||
return;
|
||||
|
||||
player.MovementDirection = direction;
|
||||
player.MovementType = movementType;
|
||||
player.MovementDirection = direction;
|
||||
|
||||
player.MovementSpeed = (player.MovementType == eMovementType.Running ? player.GetRunVelocity() : player.GetWalkVeloicty()) / 4f;
|
||||
player.Waypoints = CalculateWaypoints(player, targetCell);
|
||||
|
||||
Send(new MFLocatePlayers(gameServer.Players.Select(x => x.ToPlayerLocationDetails())));
|
||||
}
|
||||
|
||||
private List<PointF> CalculateWaypoints(PlayerState player, PointF targetCell)
|
||||
{
|
||||
// TODO: Move this somewhere else...
|
||||
var result = new List<PointF>();
|
||||
result.Add(targetCell);
|
||||
/*
|
||||
// Ensure they aren't sending crazy coordinates..
|
||||
var targetX = Math.Round(targetCell.X, 1);
|
||||
var targetY = Math.Round(targetCell.Y, 1);
|
||||
|
||||
// TODO: Legit Pathfind here...
|
||||
result.Add(new PointF(player.X, player.Y));
|
||||
int maxTries = 50;
|
||||
var curX = player.X;
|
||||
var curY = player.Y;
|
||||
var nextX = curX;
|
||||
var nextY = curY;
|
||||
while (--maxTries > 0)
|
||||
{
|
||||
if (curX < targetX)
|
||||
nextX += .1f;
|
||||
else if (curX > targetX)
|
||||
nextX -= .1f;
|
||||
|
||||
if (curY < targetY)
|
||||
nextY += .1f;
|
||||
else if (curY > targetY)
|
||||
nextY -= .1f;
|
||||
|
||||
result.Add(new PointF((float)Math.Round(nextX, 1), (float)Math.Round(nextY, 1)));
|
||||
|
||||
curX = nextX;
|
||||
curY = nextY;
|
||||
|
||||
// If we reached our target, stop here
|
||||
if (Math.Abs(curX - targetX) < 0.1f && Math.Abs(curY - targetY) < 0.1f)
|
||||
break;
|
||||
}
|
||||
|
||||
*/
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (!running)
|
||||
@ -159,7 +209,13 @@ namespace OpenDiablo2.ServiceBus
|
||||
private void Send(IMessageFrame messageFrame, bool more = false)
|
||||
{
|
||||
var attr = messageFrame.GetType().GetCustomAttributes(true).First(x => (x is MessageFrameAttribute)) as MessageFrameAttribute;
|
||||
responseSocket.SendFrame(new byte[] { (byte)attr.FrameType }.Concat(messageFrame.Data).ToArray(), more);
|
||||
using (var ms = new MemoryStream())
|
||||
using (var br = new BinaryWriter(ms))
|
||||
{
|
||||
br.Write((byte)attr.FrameType);
|
||||
messageFrame.WriteTo(br);
|
||||
responseSocket.SendFrame(ms.ToArray(), more);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnJoinGameHandler(int clientHash, eHero heroType, string playerName)
|
||||
@ -168,7 +224,7 @@ namespace OpenDiablo2.ServiceBus
|
||||
Send(new MFSetSeed(gameServer.Seed), true);
|
||||
Send(new MFPlayerInfo(gameServer.Players.Select(x => x.ToPlayerInfo())), true);
|
||||
Send(new MFLocatePlayers(gameServer.Players.Select(x => x.ToPlayerLocationDetails())), true);
|
||||
Send(new MFFocusOnPlayer(gameServer.Players.First(x => x.ClientHash == clientHash).Id));
|
||||
Send(new MFFocusOnPlayer(gameServer.Players.First(x => x.ClientHash == clientHash).UID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user