mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-20 23:47:16 -05:00
Fixed animation data issue. Started character renderer.
This commit is contained in:
parent
2f1f15c7b5
commit
3902c9d9c0
20
OpenDiablo2.Common/Interfaces/Drawing/ICharacterRenderer.cs
Normal file
20
OpenDiablo2.Common/Interfaces/Drawing/ICharacterRenderer.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces.Drawing
|
||||
{
|
||||
public interface ICharacterRenderer : IDisposable
|
||||
{
|
||||
Guid UID { get; set; }
|
||||
PlayerLocationDetails LocationDetails { get; set; }
|
||||
eHero Hero { get; set; }
|
||||
eWeaponClass WeaponClass { get; set; }
|
||||
eArmorType ArmorType { get; set; }
|
||||
eMobMode MobMode { get; set; }
|
||||
|
||||
void Update(long ms);
|
||||
void Render(int pixelOffsetX, int pixelOffsetY);
|
||||
void ResetAnimationData();
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenDiablo2.Common.Interfaces.Drawing;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
@ -29,5 +30,6 @@ namespace OpenDiablo2.Common.Interfaces
|
||||
void Draw(ILabel label);
|
||||
MapCellInfo CacheMapCell(MPQDT1Tile mapCell);
|
||||
void DrawMapCell(MapCellInfo mapCellInfo, int xPixel, int yPixel);
|
||||
ICharacterRenderer CreateCharacterRenderer();
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,9 @@ namespace OpenDiablo2.Common.Models
|
||||
this.data = source.data;
|
||||
this.Offset = source.Offset;
|
||||
}
|
||||
public int GetBit()
|
||||
public UInt32 GetBit()
|
||||
{
|
||||
var result = (data[Offset / 8] >> (Offset % 8)) & 0x01;
|
||||
var result = (UInt32)(data[Offset / 8] >> (Offset % 8)) & 0x01;
|
||||
Offset++;
|
||||
BitsRead++;
|
||||
return result;
|
||||
@ -33,10 +33,14 @@ namespace OpenDiablo2.Common.Models
|
||||
|
||||
public byte GetByte() => (byte)GetBits(8);
|
||||
public Int32 GetInt32() => MakeSigned(GetBits(32), 32);
|
||||
public UInt32 GetUInt32() => GetBits(32);
|
||||
|
||||
public int GetBits(int bits)
|
||||
public UInt32 GetBits(int bits)
|
||||
{
|
||||
var result = 0;
|
||||
if (bits == 0)
|
||||
return 0;
|
||||
|
||||
var result = 0U;
|
||||
for (var i = 0; i < bits; i++)
|
||||
result |= (GetBit() << i);
|
||||
|
||||
@ -45,14 +49,21 @@ namespace OpenDiablo2.Common.Models
|
||||
|
||||
public int GetSignedBits(int bits) => MakeSigned(GetBits(bits), bits);
|
||||
|
||||
private int MakeSigned(int value, int bits)
|
||||
|
||||
private Int32 MakeSigned(UInt32 value, int bits)
|
||||
{
|
||||
if (bits == 0)
|
||||
return 0;
|
||||
// If its a single bit, a value of 1 is -1 automagically
|
||||
if (bits == 1)
|
||||
return -value;
|
||||
return -(Int32)value;
|
||||
|
||||
// If there is no sign bit, return the value as is
|
||||
if ((value & (1 << (bits - 1))) == 0)
|
||||
return value;
|
||||
return (Int32)value;
|
||||
|
||||
// We need to extend the signed bit out so that the negative value
|
||||
// representation still works with the 2s compliment rule.
|
||||
var result = UInt32.MaxValue;
|
||||
for (byte i = 0; i < bits; i++)
|
||||
{
|
||||
@ -60,8 +71,7 @@ namespace OpenDiablo2.Common.Models
|
||||
result -= (UInt32)(1 << i);
|
||||
}
|
||||
|
||||
var newResult = (int)result;
|
||||
return newResult;
|
||||
return (Int32)result; // Force casting to a signed value
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,10 @@ namespace OpenDiablo2.Common.Models
|
||||
public eWeaponClass WeaponClass { get; internal set; }
|
||||
|
||||
public string GetDCCPath(eArmorType armorType)
|
||||
=> $"{ResourcePaths.PlayerAnimationBase}\\{COF.Hero.ToToken()}\\{CompositType.ToToken()}\\{COF.Hero.ToToken()}{CompositType.ToToken()}{armorType.ToToken()}{COF.MobMode.ToToken()}{COF.WeaponClass.ToToken()}.dcc";
|
||||
{
|
||||
var result = $"{ResourcePaths.PlayerAnimationBase}\\{COF.Hero.ToToken()}\\{CompositType.ToToken()}\\{COF.Hero.ToToken()}{CompositType.ToToken()}{armorType.ToToken()}{COF.MobMode.ToToken()}{COF.WeaponClass.ToToken()}.dcc";
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -42,12 +42,12 @@ namespace OpenDiablo2.Common.Models
|
||||
public MPQDCCDirectionFrame(BitMuncher bits, MPQDCCDirection direction)
|
||||
{
|
||||
var variable0 = bits.GetBits(direction.Variable0Bits);
|
||||
Width = bits.GetBits(direction.WidthBits);
|
||||
Height = bits.GetBits(direction.HeightBits);
|
||||
Width = (int)bits.GetBits(direction.WidthBits);
|
||||
Height = (int)bits.GetBits(direction.HeightBits);
|
||||
XOffset = bits.GetSignedBits(direction.XOffsetBits);
|
||||
YOffset = bits.GetSignedBits(direction.YOffsetBits);
|
||||
NumberOfOptionalBytes = bits.GetBits(direction.OptionalDataBits);
|
||||
NumberOfCodedBytes = bits.GetBits(direction.CodedBytesBits);
|
||||
NumberOfOptionalBytes = (int)bits.GetBits(direction.OptionalDataBits);
|
||||
NumberOfCodedBytes = (int)bits.GetBits(direction.CodedBytesBits);
|
||||
FrameIsBottomUp = bits.GetBit() == 1;
|
||||
|
||||
Box = new Rectangle(
|
||||
@ -131,7 +131,7 @@ namespace OpenDiablo2.Common.Models
|
||||
}
|
||||
public sealed class MPQDCCDirection
|
||||
{
|
||||
const int DCC_MAX_PB_ENTRY = 85000; // But why is this the magic number?
|
||||
const int DCC_MAX_PB_ENTRY = 8500; // But why is this the magic number?
|
||||
public int OutSizeCoded { get; private set; }
|
||||
public int CompressionFlags { get; private set; }
|
||||
public int Variable0Bits { get; private set; }
|
||||
@ -146,7 +146,7 @@ namespace OpenDiablo2.Common.Models
|
||||
public int EncodingTypeBitsreamSize { get; private set; }
|
||||
public int RawPixelCodesBitstreamSize { get; private set; }
|
||||
public MPQDCCDirectionFrame[] Frames { get; private set; }
|
||||
public int[] PaletteEntries { get; private set; }
|
||||
public byte[] PaletteEntries { get; private set; }
|
||||
public Rectangle Box { get; private set; }
|
||||
public Cell[] Cells { get; private set; }
|
||||
public int HorizontalCellCount { get; private set; }
|
||||
@ -156,8 +156,8 @@ namespace OpenDiablo2.Common.Models
|
||||
private static readonly byte[] crazyBitTable = { 0, 1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 26, 28, 30, 32 };
|
||||
public MPQDCCDirection(BitMuncher bm, MPQDCC file)
|
||||
{
|
||||
OutSizeCoded = bm.GetInt32();
|
||||
CompressionFlags = bm.GetBits(2);
|
||||
OutSizeCoded = (int)bm.GetUInt32();
|
||||
CompressionFlags = (int)bm.GetBits(2);
|
||||
Variable0Bits = crazyBitTable[bm.GetBits(4)];
|
||||
WidthBits = crazyBitTable[bm.GetBits(4)];
|
||||
HeightBits = crazyBitTable[bm.GetBits(4)];
|
||||
@ -168,30 +168,41 @@ namespace OpenDiablo2.Common.Models
|
||||
|
||||
Frames = new MPQDCCDirectionFrame[file.NumberOfFrames];
|
||||
|
||||
var minx = long.MaxValue;
|
||||
var miny = long.MaxValue;
|
||||
var maxx = long.MinValue;
|
||||
var maxy = long.MinValue;
|
||||
// Load the frame headers
|
||||
for (var frameIdx = 0; frameIdx < file.NumberOfFrames; frameIdx++)
|
||||
{
|
||||
Frames[frameIdx] = new MPQDCCDirectionFrame(bm, this);
|
||||
|
||||
minx = Math.Min(Frames[frameIdx].Box.X, minx);
|
||||
miny = Math.Min(Frames[frameIdx].Box.Y, miny);
|
||||
maxx = Math.Max(Frames[frameIdx].Box.Right, maxx);
|
||||
maxy = Math.Max(Frames[frameIdx].Box.Bottom, maxy);
|
||||
}
|
||||
|
||||
Box = new Rectangle
|
||||
{
|
||||
X = Frames.Min(z => z.Box.X),
|
||||
Y = Frames.Min(z => z.Box.Y),
|
||||
Width = Frames.Max(z => z.Box.Right - z.Box.Left),
|
||||
Height = Frames.Max(z => z.Box.Bottom - z.Box.Top)
|
||||
X = (int)minx,
|
||||
Y = (int)miny,
|
||||
Width = (int)(maxx - minx),
|
||||
Height = (int)(maxy - miny)
|
||||
};
|
||||
|
||||
if (OptionalDataBits > 0)
|
||||
throw new ApplicationException("Optional bits in DCC data is not currently supported.");
|
||||
|
||||
if ((CompressionFlags & 0x2) > 0)
|
||||
EqualCellsBitstreamSize = bm.GetBits(20);
|
||||
EqualCellsBitstreamSize = (int)bm.GetBits(20);
|
||||
|
||||
PixelMaskBitstreamSize = bm.GetBits(20);
|
||||
PixelMaskBitstreamSize = (int)bm.GetBits(20);
|
||||
|
||||
if ((CompressionFlags & 0x1) > 0)
|
||||
{
|
||||
EncodingTypeBitsreamSize = bm.GetBits(20);
|
||||
RawPixelCodesBitstreamSize = bm.GetBits(20);
|
||||
EncodingTypeBitsreamSize = (int)bm.GetBits(20);
|
||||
RawPixelCodesBitstreamSize = (int)bm.GetBits(20);
|
||||
}
|
||||
|
||||
|
||||
@ -200,17 +211,22 @@ namespace OpenDiablo2.Common.Models
|
||||
for (var i = 0; i < 256; i++)
|
||||
paletteEntries.Add(bm.GetBit() != 0);
|
||||
|
||||
PaletteEntries = new int[paletteEntries.Count(x => x == true)];
|
||||
PaletteEntries = new byte[paletteEntries.Count(x => x == true)];
|
||||
var paletteOffset = 0;
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
if (!paletteEntries[i])
|
||||
continue;
|
||||
|
||||
PaletteEntries[paletteOffset++] = i;
|
||||
PaletteEntries[paletteOffset++] = (byte)i;
|
||||
}
|
||||
|
||||
|
||||
// HERE BE GIANTS:
|
||||
// Because of the way this thing mashes bits together, BIT offset matters
|
||||
// here. For example, if you are on byte offset 3, bit offset 6, and
|
||||
// the EqualCellsBitstreamSize is 20 bytes, then the next bit stream
|
||||
// will be located at byte 23, bit offset 6!
|
||||
|
||||
var equalCellsBitstream = new BitMuncher(bm);
|
||||
bm.SkipBits(EqualCellsBitstreamSize);
|
||||
@ -245,7 +261,6 @@ namespace OpenDiablo2.Common.Models
|
||||
if (rawPixelCodesBitstream.BitsRead != RawPixelCodesBitstreamSize)
|
||||
throw new ApplicationException("Did not read the correct number of bits!");
|
||||
|
||||
|
||||
bm.SkipBits(pixelCodeandDisplacement.BitsRead);
|
||||
}
|
||||
|
||||
@ -258,7 +273,7 @@ namespace OpenDiablo2.Common.Models
|
||||
for (var i = 0; i < DCC_MAX_PB_ENTRY; i++)
|
||||
PixelBuffer[i] = new PixelBufferEntry { Frame = -1, FrameCellIndex = -1, Value = new byte[4] };
|
||||
|
||||
var cellBuffer = new PixelBufferEntry[Cells.Length];
|
||||
var cellBuffer = new PixelBufferEntry[HorizontalCellCount * VerticalCellCount];
|
||||
|
||||
var frameIndex = -1;
|
||||
var pbIndex = -1;
|
||||
@ -274,16 +289,18 @@ namespace OpenDiablo2.Common.Models
|
||||
var currentCellY = cellY + originCellY;
|
||||
for (var cellX = 0; cellX < frame.HorizontalCellCount; cellX++, frameCellIndex++)
|
||||
{
|
||||
var currentCell = (originCellX + cellX) + (currentCellY * frame.HorizontalCellCount);
|
||||
var currentCell = (originCellX + cellX) + (currentCellY * HorizontalCellCount);
|
||||
var nextCell = false;
|
||||
var tmp = 0;
|
||||
if (cellBuffer[currentCell] != null)
|
||||
{
|
||||
if (EqualCellsBitstreamSize > 0)
|
||||
tmp = ec.GetBit();
|
||||
tmp = (int)ec.GetBit();
|
||||
else
|
||||
tmp = 0;
|
||||
|
||||
if (tmp == 0)
|
||||
pixelMask = (UInt32)pm.GetBits(4);
|
||||
pixelMask = pm.GetBits(4);
|
||||
else
|
||||
nextCell = true;
|
||||
}
|
||||
@ -296,25 +313,28 @@ namespace OpenDiablo2.Common.Models
|
||||
var pixelStack = new UInt32[4];
|
||||
lastPixel = 0;
|
||||
int numberOfPixelBits = pixelMaskLookup[pixelMask];
|
||||
var encodingType = ((numberOfPixelBits != 0) && (EncodingTypeBitsreamSize > 0))
|
||||
? et.GetBit()
|
||||
: 0;
|
||||
int encodingType = 0;
|
||||
if ((numberOfPixelBits != 0) && (EncodingTypeBitsreamSize > 0))
|
||||
encodingType = (int)et.GetBit();
|
||||
else
|
||||
encodingType = 0;
|
||||
|
||||
int decodedPixel = 0;
|
||||
for (int i = 0; i < numberOfPixelBits; i++)
|
||||
{
|
||||
if (encodingType != 0)
|
||||
{
|
||||
pixelStack[i] = (UInt32)rp.GetBits(8);
|
||||
pixelStack[i] = rp.GetBits(8);
|
||||
}
|
||||
else
|
||||
{
|
||||
pixelStack[i] = lastPixel;
|
||||
var pixelDisplacement = pcd.GetBits(4);
|
||||
pixelStack[i] += (UInt32)pixelDisplacement;
|
||||
pixelStack[i] += pixelDisplacement;
|
||||
while (pixelDisplacement == 15)
|
||||
{
|
||||
pixelDisplacement = pcd.GetBits(4);
|
||||
pixelStack[i] += (UInt32)pixelDisplacement;
|
||||
pixelStack[i] += pixelDisplacement;
|
||||
}
|
||||
}
|
||||
if (pixelStack[i] == lastPixel)
|
||||
@ -349,10 +369,21 @@ namespace OpenDiablo2.Common.Models
|
||||
|
||||
cellBuffer[currentCell] = newEntry;
|
||||
newEntry.Frame = frameIndex;
|
||||
newEntry.FrameCellIndex = cellX + (cellY * HorizontalCellCount);
|
||||
newEntry.FrameCellIndex = cellX + (cellY * frame.HorizontalCellCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Convert the palette entry index into actual palette entries
|
||||
for (var i = 0; i < pbIndex; i++)
|
||||
{
|
||||
for (var x = 0; x < 4; x++)
|
||||
{
|
||||
var y = PixelBuffer[i].Value[x];
|
||||
PixelBuffer[i].Value[x] = PaletteEntries[y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateCellOffsets()
|
||||
|
@ -7,12 +7,16 @@ namespace OpenDiablo2.Common.Models.Mobs
|
||||
{
|
||||
public class PlayerState : MobState
|
||||
{
|
||||
public Guid UID { get; protected set; } = Guid.NewGuid();
|
||||
public eHero HeroType { get; protected set; }
|
||||
private IHeroTypeConfig HeroTypeConfig;
|
||||
private ILevelExperienceConfig ExperienceConfig;
|
||||
public int ClientHash { get; protected set; }
|
||||
public byte MovementDirection { get; set; } = 0;
|
||||
public eMovementType MovementType { get; set; } = eMovementType.Stopped;
|
||||
public eMovementType MovementType { get; set; } = eMovementType.Stopped; // TODO: This needs to mess with MobMode somehow
|
||||
public eWeaponClass WeaponClass { get; set; } = eWeaponClass.HandToHand; // Temporary
|
||||
public eArmorType ArmorType { get; set; } = eArmorType.Lite; // Temporary
|
||||
public eMobMode MobMode { get; set; } = eMobMode.PlayerNeutral; // Temporary
|
||||
|
||||
// Player character stats
|
||||
protected Stat Vitality;
|
||||
|
@ -8,8 +8,12 @@ namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class PlayerInfo
|
||||
{
|
||||
public Guid UID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public eHero Hero { get; set; }
|
||||
public eWeaponClass WeaponClass { get; set; }
|
||||
public eArmorType ArmorType { get; set; }
|
||||
public eMobMode MobMode { get; set; }
|
||||
public PlayerLocationDetails LocationDetails { get; set; }
|
||||
|
||||
public byte[] GetBytes()
|
||||
@ -17,9 +21,13 @@ namespace OpenDiablo2.Common.Models
|
||||
var result = new List<byte>();
|
||||
var nameBytes = Encoding.UTF8.GetBytes(Name);
|
||||
result.Add((byte)Hero);
|
||||
result.Add((byte)WeaponClass);
|
||||
result.Add((byte)ArmorType);
|
||||
result.Add((byte)MobMode);
|
||||
result.AddRange(BitConverter.GetBytes((Int32)nameBytes.Length));
|
||||
result.AddRange(nameBytes);
|
||||
result.AddRange(LocationDetails.GetBytes());
|
||||
result.AddRange(UID.ToByteArray());
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
@ -27,9 +35,15 @@ namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
var result = new PlayerInfo();
|
||||
result.Hero = (eHero)data[offset];
|
||||
var nameLength = BitConverter.ToInt32(data, offset + 1);
|
||||
result.Name = Encoding.UTF8.GetString(data, offset + 5, nameLength);
|
||||
result.LocationDetails = PlayerLocationDetails.FromBytes(data, offset + 5 + nameLength);
|
||||
result.WeaponClass= (eWeaponClass)data[offset + 1];
|
||||
result.ArmorType = (eArmorType)data[offset + 2];
|
||||
result.MobMode = (eMobMode)data[offset + 3];
|
||||
var nameLength = BitConverter.ToInt32(data, offset + 4);
|
||||
result.Name = Encoding.UTF8.GetString(data, offset + 8, nameLength);
|
||||
result.LocationDetails = PlayerLocationDetails.FromBytes(data, offset + 8 + nameLength);
|
||||
var uidBytes = new byte[16];
|
||||
Array.Copy(data, offset + 8 + nameLength + PlayerLocationDetails.SizeInBytes + 1, uidBytes, 0, 16);
|
||||
result.UID = new Guid(uidBytes);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -39,17 +53,22 @@ namespace OpenDiablo2.Common.Models
|
||||
|
||||
public static class PlayerInfoExtensions
|
||||
{
|
||||
// Map the player state to a PlayerInfo network package object.
|
||||
public static PlayerInfo ToPlayerInfo(this PlayerState source)
|
||||
=> new PlayerInfo
|
||||
{
|
||||
UID = source.UID,
|
||||
Hero = source.HeroType,
|
||||
LocationDetails = new PlayerLocationDetails
|
||||
{
|
||||
PlayerId = source.Id,
|
||||
PlayerX = source.GetPosition().X,
|
||||
PlayerY = source.GetPosition().Y
|
||||
PlayerY = source.GetPosition().Y,
|
||||
},
|
||||
Name = source.Name
|
||||
Name = source.Name,
|
||||
WeaponClass = source.WeaponClass,
|
||||
ArmorType = source.ArmorType,
|
||||
MobMode = source.MobMode
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +78,7 @@
|
||||
<Compile Include="Enums\Mobs\eDamageTypes.cs" />
|
||||
<Compile Include="Enums\Mobs\eMobFlags.cs" />
|
||||
<Compile Include="Enums\Mobs\eStatModifierType.cs" />
|
||||
<Compile Include="Interfaces\Drawing\ICharacterRenderer.cs" />
|
||||
<Compile Include="Interfaces\IItemManager.cs" />
|
||||
<Compile Include="Extensions\MobManagerExtensions.cs" />
|
||||
<Compile Include="Interfaces\IGameServer.cs" />
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Autofac;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Drawing;
|
||||
using OpenDiablo2.Common.Interfaces.Mobs;
|
||||
using OpenDiablo2.Core.GameState_;
|
||||
using OpenDiablo2.Core.Map_Engine;
|
||||
@ -30,7 +31,6 @@ namespace OpenDiablo2.Core
|
||||
builder.RegisterType<ResourceManager>().As<IResourceManager>().SingleInstance();
|
||||
builder.RegisterType<TextDictionary>().As<ITextDictionary>().SingleInstance();
|
||||
builder.RegisterType<TextBox>().As<ITextBox>().InstancePerDependency();
|
||||
|
||||
builder.RegisterType<MobManager>().As<IMobManager>().SingleInstance(); // TODO: This needs to have client and server versions...
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,12 @@ namespace OpenDiablo2.Core
|
||||
public IEnumerable<MPQ> GetMPQs() => mpqs;
|
||||
|
||||
public Stream GetStream(string fileName)
|
||||
=> mpqs[mpqLookup[fileName.ToLower()]].OpenFile(fileName);
|
||||
{
|
||||
if (!mpqLookup.ContainsKey(fileName.ToLower()))
|
||||
return null;
|
||||
|
||||
return mpqs[mpqLookup[fileName.ToLower()]].OpenFile(fileName);
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<string> GetTextFile(string fileName)
|
||||
|
@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenDiablo2.Common;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Drawing;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
namespace OpenDiablo2.Core.Map_Engine
|
||||
{
|
||||
@ -12,6 +15,9 @@ namespace OpenDiablo2.Core.Map_Engine
|
||||
private readonly IGameState gameState;
|
||||
private readonly IRenderWindow renderWindow;
|
||||
private readonly IResourceManager resourceManager;
|
||||
private readonly ISessionManager sessionManager;
|
||||
|
||||
private List<ICharacterRenderer> characterRenderers = new List<ICharacterRenderer>();
|
||||
|
||||
public int FocusedPlayerId { get; set; } = 0;
|
||||
|
||||
@ -32,7 +38,6 @@ namespace OpenDiablo2.Core.Map_Engine
|
||||
|
||||
private ISprite loadingSprite;
|
||||
private int cOffX, cOffY;
|
||||
//private ISprite[] tempMapCell;
|
||||
|
||||
private const int
|
||||
cellSizeX = 160,
|
||||
@ -43,14 +48,63 @@ namespace OpenDiablo2.Core.Map_Engine
|
||||
public MapEngine(
|
||||
IGameState gameState,
|
||||
IRenderWindow renderWindow,
|
||||
IResourceManager resourceManager
|
||||
IResourceManager resourceManager,
|
||||
ISessionManager sessionManager
|
||||
)
|
||||
{
|
||||
this.gameState = gameState;
|
||||
this.renderWindow = renderWindow;
|
||||
this.resourceManager = resourceManager;
|
||||
this.sessionManager = sessionManager;
|
||||
|
||||
loadingSprite = renderWindow.LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading, new Point(300, 400));
|
||||
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);
|
||||
cr.LocationDetails = loc;
|
||||
}
|
||||
}
|
||||
|
||||
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.WeaponClass = info.WeaponClass;
|
||||
cr.Hero = info.Hero;
|
||||
cr.ArmorType = info.ArmorType;
|
||||
|
||||
}
|
||||
|
||||
// Add character renderers for characters that now exist
|
||||
foreach(var info in playerInfo.Where(x => !characterRenderers.Any(z => x.UID == z.UID)))
|
||||
{
|
||||
var cr = renderWindow.CreateCharacterRenderer();
|
||||
cr.UID = info.UID;
|
||||
cr.LocationDetails = info.LocationDetails;
|
||||
cr.MobMode = info.MobMode;
|
||||
cr.WeaponClass = info.WeaponClass;
|
||||
cr.Hero = info.Hero;
|
||||
cr.ArmorType = info.ArmorType;
|
||||
characterRenderers.Add(cr);
|
||||
cr.ResetAnimationData();
|
||||
}
|
||||
}
|
||||
|
||||
public void Render()
|
||||
@ -77,9 +131,15 @@ 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 + 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 + xOffset, 210 + (int)py + (int)oy);
|
||||
|
||||
// TODO: We need to render the characters infront of, or behind the wall properly...
|
||||
foreach (var character in characterRenderers.Where(x => Math.Truncate(x.LocationDetails.PlayerX) == ax && Math.Truncate(x.LocationDetails.PlayerY) == ay))
|
||||
character.Render(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 + xOffset, 210 + (int)py + (int)oy);
|
||||
|
||||
@ -94,6 +154,9 @@ namespace OpenDiablo2.Core.Map_Engine
|
||||
|
||||
public void Update(long ms)
|
||||
{
|
||||
foreach (var character in characterRenderers)
|
||||
character.Update(ms);
|
||||
|
||||
if (FocusedPlayerId != 0)
|
||||
{
|
||||
var player = gameState.PlayerInfos.FirstOrDefault(x => x.LocationDetails.PlayerId == FocusedPlayerId);
|
||||
|
@ -93,9 +93,14 @@ namespace OpenDiablo2.Core
|
||||
|
||||
public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette)
|
||||
{
|
||||
// TODO: We need to cache this...
|
||||
byte[] binaryData;
|
||||
|
||||
using (var stream = mpqProvider.GetStream(cofLayer.GetDCCPath(armorType)))
|
||||
{
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
binaryData = new byte[stream.Length];
|
||||
stream.Read(binaryData, 0, (int)stream.Length);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Autofac;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Drawing;
|
||||
|
||||
namespace OpenDiablo2.SDL2_
|
||||
{
|
||||
@ -13,7 +14,6 @@ namespace OpenDiablo2.SDL2_
|
||||
|
||||
builder.RegisterType<SDL2RenderWindow>().AsImplementedInterfaces().SingleInstance();
|
||||
builder.RegisterType<SDL2MusicPlayer>().AsImplementedInterfaces().SingleInstance();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AutofacModule.cs" />
|
||||
<Compile Include="SDL2CharacterRenderer.cs" />
|
||||
<Compile Include="CS-SDL\SDL2.cs" />
|
||||
<Compile Include="CS-SDL\SDL2_image.cs" />
|
||||
<Compile Include="CS-SDL\SDL2_mixer.cs" />
|
||||
|
106
OpenDiablo2.SDL2/SDL2CharacterRenderer.cs
Normal file
106
OpenDiablo2.SDL2/SDL2CharacterRenderer.cs
Normal file
@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Drawing;
|
||||
using OpenDiablo2.Common.Models;
|
||||
using SDL2;
|
||||
|
||||
namespace OpenDiablo2.SDL2_
|
||||
{
|
||||
public sealed class SDL2CharacterRenderer : ICharacterRenderer
|
||||
{
|
||||
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 eWeaponClass WeaponClass { get; set; }
|
||||
public eArmorType ArmorType { get; set; }
|
||||
public eMobMode MobMode { get; set; }
|
||||
|
||||
private IntPtr renderer;
|
||||
private IntPtr tempTexture;
|
||||
|
||||
private readonly IResourceManager resourceManager;
|
||||
private readonly IPaletteProvider paletteProvider;
|
||||
|
||||
private MPQCOF animationData;
|
||||
private MPQDCC[] layerData;
|
||||
|
||||
public SDL2CharacterRenderer(IntPtr renderer, IResourceManager resourceManager, IPaletteProvider paletteProvider)
|
||||
{
|
||||
this.resourceManager = resourceManager;
|
||||
this.paletteProvider = paletteProvider;
|
||||
this.renderer = renderer;
|
||||
}
|
||||
|
||||
public void Render(int pixelOffsetX, int pixelOffsetY)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Update(long ms)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void ResetAnimationData()
|
||||
{
|
||||
|
||||
animationData = resourceManager.GetPlayerAnimation(Hero, WeaponClass, MobMode);
|
||||
if (animationData == null)
|
||||
throw new ApplicationException("Could not locate animation for the character!");
|
||||
|
||||
var palette = paletteProvider.PaletteTable["Units"];
|
||||
var data = animationData.Layers
|
||||
.Select(layer => resourceManager.GetPlayerDCC(layer, ArmorType, palette))
|
||||
.ToArray();
|
||||
|
||||
log.Warn($"{data.Where(x => x == null).Count()} animation layers were not found!");
|
||||
|
||||
layerData = data.Where(x => x != null)
|
||||
.ToArray();
|
||||
|
||||
CacheFrames();
|
||||
}
|
||||
|
||||
private unsafe void CacheFrames()
|
||||
{
|
||||
var dirIndex = 0; // TODO: Specify the real direction
|
||||
var frameIndex = 0;
|
||||
|
||||
var dirAnimation = animationData.Animations[dirIndex];
|
||||
var framesToAnimate = dirAnimation.FramesPerDirection;
|
||||
|
||||
foreach (var layer in layerData)
|
||||
{
|
||||
var direction = layer.Directions[dirIndex];
|
||||
var frame = direction.Frames[0];
|
||||
var texture = SDL.SDL_CreateTexture(
|
||||
renderer,
|
||||
SDL.SDL_PIXELFORMAT_ARGB8888,
|
||||
(int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING,
|
||||
frame.Width,
|
||||
frame.Height
|
||||
);
|
||||
|
||||
|
||||
IntPtr pixels;
|
||||
int pitch;
|
||||
|
||||
SDL.SDL_LockTexture(texture, IntPtr.Zero, out pixels, out pitch);
|
||||
|
||||
SDL.SDL_UnlockTexture(texture);
|
||||
|
||||
|
||||
tempTexture = texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Interfaces.Drawing;
|
||||
using OpenDiablo2.Common.Models;
|
||||
using SDL2;
|
||||
|
||||
@ -470,5 +471,7 @@ namespace OpenDiablo2.SDL2_
|
||||
|
||||
public uint GetTicks() => SDL.SDL_GetTicks();
|
||||
|
||||
public ICharacterRenderer CreateCharacterRenderer()
|
||||
=> new SDL2CharacterRenderer(this.renderer, resourceManager, paletteProvider);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
using OpenDiablo2.Common;
|
||||
using OpenDiablo2.Common.Attributes;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
@ -78,11 +77,6 @@ namespace OpenDiablo2.Scenes
|
||||
menuButton = createButton(eButtonType.Menu);
|
||||
menuButton.Location = new Point(393, 561);
|
||||
menuButton.OnToggle = OnMenuToggle;
|
||||
|
||||
/*var item = itemManager.getItem("hdm");
|
||||
var cursorsprite = renderWindow.LoadSprite(ResourcePaths.GeneratePathForItem(item.InvFile), Palettes.Units);
|
||||
|
||||
renderWindow.MouseCursor = renderWindow.LoadCursor(cursorsprite, 0, new Point(cursorsprite.FrameSize.Width/2, cursorsprite.FrameSize.Height / 2));*/
|
||||
}
|
||||
|
||||
private void OnMenuToggle(bool isToggled)
|
||||
@ -216,4 +210,4 @@ namespace OpenDiablo2.Scenes
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,11 +79,6 @@ namespace OpenDiablo2.Scenes
|
||||
|
||||
var loadingSprite = renderWindow.LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading, new Point(300, 400));
|
||||
|
||||
// TODO: This is just a test
|
||||
//var animation = resourceManager.GetPlayerAnimation(eHero.Necromancer, eWeaponClass.HandToHand, eMobMode.PlayerTownNeutral);
|
||||
//var path = animation.Layers.First().GetDCCPath(eArmorType.Lite);
|
||||
//var test = resourceManager.GetPlayerDCC(animation.Layers.First(), eArmorType.Lite, paletteProvider.PaletteTable["Units"]);
|
||||
|
||||
// Pre-load all the scenes for now until we fix the sdl load problem
|
||||
var scenesToLoad = new string[] {"Select Hero Class" };
|
||||
for (int i = 0; i < scenesToLoad.Count(); i++)
|
||||
@ -94,11 +89,9 @@ namespace OpenDiablo2.Scenes
|
||||
getScene(scenesToLoad[i]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
musicProvider.LoadSong(mpqProvider.GetStream("data\\global\\music\\introedit.wav"));
|
||||
|
||||
musicProvider.PlaySong();
|
||||
*/
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user