1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-11-20 03:16:26 -05:00

Added support for equipment rendering. Cleaned up player equipment strucs

This commit is contained in:
Tim Sarbin 2018-12-12 17:39:32 -05:00
parent 0402119ec7
commit a79040cc20
18 changed files with 360 additions and 118 deletions

View File

@ -14,7 +14,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using OpenDiablo2.Common.Models.Mobs;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -25,5 +27,6 @@ namespace OpenDiablo2.Common.Interfaces
IEnumerable<string> GetTextFile(string fileName); IEnumerable<string> GetTextFile(string fileName);
Stream GetStream(string fileName); Stream GetStream(string fileName);
byte[] GetBytes(string fileName); byte[] GetBytes(string fileName);
string GetCharacterDccPath(eHero hero, eMobMode mobMode, eCompositType compositType, PlayerEquipment equipment);
} }
} }

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.Common.Interfaces namespace OpenDiablo2.Common.Interfaces
{ {
@ -19,8 +20,8 @@ namespace OpenDiablo2.Common.Interfaces
MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelType levelType); MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelType levelType);
MPQDT1 GetMPQDT1(string resourcePath); MPQDT1 GetMPQDT1(string resourcePath);
Palette GetPalette(string paletteFile); Palette GetPalette(string paletteFile);
MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode, string ShieldCode, string weaponCode); MPQCOF GetPlayerAnimation(eHero hero, eMobMode mobMode, PlayerEquipment equipment);
MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette); MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, PlayerEquipment equipment, Palette palette);
Dictionary<string, List<AnimationData>> Animations { get; } Dictionary<string, List<AnimationData>> Animations { get; }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.Common.Interfaces.Drawing namespace OpenDiablo2.Common.Interfaces.Drawing
{ {
@ -9,11 +10,8 @@ namespace OpenDiablo2.Common.Interfaces.Drawing
Guid UID { get; set; } Guid UID { get; set; }
PlayerLocationDetails LocationDetails { get; set; } PlayerLocationDetails LocationDetails { get; set; }
eHero Hero { get; set; } eHero Hero { get; set; }
eWeaponClass WeaponClass { get; set; } PlayerEquipment Equipment { get; set; }
eArmorType ArmorType { get; set; }
eMobMode MobMode { get; set; } eMobMode MobMode { get; set; }
string ShieldCode { get; set; }
string WeaponCode { get; set; }
void Update(long ms); void Update(long ms);
void Render(int pixelOffsetX, int pixelOffsetY); void Render(int pixelOffsetX, int pixelOffsetY);

View File

@ -15,8 +15,6 @@
*/ */
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using System;
using System.Runtime.Caching;
namespace OpenDiablo2.Common.Interfaces namespace OpenDiablo2.Common.Interfaces
{ {

View File

@ -2,6 +2,7 @@
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -10,7 +11,7 @@ namespace OpenDiablo2.Common.Models
{ {
public sealed class Armor : Item public sealed class Armor : Item
{ {
public string Type { get; internal set; } public string Type { get; internal set; } = "FIXME"; // TODO: Fix this please
} }
public static class ArmorHelper public static class ArmorHelper
@ -22,5 +23,19 @@ namespace OpenDiablo2.Common.Models
Code = row[17], Code = row[17],
InvFile = row[33] InvFile = row[33]
}; };
public static void Write(this BinaryWriter binaryWriter, Armor armor)
{
(armor as Item).Write(binaryWriter);
binaryWriter.Write(armor.Type);
}
public static Armor ReadItemArmor(this BinaryReader binaryReader)
{
var result = new Armor();
Item.Read(binaryReader, result);
result.Type = binaryReader.ReadString();
return result;
}
} }
} }

View File

@ -1,6 +1,7 @@
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -15,5 +16,19 @@ namespace OpenDiablo2.Common.Models
public string Code { get; internal set; } // Internal code public string Code { get; internal set; } // Internal code
public string Name { get; internal set; } // Item name public string Name { get; internal set; } // Item name
public string InvFile { get; internal set; } // Sprite used for the inventory and mouse cursor public string InvFile { get; internal set; } // Sprite used for the inventory and mouse cursor
internal void Write(BinaryWriter binaryWriter)
{
binaryWriter.Write(Code);
binaryWriter.Write(Name);
binaryWriter.Write(InvFile);
}
internal static void Read(BinaryReader binaryReader, Item source)
{
source.Code = binaryReader.ReadString();
source.Name = binaryReader.ReadString();
source.InvFile = binaryReader.ReadString();
}
} }
} }

View File

@ -1,6 +1,8 @@
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Exceptions;
using OpenDiablo2.Common.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -20,6 +22,71 @@ namespace OpenDiablo2.Common.Models
public ItemInstance(Item item) public ItemInstance(Item item)
{ {
Item = item; Item = item;
Name = item.Name;
}
}
public static class ItemInstanceHelper
{
public static void Write(this BinaryWriter br, ItemInstance itemInstance)
{
if (itemInstance == null)
{
br.Write(false);
return;
}
br.Write(true);
if (itemInstance.Item is Weapon)
{
br.Write((byte)0);
br.Write((Weapon)itemInstance.Item);
}
else if (itemInstance.Item is Armor)
{
br.Write((byte)1);
br.Write((Armor)itemInstance.Item);
}
else if (itemInstance.Item is Misc)
{
br.Write((byte)2);
br.Write((Misc)itemInstance.Item);
}
else throw new OpenDiablo2Exception("Unknown item type.");
br.Write(itemInstance.Name);
br.Write((Int16)itemInstance.Level);
br.Write(itemInstance.Identified);
}
public static ItemInstance ReadItemInstance(this BinaryReader binaryReader)
{
if (!binaryReader.ReadBoolean())
return null;
Item item;
switch(binaryReader.ReadByte())
{
case 0:
item = binaryReader.ReadItemWeapon();
break;
case 1:
item = binaryReader.ReadItemArmor();
break;
case 2:
item = binaryReader.ReadItemMisc();
break;
default:
throw new OpenDiablo2Exception("Unknown item type.");
}
return new ItemInstance(item)
{
Name = binaryReader.ReadString(),
Level = binaryReader.ReadInt16(),
Identified = binaryReader.ReadBoolean()
};
} }
} }
} }

View File

@ -1,6 +1,7 @@
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -21,5 +22,17 @@ namespace OpenDiablo2.Common.Models
Code = row[12], Code = row[12],
InvFile = row[21] InvFile = row[21]
}; };
public static void Write(this BinaryWriter binaryWriter, Misc misc)
{
// Noop
}
public static Misc ReadItemMisc(this BinaryReader binaryReader)
{
var result = new Misc();
Item.Read(binaryReader, result);
return result;
}
} }
} }

View File

@ -1,6 +1,7 @@
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -22,5 +23,19 @@ namespace OpenDiablo2.Common.Models
WeaponClass = row[34], WeaponClass = row[34],
InvFile = row[45] InvFile = row[45]
}; };
public static void Write(this BinaryWriter binaryWriter, Weapon weapon)
{
(weapon as Item).Write(binaryWriter);
binaryWriter.Write(weapon.WeaponClass);
}
public static Weapon ReadItemWeapon(this BinaryReader binaryReader)
{
var result = new Weapon();
Item.Read(binaryReader, result);
result.WeaponClass = binaryReader.ReadString();
return result;
}
} }
} }

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.Common.Models namespace OpenDiablo2.Common.Models
{ {
@ -18,40 +19,9 @@ namespace OpenDiablo2.Common.Models
public bool IsTransparent { get; internal set; } public bool IsTransparent { get; internal set; }
public eDrawEffect DrawEffect { get; internal set; } public eDrawEffect DrawEffect { get; internal set; }
public eWeaponClass WeaponClass { get; internal set; } public eWeaponClass WeaponClass { get; internal set; }
public string ShieldCode { get; internal set; }
public string WeaponCode { get; internal set; }
// TODO: Move logic somewhere else.
// TODO: Consider two hand weapons.
public string GetDCCPath(eArmorType armorType)
{
string result = null;
var weaponClass = COF.WeaponClass;
if(CompositType != eCompositType.RightArm && CompositType != eCompositType.RightHand)
{
weaponClass = eWeaponClass.HandToHand;
}
if(CompositType == eCompositType.Shield)
{
result = $"{ResourcePaths.PlayerAnimationBase}\\{COF.Hero.ToToken()}\\{CompositType.ToToken()}\\{COF.Hero.ToToken()}{CompositType.ToToken()}{ShieldCode}{COF.MobMode.ToToken()}{weaponClass.ToToken()}.dcc";
}
else if (CompositType == eCompositType.RightHand)
{
result = $"{ResourcePaths.PlayerAnimationBase}\\{COF.Hero.ToToken()}\\{CompositType.ToToken()}\\{COF.Hero.ToToken()}{CompositType.ToToken()}{WeaponCode}{COF.MobMode.ToToken()}{weaponClass.ToToken()}.dcc";
}
else
{
result = $"{ResourcePaths.PlayerAnimationBase}\\{COF.Hero.ToToken()}\\{CompositType.ToToken()}\\{COF.Hero.ToToken()}{CompositType.ToToken()}{armorType.ToToken()}{COF.MobMode.ToToken()}{weaponClass.ToToken()}.dcc";
}
return result;
}
} }
public eHero Hero { get; private set; } public eHero Hero { get; private set; }
public eWeaponClass WeaponClass { get; private set; }
public eMobMode MobMode { get; private set; } public eMobMode MobMode { get; private set; }
public List<AnimationData> Animations { get; private set; } public List<AnimationData> Animations { get; private set; }
@ -63,11 +33,10 @@ namespace OpenDiablo2.Common.Models
public int FramesPerDirection { get; internal set; } public int FramesPerDirection { get; internal set; }
public int NumberOfLayers { get; internal set; } public int NumberOfLayers { get; internal set; }
public static MPQCOF Load(Stream stream, Dictionary<string, List<AnimationData>> animations, eHero hero, eWeaponClass weaponClass, eMobMode mobMode, string ShieldCode, string weaponCode) public static MPQCOF Load(Stream stream, Dictionary<string, List<AnimationData>> animations, eHero hero, eMobMode mobMode, PlayerEquipment equipment)
{ {
var result = new MPQCOF var result = new MPQCOF
{ {
WeaponClass = weaponClass,
MobMode = mobMode, MobMode = mobMode,
Hero = hero Hero = hero
}; };
@ -95,8 +64,6 @@ namespace OpenDiablo2.Common.Models
layer.IsTransparent = br.ReadByte() != 0; layer.IsTransparent = br.ReadByte() != 0;
layer.DrawEffect = (eDrawEffect)br.ReadByte(); layer.DrawEffect = (eDrawEffect)br.ReadByte();
layer.WeaponClass = Encoding.ASCII.GetString(br.ReadBytes(4)).Trim('\0').ToWeaponClass(); layer.WeaponClass = Encoding.ASCII.GetString(br.ReadBytes(4)).Trim('\0').ToWeaponClass();
layer.ShieldCode = ShieldCode;
layer.WeaponCode = weaponCode;
layers.Add(layer); layers.Add(layer);
result.CompositLayers[layer.CompositType] = layerIdx; result.CompositLayers[layer.CompositType] = layerIdx;
} }
@ -104,7 +71,7 @@ namespace OpenDiablo2.Common.Models
result.AnimationFrames = br.ReadBytes(result.FramesPerDirection).Select(x => (eAnimationFrame)x); result.AnimationFrames = br.ReadBytes(result.FramesPerDirection).Select(x => (eAnimationFrame)x);
result.Priority = br.ReadBytes(result.FramesPerDirection * result.NumberOfLayers * result.NumberOfDirections).Select(x => (eCompositType)x).ToArray(); result.Priority = br.ReadBytes(result.FramesPerDirection * result.NumberOfLayers * result.NumberOfDirections).Select(x => (eCompositType)x).ToArray();
var cofName = $"{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}".ToUpper(); var cofName = $"{hero.ToToken()}{mobMode.ToToken()}{equipment.WeaponClass.ToToken()}".ToUpper();
result.Animations = animations[cofName]; result.Animations = animations[cofName];
br.Dispose(); br.Dispose();
return result; return result;

View File

@ -0,0 +1,124 @@
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Exceptions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Models.Mobs
{
public sealed class PlayerEquipment
{
public ItemInstance Head { get; set; }
public ItemInstance Neck { get; set; }
public ItemInstance Torso { get; set; }
public ItemInstance RightArm { get; set; }
public ItemInstance LeftArm { get; set; }
public ItemInstance RightRing { get; set; }
public ItemInstance LeftRing { get; set; }
public ItemInstance Feet { get; set; }
public ItemInstance Belt { get; set; }
public ItemInstance Gloves { get; set; }
public eWeaponClass WeaponClass
{
get
{
if (LeftArm?.Item is Weapon)
return ((Weapon)LeftArm.Item).WeaponClass.ToWeaponClass();
else if (RightArm?.Item is Weapon)
return ((Weapon)RightArm.Item).WeaponClass.ToWeaponClass();
else
return eWeaponClass.None;
}
}
public eArmorType ArmorType
{
get
{
// TODO: Make things happen here
return eArmorType.Lite;
}
}
public string HashKey
=> $"{Head?.Item.Name}{Neck?.Item.Name}{Torso?.Item.Name}{RightArm?.Item.Name}{LeftArm?.Item.Name}" +
$"{RightRing?.Item.Name}{LeftRing?.Item.Name}{Feet?.Item.Name}{Belt?.Item.Name}{Gloves?.Item.Name}";
public void EquipItem(string slot, ItemInstance item)
{
switch (slot.ToLower())
{
case "head":
Head = item;
break;
case "neck":
Neck = item;
break;
case "tors":
Torso = item;
break;
case "rarm":
RightArm = item;
break;
case "larm":
LeftArm = item;
break;
case "rrin":
RightRing = item;
break;
case "lrin":
LeftRing = item;
break;
case "belt":
Belt = item;
break;
case "feet":
Feet = item;
break;
case "glov":
Gloves = item;
break;
default:
throw new OpenDiablo2Exception($"Unknown slot name '{slot}'!");
}
}
}
public static class PlayerEquipmentHelper
{
public static void Write(this BinaryWriter binaryWriter, PlayerEquipment source)
{
binaryWriter.Write(source.Head);
binaryWriter.Write(source.Neck);
binaryWriter.Write(source.Torso);
binaryWriter.Write(source.RightArm);
binaryWriter.Write(source.LeftArm);
binaryWriter.Write(source.RightRing);
binaryWriter.Write(source.LeftRing);
binaryWriter.Write(source.Belt);
binaryWriter.Write(source.Feet);
binaryWriter.Write(source.Gloves);
}
public static PlayerEquipment ReadPlayerEquipment(this BinaryReader binaryReader)
{
return new PlayerEquipment
{
Head = binaryReader.ReadItemInstance(),
Neck = binaryReader.ReadItemInstance(),
Torso = binaryReader.ReadItemInstance(),
RightArm = binaryReader.ReadItemInstance(),
LeftArm = binaryReader.ReadItemInstance(),
RightRing = binaryReader.ReadItemInstance(),
LeftRing = binaryReader.ReadItemInstance(),
Belt = binaryReader.ReadItemInstance(),
Feet = binaryReader.ReadItemInstance(),
Gloves = binaryReader.ReadItemInstance()
};
}
}
}

View File

@ -24,21 +24,17 @@ namespace OpenDiablo2.Common.Models.Mobs
{ {
public class PlayerState : MobState public class PlayerState : MobState
{ {
public Guid UID { get; protected set; } = Guid.NewGuid();
public eHero HeroType { get; protected set; }
private readonly IHeroTypeConfig HeroTypeConfig; private readonly IHeroTypeConfig HeroTypeConfig;
private readonly ILevelExperienceConfig ExperienceConfig; private readonly ILevelExperienceConfig ExperienceConfig;
public Guid UID { get; protected set; } = Guid.NewGuid();
public eHero HeroType { get; protected set; }
public int ClientHash { get; protected set; } public int ClientHash { get; protected set; }
public byte MovementDirection { get; set; } = 0; public byte MovementDirection { get; set; } = 0;
public eMovementType MovementType { get; set; } = eMovementType.Stopped; // TODO: This needs to mess with MobMode somehow public eMovementType MovementType { get; set; } = eMovementType.Stopped; // TODO: This needs to mess with MobMode somehow
public eWeaponClass WeaponClass { get; set; } = eWeaponClass.HandToHand; // Temporary public eMobMode MobMode { get; set; } = eMobMode.PlayerTownWalk;
public eArmorType ArmorType { get; set; } = eArmorType.Lite; // Temporary public PlayerEquipment Equipment { get; set; } = new PlayerEquipment();
public eMobMode MobMode { get; set; } = eMobMode.PlayerTownWalk; // Temporary
// Remove when we're passing the full inventory. Used for animations.
public string ShieldCode { get; set; }
public string WeaponCode { get; set; }
// ---
// Player character stats // Player character stats
protected Stat Vitality; protected Stat Vitality;
@ -57,8 +53,6 @@ namespace OpenDiablo2.Common.Models.Mobs
protected Stat RunVelocity; protected Stat RunVelocity;
protected Stat RunDrain; protected Stat RunDrain;
public Dictionary<String, ItemInstance> Equipment = new Dictionary<string, ItemInstance> ();
public long Experience { get; protected set; } public long Experience { get; protected set; }
public PlayerState() : base() { } public PlayerState() : base() { }
@ -101,26 +95,7 @@ namespace OpenDiablo2.Common.Models.Mobs
RefreshDerived(); RefreshDerived();
} }
public void UpdateEquipment(string slot, ItemInstance item) public void UpdateEquipment(string slot, ItemInstance item) => Equipment.EquipItem(slot, item);
{
if(Equipment.ContainsKey(slot))
{
Equipment.Remove(slot);
}
Equipment.Add(slot, item);
if(item.Item is Weapon)
{
WeaponClass = ((Weapon)item.Item).WeaponClass.ToWeaponClass();
WeaponCode = item.Item.Code;
}
if(item.Item is Armor && slot == "larm") // Shield
{
ShieldCode = item.Item.Code;
}
}
#region Level and Experience #region Level and Experience
public long GetExperienceToLevel() public long GetExperienceToLevel()

View File

@ -28,12 +28,9 @@ namespace OpenDiablo2.Common.Models
public Guid UID { get; set; } public Guid UID { get; set; }
public string Name { get; set; } public string Name { get; set; }
public eHero Hero { get; set; } public eHero Hero { get; set; }
public eWeaponClass WeaponClass { get; set; }
public eArmorType ArmorType { get; set; }
public eMobMode MobMode { get; set; } public eMobMode MobMode { get; set; }
public PlayerLocationDetails LocationDetails { get; set; } public PlayerLocationDetails LocationDetails { get; set; }
public string ShieldCode { get; set; } public PlayerEquipment Equipment { get; set; }
public string WeaponCode { get; set; }
public byte[] GetBytes() public byte[] GetBytes()
{ {
@ -41,12 +38,9 @@ namespace OpenDiablo2.Common.Models
using (var writer = new BinaryWriter(stream)) using (var writer = new BinaryWriter(stream))
{ {
writer.Write((byte)Hero); writer.Write((byte)Hero);
writer.Write((byte)WeaponClass);
writer.Write((byte)ArmorType);
writer.Write((byte)MobMode); writer.Write((byte)MobMode);
writer.Write(Name); writer.Write(Name);
writer.Write(ShieldCode != null ? ShieldCode : ""); writer.Write(Equipment);
writer.Write(WeaponCode != null ? WeaponCode : "");
writer.Write(LocationDetails.GetBytes()); writer.Write(LocationDetails.GetBytes());
writer.Write(UID.ToByteArray()); writer.Write(UID.ToByteArray());
@ -64,12 +58,9 @@ namespace OpenDiablo2.Common.Models
var result = new PlayerInfo var result = new PlayerInfo
{ {
Hero = (eHero)reader.ReadByte(), Hero = (eHero)reader.ReadByte(),
WeaponClass = (eWeaponClass)reader.ReadByte(),
ArmorType = (eArmorType)reader.ReadByte(),
MobMode = (eMobMode)reader.ReadByte(), MobMode = (eMobMode)reader.ReadByte(),
Name = reader.ReadString(), Name = reader.ReadString(),
ShieldCode = reader.ReadString(), Equipment = reader.ReadPlayerEquipment(),
WeaponCode = reader.ReadString(),
LocationDetails = PlayerLocationDetails.FromBytes(reader.ReadBytes(PlayerLocationDetails.SizeInBytes)), LocationDetails = PlayerLocationDetails.FromBytes(reader.ReadBytes(PlayerLocationDetails.SizeInBytes)),
UID = new Guid(reader.ReadBytes(16)) UID = new Guid(reader.ReadBytes(16))
}; };
@ -97,11 +88,8 @@ namespace OpenDiablo2.Common.Models
PlayerY = source.GetPosition().Y, PlayerY = source.GetPosition().Y,
}, },
Name = source.Name, Name = source.Name,
WeaponClass = source.WeaponClass,
ArmorType = source.ArmorType,
MobMode = source.MobMode, MobMode = source.MobMode,
ShieldCode = source.ShieldCode, Equipment = source.Equipment
WeaponCode = source.WeaponCode
}; };
} }
} }

View File

@ -102,7 +102,11 @@
<Compile Include="Interfaces\UI\IPanel.cs" /> <Compile Include="Interfaces\UI\IPanel.cs" />
<Compile Include="Models\AnimationData.cs" /> <Compile Include="Models\AnimationData.cs" />
<Compile Include="Models\BitMuncher.cs" /> <Compile Include="Models\BitMuncher.cs" />
<Compile Include="Models\Item\Armor.cs" />
<Compile Include="Models\Item\ItemInstance.cs" /> <Compile Include="Models\Item\ItemInstance.cs" />
<Compile Include="Models\Item\Misc.cs" />
<Compile Include="Models\Item\Weapon.cs" />
<Compile Include="Models\Mobs\PlayerEquipment.cs" />
<Compile Include="Models\MPQCOF.cs" /> <Compile Include="Models\MPQCOF.cs" />
<Compile Include="Models\MPQDCC.cs" /> <Compile Include="Models\MPQDCC.cs" />
<Compile Include="Models\ItemContainerLayout.cs" /> <Compile Include="Models\ItemContainerLayout.cs" />
@ -143,9 +147,6 @@
<Compile Include="Models\ButtonLayout.cs" /> <Compile Include="Models\ButtonLayout.cs" />
<Compile Include="Models\LevelDetail.cs" /> <Compile Include="Models\LevelDetail.cs" />
<Compile Include="Models\Item\Item.cs" /> <Compile Include="Models\Item\Item.cs" />
<Compile Include="Models\Item\Armor.cs" />
<Compile Include="Models\Item\Weapon.cs" />
<Compile Include="Models\Item\Misc.cs" />
<Compile Include="Models\LevelPreset.cs" /> <Compile Include="Models\LevelPreset.cs" />
<Compile Include="Models\LevelType.cs" /> <Compile Include="Models\LevelType.cs" />
<Compile Include="Models\MapCellInfo.cs" /> <Compile Include="Models\MapCellInfo.cs" />

View File

@ -14,8 +14,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
using OpenDiablo2.Common;
using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Exceptions;
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using OpenDiablo2.Common.Models.Mobs;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -93,5 +97,59 @@ namespace OpenDiablo2.Core
public IEnumerable<string> GetTextFile(string fileName) public IEnumerable<string> GetTextFile(string fileName)
=> new StreamReader(mpqs[mpqLookup[fileName.ToLower()]].OpenFile(fileName)).ReadToEnd().Split('\n'); => new StreamReader(mpqs[mpqLookup[fileName.ToLower()]].OpenFile(fileName)).ReadToEnd().Split('\n');
public string GetCharacterDccPath(eHero hero, eMobMode mobMode, eCompositType compositType, PlayerEquipment equipment)
{
var fileName = ($@"{ResourcePaths.PlayerAnimationBase}\{hero.ToToken()}\{compositType.ToToken()}\{hero.ToToken()}{compositType.ToToken()}").ToLower();
switch (compositType)
{
case eCompositType.Head:
fileName += $"{equipment.Head?.Item.Code ?? eArmorType.Lite.ToToken()}{mobMode.ToToken()}";
return mpqLookup.ContainsKey($"{fileName}{equipment.WeaponClass.ToToken()}.dcc".ToLower())
? $"{fileName}{equipment.WeaponClass.ToToken()}.dcc".ToLower()
: $"{fileName}{eWeaponClass.HandToHand.ToToken()}.dcc".ToLower();
case eCompositType.Torso:
case eCompositType.Legs:
case eCompositType.RightArm:
case eCompositType.LeftArm:
fileName += $"{equipment.ArmorType.ToToken()}{mobMode.ToToken()}";
return mpqLookup.ContainsKey($"{fileName}{equipment.WeaponClass.ToToken()}.dcc".ToLower())
? $"{fileName}{equipment.WeaponClass.ToToken()}.dcc".ToLower()
: $"{fileName}{eWeaponClass.HandToHand.ToToken()}.dcc".ToLower();
case eCompositType.RightHand:
if (!(equipment.RightArm?.Item is Weapon))
return null;
fileName += $"{equipment.RightArm.Item.Code}{mobMode.ToToken()}{equipment.WeaponClass.ToToken()}.dcc".ToLower();
return fileName;
case eCompositType.LeftHand:
if (!(equipment.LeftArm?.Item is Weapon))
return null;
fileName += $"{equipment.LeftArm.Item.Code}{mobMode.ToToken()}{equipment.WeaponClass.ToToken()}.dcc".ToLower();
return fileName;
case eCompositType.Shield:
if (!(equipment.LeftArm?.Item is Armor))
return null;
fileName += $"{equipment.LeftArm.Item.Code}{mobMode.ToToken()}";
return mpqLookup.ContainsKey($"{fileName}{equipment.WeaponClass.ToToken()}.dcc".ToLower())
? $"{fileName}{equipment.WeaponClass.ToToken()}.dcc".ToLower()
: $"{fileName}{eWeaponClass.HandToHand.ToToken()}.dcc".ToLower();
// TODO: Figure these out...
case eCompositType.Special1:
case eCompositType.Special2:
fileName += $"{equipment.ArmorType.ToToken()}{mobMode.ToToken()}{equipment.WeaponClass}.dcc".ToLower();
return mpqLookup.ContainsKey(fileName)
? fileName
: null; // TODO: Should we silence this?
case eCompositType.Special3:
case eCompositType.Special4:
case eCompositType.Special5:
case eCompositType.Special6:
case eCompositType.Special7:
case eCompositType.Special8:
default:
return null;
}
}
} }
} }

View File

@ -76,9 +76,8 @@ namespace OpenDiablo2.Core.Map_Engine
// TODO: This shouldn't be necessary... // TODO: This shouldn't be necessary...
cr.LocationDetails = info.LocationDetails; cr.LocationDetails = info.LocationDetails;
cr.MobMode = info.MobMode; cr.MobMode = info.MobMode;
cr.WeaponClass = info.WeaponClass;
cr.Hero = info.Hero; cr.Hero = info.Hero;
cr.ArmorType = info.ArmorType; cr.Equipment = info.Equipment;
} }
@ -89,11 +88,8 @@ namespace OpenDiablo2.Core.Map_Engine
cr.UID = info.UID; cr.UID = info.UID;
cr.LocationDetails = info.LocationDetails; cr.LocationDetails = info.LocationDetails;
cr.MobMode = info.MobMode; cr.MobMode = info.MobMode;
cr.WeaponClass = info.WeaponClass; cr.Equipment = info.Equipment;
cr.Hero = info.Hero; cr.Hero = info.Hero;
cr.ArmorType = info.ArmorType;
cr.ShieldCode = info.ShieldCode;
cr.WeaponCode = info.WeaponCode;
characterRenderers.Add(cr); characterRenderers.Add(cr);
cr.ResetAnimationData(); cr.ResetAnimationData();
} }

View File

@ -20,6 +20,7 @@ using OpenDiablo2.Common;
using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using OpenDiablo2.Common.Models.Mobs;
namespace OpenDiablo2.Core namespace OpenDiablo2.Core
{ {
@ -64,20 +65,27 @@ namespace OpenDiablo2.Core
public MPQDT1 GetMPQDT1(string resourcePath) public MPQDT1 GetMPQDT1(string resourcePath)
=> cache.AddOrGetExisting($"DT1::{resourcePath}", () => new MPQDT1(mpqProvider.GetStream(resourcePath))); => cache.AddOrGetExisting($"DT1::{resourcePath}", () => new MPQDT1(mpqProvider.GetStream(resourcePath)));
public MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode, string shieldCode, string weaponCode) public MPQCOF GetPlayerAnimation(eHero hero, eMobMode mobMode, PlayerEquipment equipment)
=> cache.AddOrGetExisting($"COF::{hero}::{weaponClass}::{mobMode}", () => => cache.AddOrGetExisting($"COF::{hero}{mobMode.ToToken()}{equipment.HashKey}", () =>
{ {
var path = $"{ResourcePaths.PlayerAnimationBase}\\{hero.ToToken()}\\COF\\{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}.cof";
return MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, weaponClass, mobMode, shieldCode, weaponCode); var path = $"{ResourcePaths.PlayerAnimationBase}\\{hero.ToToken()}\\COF\\{hero.ToToken()}{mobMode.ToToken()}{equipment.WeaponClass.ToToken()}.cof";
return MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, mobMode, equipment);
}, new System.Runtime.Caching.CacheItemPolicy { Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable }); }, new System.Runtime.Caching.CacheItemPolicy { Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable });
public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette) public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, PlayerEquipment equipment, Palette palette)
{ {
return cache.AddOrGetExisting($"PlayerDCC::{cofLayer.GetDCCPath(armorType)}", () => // TODO: Smarter hashing maybe
return cache.AddOrGetExisting($"PlayerDCC::{cofLayer.CompositType.ToToken()}{cofLayer.COF.MobMode.ToToken()}{equipment.HashKey}", () =>
{ {
byte[] binaryData; byte[] binaryData;
var streamPath = cofLayer.GetDCCPath(armorType); var streamPath = mpqProvider.GetCharacterDccPath(cofLayer.COF.Hero, cofLayer.COF.MobMode, cofLayer.CompositType, equipment);
// If stream path is null, there is nothing to load for this layer (this is NOT an error!)
if (streamPath == null)
return null;
using (var stream = mpqProvider.GetStream(streamPath)) using (var stream = mpqProvider.GetStream(streamPath))
{ {
if (stream == null) if (stream == null)

View File

@ -23,6 +23,7 @@ using OpenDiablo2.Common.Exceptions;
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Interfaces.Drawing; using OpenDiablo2.Common.Interfaces.Drawing;
using OpenDiablo2.Common.Models; using OpenDiablo2.Common.Models;
using OpenDiablo2.Common.Models.Mobs;
using SDL2; using SDL2;
namespace OpenDiablo2.SDL2_ namespace OpenDiablo2.SDL2_
@ -45,8 +46,7 @@ namespace OpenDiablo2.SDL2_
public Guid UID { get; set; } public Guid UID { get; set; }
public PlayerLocationDetails LocationDetails { get; set; } public PlayerLocationDetails LocationDetails { get; set; }
public eHero Hero { get; set; } public eHero Hero { get; set; }
public eWeaponClass WeaponClass { get; set; } public PlayerEquipment Equipment { get; set; }
public eArmorType ArmorType { get; set; }
public eMobMode MobMode { get; set; } public eMobMode MobMode { get; set; }
public string ShieldCode { get; set; } public string ShieldCode { get; set; }
public string WeaponCode { get; set; } public string WeaponCode { get; set; }
@ -134,12 +134,12 @@ namespace OpenDiablo2.SDL2_
if (currentDirectionCache != null) if (currentDirectionCache != null)
return; return;
animationData = resourceManager.GetPlayerAnimation(Hero, WeaponClass, MobMode, ShieldCode, WeaponCode); animationData = resourceManager.GetPlayerAnimation(Hero, MobMode, Equipment);
if (animationData == null) if (animationData == null)
throw new OpenDiablo2Exception("Could not locate animation for the character!"); throw new OpenDiablo2Exception("Could not locate animation for the character!");
var palette = paletteProvider.PaletteTable["Units"]; var palette = paletteProvider.PaletteTable["Units"];
CacheFrames(animationData.Layers.Select(layer => resourceManager.GetPlayerDCC(layer, ArmorType, palette)).ToArray()); CacheFrames(animationData.Layers.Select(layer => resourceManager.GetPlayerDCC(layer, Equipment, palette)).ToArray());
} }
private unsafe void CacheFrames(MPQDCC[] layerData) private unsafe void CacheFrames(MPQDCC[] layerData)