mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-02-08 17:46:24 -05:00
Initial commit for animation decoding (not finished). Minor fixups.
This commit is contained in:
parent
4a77e47ddd
commit
f2c14375cf
17
OpenDiablo2.Common/Enums/eAnimationFrame.cs
Normal file
17
OpenDiablo2.Common/Enums/eAnimationFrame.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Enums
|
||||
{
|
||||
public enum eAnimationFrame
|
||||
{
|
||||
NoEvent,
|
||||
Attack,
|
||||
Missile,
|
||||
Sound,
|
||||
Skill
|
||||
}
|
||||
}
|
27
OpenDiablo2.Common/Enums/eArmorType.cs
Normal file
27
OpenDiablo2.Common/Enums/eArmorType.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Enums
|
||||
{
|
||||
public enum eArmorType
|
||||
{
|
||||
Lite,
|
||||
Medium,
|
||||
Heavy
|
||||
}
|
||||
|
||||
public static class eArmorTypeHelper
|
||||
{
|
||||
private static readonly Dictionary<eArmorType, string> tokens = new Dictionary<eArmorType, string>()
|
||||
{
|
||||
{ eArmorType.Lite , "lit" },
|
||||
{ eArmorType.Medium , "med" },
|
||||
{ eArmorType.Heavy , "hvy" }
|
||||
};
|
||||
public static string ToToken(this eArmorType armorType) => tokens[armorType];
|
||||
public static eArmorType ToArmorType(this string source) => tokens.First(x => x.Value.ToUpper() == source.ToUpper()).Key;
|
||||
}
|
||||
}
|
55
OpenDiablo2.Common/Enums/eCompositType.cs
Normal file
55
OpenDiablo2.Common/Enums/eCompositType.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Enums
|
||||
{
|
||||
public enum eCompositType
|
||||
{
|
||||
Head,
|
||||
Torso,
|
||||
Legs,
|
||||
RightArm,
|
||||
LeftArm,
|
||||
RightHand,
|
||||
LeftHand,
|
||||
Shield,
|
||||
Special1,
|
||||
Special2,
|
||||
Special3,
|
||||
Special4,
|
||||
Special5,
|
||||
Special6,
|
||||
Special7,
|
||||
Special8
|
||||
}
|
||||
|
||||
public static class eCompositeTypeHelper
|
||||
{
|
||||
private readonly static Dictionary<eCompositType, string> tokens = new Dictionary<eCompositType, string>
|
||||
{
|
||||
{ eCompositType.Head , "HD" },
|
||||
{ eCompositType.Torso , "TR" },
|
||||
{ eCompositType.Legs , "LG" },
|
||||
{ eCompositType.RightArm , "RA" },
|
||||
{ eCompositType.LeftArm , "LA" },
|
||||
{ eCompositType.RightHand , "RH" },
|
||||
{ eCompositType.LeftHand , "LH" },
|
||||
{ eCompositType.Shield , "SH" },
|
||||
{ eCompositType.Special1 , "S1" },
|
||||
{ eCompositType.Special2 , "S2" },
|
||||
{ eCompositType.Special3 , "S3" },
|
||||
{ eCompositType.Special4 , "S4" },
|
||||
{ eCompositType.Special5 , "S5" },
|
||||
{ eCompositType.Special6 , "S6" },
|
||||
{ eCompositType.Special7 , "S7" },
|
||||
{ eCompositType.Special8 , "S8" }
|
||||
};
|
||||
|
||||
public static string ToToken(this eCompositType source) => tokens[source];
|
||||
public static eCompositType ToCompositeType(this string source) => tokens.First(x => x.Value.ToUpper() == source.ToUpper()).Key;
|
||||
|
||||
}
|
||||
}
|
29
OpenDiablo2.Common/Enums/eDrawEffect.cs
Normal file
29
OpenDiablo2.Common/Enums/eDrawEffect.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Enums
|
||||
{
|
||||
public enum eDrawEffect
|
||||
{
|
||||
//75 % transparency (colormaps 561-816 in a .pl2)
|
||||
PctTransparency75,
|
||||
|
||||
//50 % transparency (colormaps 305-560 in a .pl2)
|
||||
PctTransparency50,
|
||||
|
||||
//25 % transparency (colormaps 49-304 in a .pl2)
|
||||
PctTransparency25,
|
||||
|
||||
//Screen (colormaps 817-1072 in a .pl2)
|
||||
Screen,
|
||||
|
||||
//luminance (colormaps 1073-1328 in a .pl2)
|
||||
Luminance,
|
||||
|
||||
//bright alpha blending (colormaps 1457-1712 in a .pl2)
|
||||
BringAlphaBlending
|
||||
}
|
||||
}
|
@ -16,4 +16,21 @@ namespace OpenDiablo2.Common.Enums
|
||||
Amazon,
|
||||
Druid
|
||||
}
|
||||
|
||||
public static class eHeroExtensions
|
||||
{
|
||||
public readonly static Dictionary<eHero, string> tokens = new Dictionary<eHero, string>
|
||||
{
|
||||
{ eHero.Amazon , "AM" },
|
||||
{ eHero.Sorceress , "SO" },
|
||||
{ eHero.Necromancer , "NE" },
|
||||
{ eHero.Paladin , "PA" },
|
||||
{ eHero.Barbarian , "BA" },
|
||||
{ eHero.Druid , "DZ" },
|
||||
{ eHero.Assassin , "AI" }
|
||||
};
|
||||
|
||||
public static string ToToken(this eHero source) => tokens[source];
|
||||
public static eHero ToHero(this string source) => tokens.First(x => x.Value.ToUpper() == source.ToUpper()).Key;
|
||||
}
|
||||
}
|
||||
|
117
OpenDiablo2.Common/Enums/eMobMode.cs
Normal file
117
OpenDiablo2.Common/Enums/eMobMode.cs
Normal file
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Enums
|
||||
{
|
||||
public enum eMobMode
|
||||
{
|
||||
PlayerDeath,
|
||||
PlayerNeutral,
|
||||
PlayerWalk,
|
||||
PlayerRun,
|
||||
PlayerGetHit,
|
||||
PlayerTownNeutral,
|
||||
PlayerTownWalk,
|
||||
PlayerAttack1,
|
||||
PlayerAttack2,
|
||||
PlayerBlock,
|
||||
PlayerCast,
|
||||
PlayerThrow,
|
||||
PlayerKick,
|
||||
PlayerSkill1,
|
||||
PlayerSkill2,
|
||||
PlayerSkill3,
|
||||
PlayerSkill4,
|
||||
PlayerDead,
|
||||
PlayerSequence,
|
||||
PlayerKnockBack,
|
||||
MonsterDeath,
|
||||
MonsterNeutral,
|
||||
MonsterWalk,
|
||||
MonsterGetHit,
|
||||
MonsterAttack1,
|
||||
MonsterAttack2,
|
||||
MonsterBlock,
|
||||
MonsterCast,
|
||||
MonsterSkill1,
|
||||
MonsterSkill2,
|
||||
MonsterSkill3,
|
||||
MonsterSkill4,
|
||||
MonsterDead,
|
||||
MonsterKnockback,
|
||||
MonsterSequence,
|
||||
MonsterRun,
|
||||
ObjectNeutral,
|
||||
ObjectOperating,
|
||||
ObjectOpened,
|
||||
ObjectSpecial1,
|
||||
ObjectSpecial2,
|
||||
ObjectSpecial3,
|
||||
ObjectSpecial4,
|
||||
ObjectSpecial5
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static class eMobModeExtensions
|
||||
{
|
||||
private static readonly Dictionary<eMobMode, string> mobModes = new Dictionary<eMobMode, string>
|
||||
{
|
||||
{ eMobMode.PlayerDeath ,"DT" },
|
||||
{ eMobMode.PlayerNeutral ,"NU" },
|
||||
{ eMobMode.PlayerWalk ,"WL" },
|
||||
{ eMobMode.PlayerRun ,"RN" },
|
||||
{ eMobMode.PlayerGetHit ,"GH" },
|
||||
{ eMobMode.PlayerTownNeutral ,"TN" },
|
||||
{ eMobMode.PlayerTownWalk ,"TW" },
|
||||
{ eMobMode.PlayerAttack1 ,"A1" },
|
||||
{ eMobMode.PlayerAttack2 ,"A2" },
|
||||
{ eMobMode.PlayerBlock ,"BL" },
|
||||
{ eMobMode.PlayerCast ,"SC" },
|
||||
{ eMobMode.PlayerThrow ,"TH" },
|
||||
{ eMobMode.PlayerKick ,"KK" },
|
||||
{ eMobMode.PlayerSkill1 ,"S1" },
|
||||
{ eMobMode.PlayerSkill2 ,"S2" },
|
||||
{ eMobMode.PlayerSkill3 ,"S3" },
|
||||
{ eMobMode.PlayerSkill4 ,"S4" },
|
||||
{ eMobMode.PlayerDead ,"DD" },
|
||||
{ eMobMode.PlayerSequence ,"GH" },
|
||||
{ eMobMode.PlayerKnockBack ,"GH" },
|
||||
|
||||
{ eMobMode.MonsterDeath , "DT" },
|
||||
{ eMobMode.MonsterNeutral , "NU" },
|
||||
{ eMobMode.MonsterWalk , "WL" },
|
||||
{ eMobMode.MonsterGetHit , "GH" },
|
||||
{ eMobMode.MonsterAttack1 , "A1" },
|
||||
{ eMobMode.MonsterAttack2 , "A2" },
|
||||
{ eMobMode.MonsterBlock , "BL" },
|
||||
{ eMobMode.MonsterCast , "SC" },
|
||||
{ eMobMode.MonsterSkill1 , "S1" },
|
||||
{ eMobMode.MonsterSkill2 , "S2" },
|
||||
{ eMobMode.MonsterSkill3 , "S3" },
|
||||
{ eMobMode.MonsterSkill4 , "S4" },
|
||||
{ eMobMode.MonsterDead , "DD" },
|
||||
{ eMobMode.MonsterKnockback , "GH" },
|
||||
{ eMobMode.MonsterSequence , "xx" },
|
||||
{ eMobMode.MonsterRun , "RN" },
|
||||
|
||||
{ eMobMode.ObjectNeutral , "NU" },
|
||||
{ eMobMode.ObjectOperating , "OP" },
|
||||
{ eMobMode.ObjectOpened , "ON" },
|
||||
{ eMobMode.ObjectSpecial1 , "S1" },
|
||||
{ eMobMode.ObjectSpecial2 , "S2" },
|
||||
{ eMobMode.ObjectSpecial3 , "S3" },
|
||||
{ eMobMode.ObjectSpecial4 , "S4" },
|
||||
{ eMobMode.ObjectSpecial5 , "S5" }
|
||||
|
||||
|
||||
};
|
||||
|
||||
public static string ToToken(this eMobMode src) => mobModes[src];
|
||||
public static eMobMode FromToken(this string token) => mobModes.First(x => x.Value.ToUpper() == token.ToUpper()).Key;
|
||||
}
|
||||
|
||||
}
|
53
OpenDiablo2.Common/Enums/eWeaponClass.cs
Normal file
53
OpenDiablo2.Common/Enums/eWeaponClass.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Enums
|
||||
{
|
||||
public enum eWeaponClass
|
||||
{
|
||||
None,
|
||||
HandToHand,
|
||||
Bow,
|
||||
OneHandSwing,
|
||||
OneHandThrust,
|
||||
Staff,
|
||||
TwoHandSwing,
|
||||
TwoHandThrust,
|
||||
Crossbow,
|
||||
LeftJabRightSwing,
|
||||
LeftJabRightThrust,
|
||||
LeftSwingRightSwing,
|
||||
LeftSwingRightThrust,
|
||||
OneHandToHand,
|
||||
TwoHandToHand
|
||||
}
|
||||
|
||||
public static class eWeaponClassExtensions
|
||||
{
|
||||
private static readonly Dictionary<eWeaponClass, string> codes = new Dictionary<eWeaponClass, string>
|
||||
{
|
||||
{eWeaponClass.None , "" },
|
||||
{eWeaponClass.HandToHand , "hth" },
|
||||
{eWeaponClass.Bow , "bow" },
|
||||
{eWeaponClass.OneHandSwing , "1hs" },
|
||||
{eWeaponClass.OneHandThrust , "1ht" },
|
||||
{eWeaponClass.Staff , "stf" },
|
||||
{eWeaponClass.TwoHandSwing , "2hs" },
|
||||
{eWeaponClass.TwoHandThrust , "2ht" },
|
||||
{eWeaponClass.Crossbow , "xbw" },
|
||||
{eWeaponClass.LeftJabRightSwing , "1js" },
|
||||
{eWeaponClass.LeftJabRightThrust , "1jt" },
|
||||
{eWeaponClass.LeftSwingRightSwing , "1ss" },
|
||||
{eWeaponClass.LeftSwingRightThrust , "1st" },
|
||||
{eWeaponClass.OneHandToHand , "ht1" },
|
||||
{eWeaponClass.TwoHandToHand , "ht2" }
|
||||
};
|
||||
|
||||
public static string ToToken(this eWeaponClass source) => codes[source];
|
||||
public static eWeaponClass ToWeaponClass(this string source) => codes.First(x => x.Value.ToUpper() == source.ToUpper()).Key;
|
||||
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
using OpenDiablo2.Common.Models;
|
||||
using System.Collections.Generic;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
namespace OpenDiablo2.Common.Interfaces
|
||||
{
|
||||
@ -17,5 +19,9 @@ namespace OpenDiablo2.Common.Interfaces
|
||||
MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelDetail levelDetail, LevelType levelType);
|
||||
MPQDT1 GetMPQDT1(string resourcePath);
|
||||
Palette GetPalette(string paletteName);
|
||||
MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode);
|
||||
MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette);
|
||||
|
||||
Dictionary<string, List<AnimationData>> Animations { get; }
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,6 @@ namespace OpenDiablo2.Common.Interfaces
|
||||
|
||||
public interface ISessionManager : ISessionEventProvider, IDisposable
|
||||
{
|
||||
Guid PlayerId { get; }
|
||||
|
||||
void Initialize();
|
||||
void Stop();
|
||||
|
||||
|
47
OpenDiablo2.Common/Models/AnimationData.cs
Normal file
47
OpenDiablo2.Common/Models/AnimationData.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class AnimationData
|
||||
{
|
||||
public string COFName { get; set; }
|
||||
public int FramesPerDirection { get; set; }
|
||||
public int AnimationSpeed { get; set; }
|
||||
public byte[] Flags { get; set; }
|
||||
|
||||
public static Dictionary<string, List<AnimationData>> LoadFromStream(Stream stream)
|
||||
{
|
||||
var result = new Dictionary<string, List<AnimationData>>();
|
||||
var br = new BinaryReader(stream);
|
||||
|
||||
while(stream.Length > stream.Position)
|
||||
{
|
||||
var dataCount = br.ReadInt32();
|
||||
|
||||
for (int i = 0; i < dataCount; ++i)
|
||||
{
|
||||
var data = new AnimationData
|
||||
{
|
||||
COFName = Encoding.ASCII.GetString(br.ReadBytes(8)).Trim('\0'),
|
||||
FramesPerDirection = br.ReadInt32(),
|
||||
AnimationSpeed = br.ReadInt32(),
|
||||
Flags = br.ReadBytes(144),
|
||||
};
|
||||
|
||||
if (!result.ContainsKey(data.COFName.ToUpper()))
|
||||
result[data.COFName.ToUpper()] = new List<AnimationData>();
|
||||
|
||||
result[data.COFName.ToUpper()].Add(data);
|
||||
}
|
||||
}
|
||||
|
||||
br.Dispose();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
60
OpenDiablo2.Common/Models/BitMuncher.cs
Normal file
60
OpenDiablo2.Common/Models/BitMuncher.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class BitMuncher
|
||||
{
|
||||
private readonly byte[] data;
|
||||
|
||||
public int Offset { get; private set; }
|
||||
public int BitsRead { get; private set; }
|
||||
|
||||
public BitMuncher(byte[] data, int offset = 0)
|
||||
{
|
||||
this.data = data;
|
||||
this.Offset = offset;
|
||||
}
|
||||
|
||||
public BitMuncher(BitMuncher source)
|
||||
{
|
||||
this.data = source.data;
|
||||
this.Offset = source.Offset;
|
||||
}
|
||||
public int GetBit()
|
||||
{
|
||||
var result = (data[Offset / 8] >> (Offset % 8)) & 0x01;
|
||||
Offset++;
|
||||
BitsRead++;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void SkipBits(int bits) => Offset += bits;
|
||||
|
||||
public byte GetByte() => (byte)GetBits(8);
|
||||
public Int32 GetInt32() => MakeSigned(GetBits(32), 32);
|
||||
|
||||
public int GetBits(int bits)
|
||||
{
|
||||
var result = 0;
|
||||
for (var i = 0; i < bits; i++)
|
||||
result |= (GetBit() << i);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int GetSignedBits(int bits) => MakeSigned(GetBits(bits), bits);
|
||||
|
||||
private int MakeSigned(int value, int bits)
|
||||
{
|
||||
var msbSet = ((value & (1 << (bits - 1))) != 0);
|
||||
if (msbSet)
|
||||
{
|
||||
value = ~value + 1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@ namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
var i = x + (y * Width);
|
||||
if (i >= ImageData.Length)
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
var index = ImageData[i];
|
||||
if (index == -1)
|
||||
@ -44,10 +44,6 @@ namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private UInt32 version;
|
||||
private UInt32 unknown1; // 01 00 00 00 ???
|
||||
private UInt32 unknown2; // 00 00 00 00 ???
|
||||
private UInt32 termination; // EE EE EE EE or CD CD CD CD ???
|
||||
private UInt32[] framePointers;
|
||||
public ImageFrame[] Frames { get; private set; }
|
||||
|
||||
@ -57,12 +53,13 @@ namespace OpenDiablo2.Common.Models
|
||||
public static ImageSet LoadFromStream(Stream stream)
|
||||
{
|
||||
var br = new BinaryReader(stream);
|
||||
var version = br.ReadUInt32();
|
||||
var unknown1 = br.ReadUInt32();
|
||||
var unknown2 = br.ReadUInt32();
|
||||
var termination = br.ReadUInt32();
|
||||
|
||||
var result = new ImageSet
|
||||
{
|
||||
version = br.ReadUInt32(),
|
||||
unknown1 = br.ReadUInt32(),
|
||||
unknown2 = br.ReadUInt32(),
|
||||
termination = br.ReadUInt32(),
|
||||
Directions = br.ReadUInt32(),
|
||||
FramesPerDirection = br.ReadUInt32()
|
||||
};
|
||||
|
74
OpenDiablo2.Common/Models/MPQCOF.cs
Normal file
74
OpenDiablo2.Common/Models/MPQCOF.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
|
||||
namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class MPQCOF
|
||||
{
|
||||
public class COFLayer
|
||||
{
|
||||
public MPQCOF COF { get; internal set; }
|
||||
public eCompositType CompositType { get; internal set; }
|
||||
public byte Shadow { get; internal set; }
|
||||
public bool IsTransparent { get; internal set; }
|
||||
public eDrawEffect DrawEffect { get; internal set; }
|
||||
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";
|
||||
|
||||
}
|
||||
|
||||
public eHero Hero { get; private set; }
|
||||
public eWeaponClass WeaponClass { get; private set; }
|
||||
public eMobMode MobMode { get; private set; }
|
||||
public List<AnimationData> Animations { get; private set; }
|
||||
|
||||
public IEnumerable<COFLayer> Layers { get; private set; }
|
||||
public IEnumerable<eAnimationFrame> AnimationFrames { get; private set; }
|
||||
|
||||
public static MPQCOF Load(Stream stream, Dictionary<string, List<AnimationData>> animations, eHero hero, eWeaponClass weaponClass, eMobMode mobMode)
|
||||
{
|
||||
var result = new MPQCOF
|
||||
{
|
||||
WeaponClass = weaponClass,
|
||||
MobMode = mobMode,
|
||||
Hero = hero
|
||||
};
|
||||
|
||||
var br = new BinaryReader(stream);
|
||||
|
||||
var numLayers = br.ReadByte();
|
||||
var framesPerDir = br.ReadByte();
|
||||
var numDirections = br.ReadByte();
|
||||
|
||||
br.ReadBytes(25); // Skip 25 unknown bytes...
|
||||
|
||||
var layers = new List<COFLayer>();
|
||||
for (var layerIdx = 0; layerIdx < numLayers; layerIdx++)
|
||||
{
|
||||
var layer = new COFLayer();
|
||||
layer.COF = result;
|
||||
layer.CompositType = (eCompositType)br.ReadByte();
|
||||
layer.Shadow = br.ReadByte();
|
||||
br.ReadByte(); // Unknown
|
||||
layer.IsTransparent = br.ReadByte() != 0;
|
||||
layer.DrawEffect = (eDrawEffect)br.ReadByte();
|
||||
layers.Add(layer);
|
||||
layer.WeaponClass = Encoding.ASCII.GetString(br.ReadBytes(4)).Trim('\0').ToWeaponClass();
|
||||
}
|
||||
result.Layers = layers;
|
||||
result.AnimationFrames = br.ReadBytes(framesPerDir).Select(x => (eAnimationFrame)x);
|
||||
|
||||
var cofName = $"{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}".ToUpper();
|
||||
result.Animations = animations[cofName];
|
||||
br.Dispose();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
440
OpenDiablo2.Common/Models/MPQDCC.cs
Normal file
440
OpenDiablo2.Common/Models/MPQDCC.cs
Normal file
@ -0,0 +1,440 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace OpenDiablo2.Common.Models
|
||||
{
|
||||
public sealed class MPQDCC
|
||||
{
|
||||
public struct PixelBufferEntry
|
||||
{
|
||||
public byte[] Value;
|
||||
public int Frame;
|
||||
public int FrameCellIndex;
|
||||
}
|
||||
|
||||
public struct Cell
|
||||
{
|
||||
public int Width;
|
||||
public int Height;
|
||||
public int XOffset;
|
||||
public int YOffset;
|
||||
}
|
||||
|
||||
public sealed class MPQDCCDirectionFrame
|
||||
{
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public int XOffset { get; private set; }
|
||||
public int YOffset { get; private set; }
|
||||
public int NumberOfOptionalBytes { get; private set; }
|
||||
public int NumberOfCodedBytes { get; private set; }
|
||||
public bool FrameIsBottomUp { get; private set; }
|
||||
public Rectangle Box { get; private set; }
|
||||
public Cell[] Cells { get; private set; }
|
||||
public int HorizontalCellCount { get; private set; }
|
||||
public int VerticalCellCount { get; private set; }
|
||||
|
||||
|
||||
|
||||
public MPQDCCDirectionFrame(BitMuncher bits, MPQDCCDirection direction)
|
||||
{
|
||||
var variable0 = bits.GetBits(direction.Variable0Bits);
|
||||
Width = bits.GetBits(direction.WidthBits);
|
||||
Height = bits.GetBits(direction.HeightBits);
|
||||
XOffset = bits.GetSignedBits(direction.XOffsetBits);
|
||||
YOffset = bits.GetSignedBits(direction.YOffsetBits);
|
||||
NumberOfOptionalBytes = bits.GetBits(direction.OptionalDataBits);
|
||||
NumberOfCodedBytes = bits.GetBits(direction.CodedBytesBits);
|
||||
FrameIsBottomUp = bits.GetBit() == 1;
|
||||
|
||||
Box = new Rectangle(
|
||||
XOffset,
|
||||
YOffset - Height + 1,
|
||||
Width,
|
||||
Height
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public void MakeCells(MPQDCCDirection direction)
|
||||
{
|
||||
var w = 4 - ((Box.Left - direction.Box.Left) % 4); // Width of the first column (in pixels)
|
||||
if ((Box.Width - w) <= 1)
|
||||
HorizontalCellCount = 1;
|
||||
else
|
||||
{
|
||||
HorizontalCellCount = 2 + ((Box.Width - w - 1) / 4);
|
||||
if (((Box.Width - w - 1) % 4) == 0)
|
||||
HorizontalCellCount--;
|
||||
}
|
||||
|
||||
|
||||
var h = 4 - ((Box.Top - direction.Box.Top) % 4); // Height of the first column (in pixels)
|
||||
if ((Box.Height - h) <= 1)
|
||||
VerticalCellCount = 1;
|
||||
else
|
||||
{
|
||||
VerticalCellCount = 2 + ((Box.Height - h - 1) / 4);
|
||||
if (((Box.Height - h - 1) % 4) == 0)
|
||||
VerticalCellCount--;
|
||||
}
|
||||
|
||||
Cells = new Cell[HorizontalCellCount * VerticalCellCount];
|
||||
|
||||
// Calculate the cell widths and heights
|
||||
var cellWidths = new int[HorizontalCellCount];
|
||||
if (HorizontalCellCount == 1)
|
||||
cellWidths[0] = Box.Width;
|
||||
else
|
||||
{
|
||||
cellWidths[0] = w;
|
||||
for (var i = 1; i < (HorizontalCellCount - 1); i++)
|
||||
cellWidths[i] = 4;
|
||||
cellWidths[HorizontalCellCount - 1] = Box.Width - w - (4 * (HorizontalCellCount - 2));
|
||||
}
|
||||
|
||||
var cellHeights = new int[VerticalCellCount];
|
||||
if (VerticalCellCount == 1)
|
||||
cellHeights[0] = Box.Height;
|
||||
else
|
||||
{
|
||||
cellHeights[0] = h;
|
||||
for (var i = 1; i < (VerticalCellCount - 1); i++)
|
||||
cellHeights[i] = 4;
|
||||
cellHeights[VerticalCellCount - 1] = Box.Height - h - (4 * (VerticalCellCount - 2));
|
||||
}
|
||||
|
||||
Cells = new Cell[HorizontalCellCount * VerticalCellCount];
|
||||
var offsetY = Box.Top - direction.Box.Top;
|
||||
for (var y = 0; y < VerticalCellCount; y++)
|
||||
{
|
||||
var offsetX = Box.Left - direction.Box.Left;
|
||||
for (var x = 0; x < HorizontalCellCount; x++)
|
||||
{
|
||||
Cells[x + (y * HorizontalCellCount)] = new Cell
|
||||
{
|
||||
XOffset = offsetX,
|
||||
YOffset = offsetY,
|
||||
Width = cellWidths[x],
|
||||
Height = cellHeights[y]
|
||||
};
|
||||
offsetX += cellWidths[x];
|
||||
}
|
||||
offsetY += cellHeights[y];
|
||||
}
|
||||
}
|
||||
}
|
||||
public sealed class MPQDCCDirection
|
||||
{
|
||||
public int OutSizeCoded { get; private set; }
|
||||
public int CompressionFlags { get; private set; }
|
||||
public int Variable0Bits { get; private set; }
|
||||
public int WidthBits { get; private set; }
|
||||
public int HeightBits { get; private set; }
|
||||
public int XOffsetBits { get; private set; }
|
||||
public int YOffsetBits { get; private set; }
|
||||
public int OptionalDataBits { get; private set; }
|
||||
public int CodedBytesBits { get; private set; }
|
||||
public int EqualCellsBitstreamSize { get; private set; }
|
||||
public int PixelMaskBitstreamSize { get; private set; }
|
||||
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 Rectangle Box { get; private set; }
|
||||
public Cell[] Cells { get; private set; }
|
||||
public int HorizontalCellCount { get; private set; }
|
||||
public int VerticalCellCount { get; private set; }
|
||||
public List<PixelBufferEntry> PixelBuffer { get; private set; }
|
||||
|
||||
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);
|
||||
Variable0Bits = crazyBitTable[bm.GetBits(4)];
|
||||
WidthBits = crazyBitTable[bm.GetBits(4)];
|
||||
HeightBits = crazyBitTable[bm.GetBits(4)];
|
||||
XOffsetBits = crazyBitTable[bm.GetBits(4)];
|
||||
YOffsetBits = crazyBitTable[bm.GetBits(4)];
|
||||
OptionalDataBits = crazyBitTable[bm.GetBits(4)];
|
||||
CodedBytesBits = crazyBitTable[bm.GetBits(4)];
|
||||
|
||||
Frames = new MPQDCCDirectionFrame[file.NumberOfFrames];
|
||||
|
||||
if (Variable0Bits != 0)
|
||||
throw new ApplicationException("Variable0Bits is not 0. This is not ok.");
|
||||
|
||||
// Load the frame headers
|
||||
for (var frameIdx = 0; frameIdx < file.NumberOfFrames; frameIdx++)
|
||||
Frames[frameIdx] = new MPQDCCDirectionFrame(bm, this);
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
if (OptionalDataBits > 0)
|
||||
throw new ApplicationException("Optional bits in DCC data is not currently supported.");
|
||||
|
||||
if ((CompressionFlags & 0x2) > 0)
|
||||
EqualCellsBitstreamSize = bm.GetBits(20);
|
||||
|
||||
PixelMaskBitstreamSize = bm.GetBits(20);
|
||||
|
||||
if ((CompressionFlags & 0x1) > 0)
|
||||
{
|
||||
EncodingTypeBitsreamSize = bm.GetBits(20);
|
||||
RawPixelCodesBitstreamSize = bm.GetBits(20);
|
||||
}
|
||||
|
||||
|
||||
// PixelValuesKey
|
||||
var paletteEntries = new List<bool>();
|
||||
for (var i = 0; i < 256; i++)
|
||||
paletteEntries.Add(bm.GetBit() != 0);
|
||||
|
||||
PaletteEntries = new int[paletteEntries.Count(x => x == true)];
|
||||
var paletteOffset = 0;
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
if (!paletteEntries[i])
|
||||
continue;
|
||||
|
||||
PaletteEntries[paletteOffset++] = i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var equalCellsBitstream = new BitMuncher(bm);
|
||||
bm.SkipBits(EqualCellsBitstreamSize);
|
||||
|
||||
var pixelMaskBitstream = new BitMuncher(bm);
|
||||
bm.SkipBits(PixelMaskBitstreamSize);
|
||||
|
||||
var encodingTypeBitsream = new BitMuncher(bm);
|
||||
bm.SkipBits(EncodingTypeBitsreamSize);
|
||||
|
||||
var rawPixelCodesBitstream = new BitMuncher(bm);
|
||||
bm.SkipBits(RawPixelCodesBitstreamSize);
|
||||
|
||||
var pixelCodeandDisplacement = new BitMuncher(bm);
|
||||
|
||||
CalculateCellOffsets();
|
||||
|
||||
foreach (var frame in Frames)
|
||||
frame.MakeCells(this);
|
||||
|
||||
FillPixelBuffer(pixelCodeandDisplacement, equalCellsBitstream, pixelMaskBitstream, encodingTypeBitsream, rawPixelCodesBitstream);
|
||||
|
||||
if (equalCellsBitstream.BitsRead != EqualCellsBitstreamSize)
|
||||
throw new ApplicationException("Did not read the correct number of bits!");
|
||||
|
||||
if (pixelMaskBitstream.BitsRead != PixelMaskBitstreamSize)
|
||||
throw new ApplicationException("Did not read the correct number of bits!");
|
||||
|
||||
if (encodingTypeBitsream.BitsRead != EncodingTypeBitsreamSize)
|
||||
throw new ApplicationException("Did not read the correct number of bits!");
|
||||
|
||||
if (rawPixelCodesBitstream.BitsRead != RawPixelCodesBitstreamSize)
|
||||
throw new ApplicationException("Did not read the correct number of bits!");
|
||||
|
||||
|
||||
bm.SkipBits(pixelCodeandDisplacement.BitsRead);
|
||||
}
|
||||
|
||||
private static readonly int[] pixelMaskLookup = new int[] { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
|
||||
private void FillPixelBuffer(BitMuncher pcd, BitMuncher ec, BitMuncher pm, BitMuncher et, BitMuncher rp)
|
||||
{
|
||||
UInt32 lastPixel = 0;
|
||||
var cellBuffer = new int[HorizontalCellCount * VerticalCellCount]; // Store the offset to the cell buffer
|
||||
for (var i = 0; i < cellBuffer.Length; i++)
|
||||
cellBuffer[i] = -1;
|
||||
|
||||
PixelBuffer = new List<PixelBufferEntry>();
|
||||
|
||||
var frameIndex = -1;
|
||||
UInt32 pixelMask = 0x00;
|
||||
foreach (var frame in Frames)
|
||||
{
|
||||
frameIndex++;
|
||||
var originCellX = (frame.Box.Left - Box.Left) / 4;
|
||||
var originCellY = (frame.Box.Top - Box.Top) / 4;
|
||||
|
||||
for (var cellY = 0; cellY < frame.VerticalCellCount; cellY++)
|
||||
{
|
||||
var currentCellY = cellY + originCellY;
|
||||
for (var cellX = 0; cellX < frame.HorizontalCellCount; cellX++)
|
||||
{
|
||||
var currentCell = (originCellX + cellX) + (currentCellY * HorizontalCellCount);
|
||||
var nextCell = false;
|
||||
if (cellBuffer[currentCell] != -1)
|
||||
{
|
||||
var tmp = 0;
|
||||
if (EqualCellsBitstreamSize > 0)
|
||||
tmp = ec.GetBit();
|
||||
|
||||
if (tmp == 0)
|
||||
pixelMask = (UInt32)pm.GetBits(4);
|
||||
else
|
||||
nextCell = true;
|
||||
}
|
||||
else pixelMask = 0x0F;
|
||||
|
||||
|
||||
if (nextCell == false)
|
||||
{
|
||||
// Decode the pixels
|
||||
UInt32[] pixelStack = new UInt32[4];
|
||||
int numberOfPixelBits = pixelMaskLookup[pixelMask];
|
||||
var encodingType = ((numberOfPixelBits != 0) && (EncodingTypeBitsreamSize > 0))
|
||||
? et.GetBit()
|
||||
: 0;
|
||||
int decodedPixel = 0;
|
||||
for (int i = 0; i < numberOfPixelBits; i++)
|
||||
{
|
||||
if (encodingType != 0)
|
||||
{
|
||||
pixelStack[i] = (UInt32)rp.GetBits(8);
|
||||
}
|
||||
else
|
||||
{
|
||||
pixelStack[i] = lastPixel;
|
||||
var pixelDisplacement = pcd.GetBits(4);
|
||||
pixelStack[i] += (UInt32)pixelDisplacement;
|
||||
while (pixelDisplacement == 15)
|
||||
{
|
||||
pixelDisplacement = pcd.GetBits(4);
|
||||
pixelStack[i] += (UInt32)pixelDisplacement;
|
||||
}
|
||||
}
|
||||
if (pixelStack[i] == lastPixel)
|
||||
{
|
||||
pixelStack[i] = 0;
|
||||
i = numberOfPixelBits; // Just break here....
|
||||
}
|
||||
else
|
||||
{
|
||||
lastPixel = pixelStack[i];
|
||||
decodedPixel++;
|
||||
}
|
||||
}
|
||||
|
||||
var oldEntry = cellBuffer[currentCell];
|
||||
var curIdx = decodedPixel - 1;
|
||||
var newEntry = new PixelBufferEntry { Value = new byte[4] };
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if ((pixelMask * (1 << i)) != 0)
|
||||
{
|
||||
if (curIdx >= 0)
|
||||
newEntry.Value[i] = (byte)pixelStack[curIdx--];
|
||||
else
|
||||
newEntry.Value[i] = 0;
|
||||
}
|
||||
else
|
||||
newEntry.Value[i] = PixelBuffer[oldEntry].Value[i];
|
||||
}
|
||||
newEntry.Frame = frameIndex;
|
||||
newEntry.FrameCellIndex = XOffsetBits + (cellY * HorizontalCellCount);
|
||||
PixelBuffer.Add(newEntry);
|
||||
cellBuffer[currentCell] = PixelBuffer.Count() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateCellOffsets()
|
||||
{
|
||||
// Calculate the number of vertical and horizontal cells we need
|
||||
HorizontalCellCount = 1 + (Box.Width - 1) / 4;
|
||||
VerticalCellCount = 1 + (Box.Height - 1) / 4;
|
||||
|
||||
// Calculate the cell widths
|
||||
var cellWidths = new int[HorizontalCellCount];
|
||||
if (HorizontalCellCount == 1)
|
||||
cellWidths[0] = Box.Width;
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < HorizontalCellCount - 1; i++)
|
||||
cellWidths[i] = 4;
|
||||
cellWidths[HorizontalCellCount - 1] = Box.Width - (4 * (HorizontalCellCount - 1));
|
||||
}
|
||||
|
||||
// Calculate the cell heights
|
||||
var cellHeights = new int[VerticalCellCount];
|
||||
if (VerticalCellCount == 1)
|
||||
cellHeights[0] = Box.Height;
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < VerticalCellCount - 1; i++)
|
||||
cellHeights[i] = 4;
|
||||
cellHeights[VerticalCellCount - 1] = Box.Height - (4 * (VerticalCellCount - 1));
|
||||
}
|
||||
|
||||
// Set the cell widths and heights in the cell buffer
|
||||
Cells = new Cell[VerticalCellCount * HorizontalCellCount];
|
||||
var yOffset = 0;
|
||||
for (var y = 0; y < VerticalCellCount; y++)
|
||||
{
|
||||
var xOffset = 0;
|
||||
for (var x = 0; x < HorizontalCellCount; x++)
|
||||
{
|
||||
Cells[x + (y * HorizontalCellCount)] = new Cell
|
||||
{
|
||||
Width = cellWidths[x],
|
||||
Height = cellHeights[y],
|
||||
XOffset = xOffset,
|
||||
YOffset = yOffset
|
||||
};
|
||||
xOffset += 4;
|
||||
}
|
||||
yOffset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int Signature { get; private set; }
|
||||
public int Version { get; private set; }
|
||||
public int NumberOfDirections { get; private set; }
|
||||
public int NumberOfFrames { get; private set; }
|
||||
public MPQDCCDirection[] Directions { get; private set; }
|
||||
|
||||
public MPQDCC(byte[] data, Palette palette)
|
||||
{
|
||||
var bm = new BitMuncher(data);
|
||||
Signature = bm.GetByte();
|
||||
if (Signature != 0x74)
|
||||
throw new ApplicationException("Signature expected to be 0x74 but it is not.");
|
||||
|
||||
Version = bm.GetByte();
|
||||
NumberOfDirections = bm.GetByte();
|
||||
NumberOfFrames = bm.GetInt32();
|
||||
|
||||
if (bm.GetInt32() != 1)
|
||||
throw new ApplicationException("This value isn't 1. It has to be 1.");
|
||||
|
||||
var totalSizeCoded = bm.GetInt32();
|
||||
var directionOffsets = new int[NumberOfDirections];
|
||||
for (var i = 0; i < NumberOfDirections; i++)
|
||||
directionOffsets[i] = bm.GetInt32();
|
||||
|
||||
Directions = new MPQDCCDirection[NumberOfDirections];
|
||||
for (var i = 0; i < NumberOfDirections; i++)
|
||||
{
|
||||
Directions[i] = new MPQDCCDirection(new BitMuncher(data, directionOffsets[i] * 8), this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
<NoWarn>IDE0049</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
@ -55,7 +56,12 @@
|
||||
<Compile Include="Attributes\MessageFrameAttribute.cs" />
|
||||
<Compile Include="Attributes\SceneAttribute.cs" />
|
||||
<Compile Include="AutofacModule.cs" />
|
||||
<Compile Include="Enums\eAnimationFrame.cs" />
|
||||
<Compile Include="Enums\eArmorType.cs" />
|
||||
<Compile Include="Enums\eCompositType.cs" />
|
||||
<Compile Include="Enums\eDrawEffect.cs" />
|
||||
<Compile Include="Enums\eMessageFrameType.cs" />
|
||||
<Compile Include="Enums\eMobMode.cs" />
|
||||
<Compile Include="Enums\eMovementType.cs" />
|
||||
<Compile Include="Enums\ePanelFrameType.cs" />
|
||||
<Compile Include="Enums\eButtonType.cs" />
|
||||
@ -66,6 +72,7 @@
|
||||
<Compile Include="Enums\eRenderCellType.cs" />
|
||||
<Compile Include="Enums\eSessionType.cs" />
|
||||
<Compile Include="Enums\eTextAlign.cs" />
|
||||
<Compile Include="Enums\eWeaponClass.cs" />
|
||||
<Compile Include="Enums\eWildBorder.cs" />
|
||||
<Compile Include="Enums\Mobs\eDamageTypes.cs" />
|
||||
<Compile Include="Enums\Mobs\eMobFlags.cs" />
|
||||
@ -76,6 +83,10 @@
|
||||
<Compile Include="Interfaces\MessageBus\IMessageFrame.cs" />
|
||||
<Compile Include="Interfaces\MessageBus\ISessionManager.cs" />
|
||||
<Compile Include="Interfaces\MessageBus\ISessionServer.cs" />
|
||||
<Compile Include="Models\AnimationData.cs" />
|
||||
<Compile Include="Models\BitMuncher.cs" />
|
||||
<Compile Include="Models\MPQCOF.cs" />
|
||||
<Compile Include="Models\MPQDCC.cs" />
|
||||
<Compile Include="Models\PlayerInfo.cs" />
|
||||
<Compile Include="Models\PlayerLocationDetails.cs" />
|
||||
<Compile Include="Interfaces\UI\IButton.cs" />
|
||||
|
@ -125,6 +125,11 @@ namespace OpenDiablo2.Common
|
||||
public static string LevelType = "data\\global\\excel\\LvlTypes.txt";
|
||||
public static string LevelDetails = "data\\global\\excel\\Levels.txt";
|
||||
|
||||
// --- Animations ---
|
||||
public static string ObjectData = "data\\global\\objects";
|
||||
public static string AnimationData = "data\\global\\animdata.d2";
|
||||
public static string PlayerAnimationBase = "data\\global\\CHARS";
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ namespace OpenDiablo2.Core
|
||||
{
|
||||
public sealed class EngineDataManager : IEngineDataManager
|
||||
{
|
||||
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private readonly IMPQProvider mpqProvider;
|
||||
|
||||
public List<LevelPreset> LevelPresets { get; internal set; }
|
||||
@ -28,6 +30,7 @@ namespace OpenDiablo2.Core
|
||||
|
||||
private void LoadLevelTypes()
|
||||
{
|
||||
log.Info("Loading level types");
|
||||
var data = mpqProvider
|
||||
.GetTextFile(ResourcePaths.LevelType)
|
||||
.Skip(1)
|
||||
@ -42,6 +45,7 @@ namespace OpenDiablo2.Core
|
||||
|
||||
private void LoadLevelPresets()
|
||||
{
|
||||
log.Info("Loading level presets");
|
||||
var data = mpqProvider
|
||||
.GetTextFile(ResourcePaths.LevelPreset)
|
||||
.Skip(1)
|
||||
@ -56,6 +60,7 @@ namespace OpenDiablo2.Core
|
||||
|
||||
private void LoadLevelDetails()
|
||||
{
|
||||
log.Info("Loading level details");
|
||||
var data = mpqProvider
|
||||
.GetTextFile(ResourcePaths.LevelDetails)
|
||||
.Skip(1)
|
||||
|
@ -28,8 +28,7 @@ namespace OpenDiablo2.Core
|
||||
|
||||
private Dictionary<string, SoundEntry> soundTable = new Dictionary<string, SoundEntry>();
|
||||
public Dictionary<string, Palette> PaletteTable { get; private set; } = new Dictionary<string, Palette>();
|
||||
|
||||
|
||||
|
||||
public GameEngine(
|
||||
GlobalConfiguration globalConfig,
|
||||
IMPQProvider mpqProvider,
|
||||
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenDiablo2.Common;
|
||||
using OpenDiablo2.Common.Enums;
|
||||
using OpenDiablo2.Common.Interfaces;
|
||||
using OpenDiablo2.Common.Models;
|
||||
|
||||
@ -17,11 +19,16 @@ namespace OpenDiablo2.Core
|
||||
private Dictionary<string, MPQFont> MPQFonts = new Dictionary<string, MPQFont>();
|
||||
private Dictionary<string, Palette> Palettes = new Dictionary<string, Palette>();
|
||||
private Dictionary<string, MPQDT1> DTs = new Dictionary<string, MPQDT1>();
|
||||
private Dictionary<string, MPQCOF> PlayerCOFs = new Dictionary<string, MPQCOF>();
|
||||
|
||||
public Dictionary<string, List<AnimationData>> Animations { get; private set; } = new Dictionary<string, List<AnimationData>>();
|
||||
|
||||
public ResourceManager(IMPQProvider mpqProvider, IEngineDataManager engineDataManager)
|
||||
{
|
||||
this.mpqProvider = mpqProvider;
|
||||
this.engineDataManager = engineDataManager;
|
||||
|
||||
Animations = AnimationData.LoadFromStream(mpqProvider.GetStream(ResourcePaths.AnimationData));
|
||||
}
|
||||
|
||||
public ImageSet GetImageSet(string resourcePath)
|
||||
@ -70,5 +77,30 @@ namespace OpenDiablo2.Core
|
||||
|
||||
return DTs[resourcePath];
|
||||
}
|
||||
|
||||
public MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode)
|
||||
{
|
||||
var key = $"{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}";
|
||||
if (PlayerCOFs.ContainsKey(key))
|
||||
return PlayerCOFs[key];
|
||||
|
||||
var path = $"{ResourcePaths.PlayerAnimationBase}\\{hero.ToToken()}\\COF\\{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}.cof";
|
||||
var result = MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, weaponClass, mobMode);
|
||||
PlayerCOFs[key] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette)
|
||||
{
|
||||
byte[] binaryData;
|
||||
using (var stream = mpqProvider.GetStream(cofLayer.GetDCCPath(armorType)))
|
||||
{
|
||||
binaryData = new byte[stream.Length];
|
||||
stream.Read(binaryData, 0, (int)stream.Length);
|
||||
}
|
||||
var result = new MPQDCC(binaryData, palette);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,10 @@ 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" };
|
||||
|
@ -37,7 +37,6 @@ namespace OpenDiablo2.Scenes
|
||||
private readonly IPaletteProvider paletteProvider;
|
||||
private readonly IMPQProvider mpqProvider;
|
||||
private readonly IMouseInfoProvider mouseInfoProvider;
|
||||
private readonly IMusicProvider musicProvider;
|
||||
private readonly ISceneManager sceneManager;
|
||||
private readonly ITextDictionary textDictionary;
|
||||
private readonly IKeyboardInfoProvider keyboardInfoProvider;
|
||||
@ -58,7 +57,6 @@ namespace OpenDiablo2.Scenes
|
||||
IPaletteProvider paletteProvider,
|
||||
IMPQProvider mpqProvider,
|
||||
IMouseInfoProvider mouseInfoProvider,
|
||||
IMusicProvider musicProvider,
|
||||
ISceneManager sceneManager,
|
||||
Func<eButtonType, IButton> createButton,
|
||||
Func<ITextBox> createTextBox,
|
||||
|
@ -24,7 +24,6 @@ namespace OpenDiablo2.ServiceBus
|
||||
private RequestSocket requestSocket;
|
||||
private AutoResetEvent resetEvent = new AutoResetEvent(false);
|
||||
private ISessionServer sessionServer;
|
||||
public Guid PlayerId { get; private set; }
|
||||
private bool running = false;
|
||||
|
||||
public OnSetSeedEvent OnSetSeed { get; set; }
|
||||
|
Loading…
Reference in New Issue
Block a user