diff --git a/OpenDiablo2.Common/Enums/eItemContainerType.cs b/OpenDiablo2.Common/Enums/eItemContainerType.cs index e27fbae6..826607ba 100644 --- a/OpenDiablo2.Common/Enums/eItemContainerType.cs +++ b/OpenDiablo2.Common/Enums/eItemContainerType.cs @@ -1,4 +1,6 @@ -namespace OpenDiablo2.Common.Enums +using System.Collections.Generic; + +namespace OpenDiablo2.Common.Enums { public enum eItemContainerType { @@ -12,4 +14,4 @@ Ring, Generic } -} +} \ No newline at end of file diff --git a/OpenDiablo2.Common/Enums/eMessageFrameType.cs b/OpenDiablo2.Common/Enums/eMessageFrameType.cs index b0d7bf35..8f244887 100644 --- a/OpenDiablo2.Common/Enums/eMessageFrameType.cs +++ b/OpenDiablo2.Common/Enums/eMessageFrameType.cs @@ -12,7 +12,6 @@ MoveRequest = 0x06, PlayerMove = 0x07, - MAX = 0xFF, // NOTE: // You absolutely cannot have a higher ID than this without // changing the message header to multi-byte for ALL frame types!!! diff --git a/OpenDiablo2.Common/Interfaces/Data/IResourceManager.cs b/OpenDiablo2.Common/Interfaces/Data/IResourceManager.cs index e5b5a5c6..cceb123f 100644 --- a/OpenDiablo2.Common/Interfaces/Data/IResourceManager.cs +++ b/OpenDiablo2.Common/Interfaces/Data/IResourceManager.cs @@ -19,7 +19,7 @@ namespace OpenDiablo2.Common.Interfaces MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelType levelType); MPQDT1 GetMPQDT1(string resourcePath); Palette GetPalette(string paletteFile); - MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode); + MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode, string ShieldCode, string weaponCode); MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette); Dictionary> Animations { get; } diff --git a/OpenDiablo2.Common/Interfaces/Drawing/ICharacterRenderer.cs b/OpenDiablo2.Common/Interfaces/Drawing/ICharacterRenderer.cs index 8f2cac23..bc04b23f 100644 --- a/OpenDiablo2.Common/Interfaces/Drawing/ICharacterRenderer.cs +++ b/OpenDiablo2.Common/Interfaces/Drawing/ICharacterRenderer.cs @@ -11,8 +11,10 @@ namespace OpenDiablo2.Common.Interfaces.Drawing eHero Hero { get; set; } eWeaponClass WeaponClass { 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 Render(int pixelOffsetX, int pixelOffsetY); void ResetAnimationData(); diff --git a/OpenDiablo2.Common/Interfaces/IGameState.cs b/OpenDiablo2.Common/Interfaces/IGameState.cs index 84a87221..4beddbe4 100644 --- a/OpenDiablo2.Common/Interfaces/IGameState.cs +++ b/OpenDiablo2.Common/Interfaces/IGameState.cs @@ -16,8 +16,8 @@ namespace OpenDiablo2.Common.Interfaces Palette CurrentPalette { get; } IEnumerable PlayerInfos { get; } - Item SelectedItem { get; } - void SelectItem(Item item); + ItemInstance SelectedItem { get; } + void SelectItem(ItemInstance item); int CameraOffset { get; set; } diff --git a/OpenDiablo2.Common/Interfaces/IItemInstance.cs b/OpenDiablo2.Common/Interfaces/IItemInstance.cs new file mode 100644 index 00000000..00d4d858 --- /dev/null +++ b/OpenDiablo2.Common/Interfaces/IItemInstance.cs @@ -0,0 +1,30 @@ +/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C# + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using OpenDiablo2.Common.Models; +using System; +using System.Runtime.Caching; + +namespace OpenDiablo2.Common.Interfaces +{ + public interface IItemInstance + { + Item Item { get; } + string Name { get; } + int Level { get; } + bool Identified { get; } + } +} diff --git a/OpenDiablo2.Common/Interfaces/IItemManager.cs b/OpenDiablo2.Common/Interfaces/IItemManager.cs index 1d91c3e9..746f6030 100644 --- a/OpenDiablo2.Common/Interfaces/IItemManager.cs +++ b/OpenDiablo2.Common/Interfaces/IItemManager.cs @@ -6,5 +6,6 @@ namespace OpenDiablo2.Common.Interfaces public interface IItemManager { Item getItem(string code); + ItemInstance getItemInstance(string code); } } diff --git a/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs b/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs index 236cebc2..d679ebfa 100644 --- a/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs +++ b/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs @@ -1,4 +1,5 @@ -using System; +using OpenDiablo2.Common.Models.Mobs; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -47,8 +48,6 @@ namespace OpenDiablo2.Common.Interfaces.Mobs int StartingSkill { get; } string BaseWeaponClass { get; } - IEnumerable ItemNames { get; } - IEnumerable ItemLocs { get; } - IEnumerable ItemCounts { get; } + List InitialEquipment { get; } } } diff --git a/OpenDiablo2.Common/Interfaces/UI/IItemContainer.cs b/OpenDiablo2.Common/Interfaces/UI/IItemContainer.cs index fb233f05..585420e1 100644 --- a/OpenDiablo2.Common/Interfaces/UI/IItemContainer.cs +++ b/OpenDiablo2.Common/Interfaces/UI/IItemContainer.cs @@ -6,10 +6,10 @@ namespace OpenDiablo2.Common.Interfaces { public interface IItemContainer : IDisposable { - Item ContainedItem { get; } + ItemInstance ContainedItem { get; } Point Location { get; set; } - void SetContainedItem(Item containedItem); + void SetContainedItem(ItemInstance containedItem); void Render(); void Update(); } diff --git a/OpenDiablo2.Common/Models/Item/Armor.cs b/OpenDiablo2.Common/Models/Item/Armor.cs index 8b6fe19a..41f882ff 100644 --- a/OpenDiablo2.Common/Models/Item/Armor.cs +++ b/OpenDiablo2.Common/Models/Item/Armor.cs @@ -1,4 +1,5 @@ -using OpenDiablo2.Common.Interfaces; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -9,7 +10,7 @@ namespace OpenDiablo2.Common.Models { public sealed class Armor : Item { - + public string Type { get; internal set; } } public static class ArmorHelper diff --git a/OpenDiablo2.Common/Models/Item/ItemInstance.cs b/OpenDiablo2.Common/Models/Item/ItemInstance.cs new file mode 100644 index 00000000..744fd606 --- /dev/null +++ b/OpenDiablo2.Common/Models/Item/ItemInstance.cs @@ -0,0 +1,25 @@ +using OpenDiablo2.Common.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models +{ + /** + * Base Item class, contains common attributes for all item types. + **/ + public class ItemInstance : IItemInstance + { + public Item Item { get; internal set; } + public string Name { get; internal set; } + public int Level { get; internal set; } + public bool Identified { get; internal set; } + + public ItemInstance(Item item) + { + Item = item; + } + } +} diff --git a/OpenDiablo2.Common/Models/Item/Weapon.cs b/OpenDiablo2.Common/Models/Item/Weapon.cs index 6e1d595b..8c0782ed 100644 --- a/OpenDiablo2.Common/Models/Item/Weapon.cs +++ b/OpenDiablo2.Common/Models/Item/Weapon.cs @@ -9,7 +9,7 @@ namespace OpenDiablo2.Common.Models { public sealed class Weapon : Item { - + public string WeaponClass { get; internal set; } } public static class WeaponHelper @@ -19,6 +19,7 @@ namespace OpenDiablo2.Common.Models { Name = row[0], Code = row[2], + WeaponClass = row[34], InvFile = row[45] }; } diff --git a/OpenDiablo2.Common/Models/MPQCOF.cs b/OpenDiablo2.Common/Models/MPQCOF.cs index 71a61000..86c5cc46 100644 --- a/OpenDiablo2.Common/Models/MPQCOF.cs +++ b/OpenDiablo2.Common/Models/MPQCOF.cs @@ -18,10 +18,33 @@ namespace OpenDiablo2.Common.Models public bool IsTransparent { get; internal set; } public eDrawEffect DrawEffect { 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) { - var result = $"{ResourcePaths.PlayerAnimationBase}\\{COF.Hero.ToToken()}\\{CompositType.ToToken()}\\{COF.Hero.ToToken()}{CompositType.ToToken()}{armorType.ToToken()}{COF.MobMode.ToToken()}{COF.WeaponClass.ToToken()}.dcc"; + 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; } @@ -35,7 +58,7 @@ namespace OpenDiablo2.Common.Models public IEnumerable Layers { get; private set; } public IEnumerable AnimationFrames { get; private set; } - public static MPQCOF Load(Stream stream, Dictionary> animations, eHero hero, eWeaponClass weaponClass, eMobMode mobMode) + public static MPQCOF Load(Stream stream, Dictionary> animations, eHero hero, eWeaponClass weaponClass, eMobMode mobMode, string ShieldCode, string weaponCode) { var result = new MPQCOF { @@ -66,6 +89,8 @@ namespace OpenDiablo2.Common.Models layer.DrawEffect = (eDrawEffect)br.ReadByte(); layers.Add(layer); layer.WeaponClass = Encoding.ASCII.GetString(br.ReadBytes(4)).Trim('\0').ToWeaponClass(); + layer.ShieldCode = ShieldCode; + layer.WeaponCode = weaponCode; } result.Layers = layers; result.AnimationFrames = br.ReadBytes(framesPerDir).Select(x => (eAnimationFrame)x); diff --git a/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs b/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs index 56470342..df7d2fca 100644 --- a/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs +++ b/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs @@ -46,9 +46,7 @@ namespace OpenDiablo2.Common.Models.Mobs public int StartingSkill { get; protected set; } public string BaseWeaponClass { get; protected set; } - public IEnumerable ItemNames { get; } - public IEnumerable ItemLocs { get; } - public IEnumerable ItemCounts { get; } + public List InitialEquipment { get; } public HeroTypeConfig(int vitality, int strength, int dexterity, int energy, int health, int mana, int stamina, int manaRegen, @@ -59,7 +57,7 @@ namespace OpenDiablo2.Common.Models.Mobs int walkFrames, int runFrames, int swingFrames, int spellFrames, int getHitFrames, int bowFrames, int startingSkill, string baseWeaponClass, - List itemNames, List itemLocs, List itemCounts) + List initialEquipment) { StartingDexterity = dexterity; StartingVitality = vitality; @@ -97,12 +95,17 @@ namespace OpenDiablo2.Common.Models.Mobs StartingSkill = startingSkill; BaseWeaponClass = baseWeaponClass; - ItemNames = itemNames; - ItemLocs = itemLocs; - ItemCounts = itemCounts; + InitialEquipment = initialEquipment; } } + public struct InitialEquipment + { + public string name; + public string location; + public int count; + }; + public static class HeroTypeConfigHelper { public static IHeroTypeConfig ToHeroTypeConfig(this string[] row) @@ -134,15 +137,21 @@ namespace OpenDiablo2.Common.Models.Mobs // 57 58 59 60 61 62 // item9 item9loc item9count item10 item10loc item10count + + List itemNames = new List(); List itemLocs = new List(); List itemCounts = new List(); + List initialEquipment = new List(); for(int i = 33; i <= 60; i+=3) { - itemNames.Add(row[i]); - itemLocs.Add(row[i + 1]); - itemCounts.Add(Convert.ToInt32(row[i + 2])); + var item = new InitialEquipment(); + item.name = row[i]; + item.location = row[i + 1]; + item.count = Convert.ToInt32(row[i + 2]); + + initialEquipment.Add(item); } return new HeroTypeConfig( @@ -175,9 +184,7 @@ namespace OpenDiablo2.Common.Models.Mobs bowFrames: Convert.ToInt32(row[29]), startingSkill: Convert.ToInt32(row[31]), baseWeaponClass: row[32], - itemNames: itemNames, - itemLocs: itemLocs, - itemCounts: itemCounts); + initialEquipment: initialEquipment); } } } diff --git a/OpenDiablo2.Common/Models/Mobs/PlayerState.cs b/OpenDiablo2.Common/Models/Mobs/PlayerState.cs index 2a844200..5853b6fe 100644 --- a/OpenDiablo2.Common/Models/Mobs/PlayerState.cs +++ b/OpenDiablo2.Common/Models/Mobs/PlayerState.cs @@ -15,6 +15,7 @@ */ using System; +using System.Collections.Generic; using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Enums.Mobs; using OpenDiablo2.Common.Interfaces.Mobs; @@ -34,6 +35,11 @@ namespace OpenDiablo2.Common.Models.Mobs public eArmorType ArmorType { get; set; } = eArmorType.Lite; // Temporary 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 protected Stat Vitality; protected Stat Strength; @@ -51,7 +57,7 @@ namespace OpenDiablo2.Common.Models.Mobs protected Stat RunVelocity; protected Stat RunDrain; - + public Dictionary Equipment = new Dictionary (); public long Experience { get; protected set; } @@ -81,6 +87,7 @@ namespace OpenDiablo2.Common.Models.Mobs Experience = experience; // how much total exp do they have + HeroType = herotype; HeroTypeConfig = heroconfig; ExperienceConfig = expconfig; @@ -94,6 +101,27 @@ namespace OpenDiablo2.Common.Models.Mobs RefreshDerived(); } + public void UpdateEquipment(string slot, ItemInstance 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 public long GetExperienceToLevel() { diff --git a/OpenDiablo2.Common/Models/PlayerInfo.cs b/OpenDiablo2.Common/Models/PlayerInfo.cs index d04f66cb..6f519769 100644 --- a/OpenDiablo2.Common/Models/PlayerInfo.cs +++ b/OpenDiablo2.Common/Models/PlayerInfo.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text; using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Models.Mobs; @@ -31,41 +32,53 @@ namespace OpenDiablo2.Common.Models public eArmorType ArmorType { get; set; } public eMobMode MobMode { get; set; } public PlayerLocationDetails LocationDetails { get; set; } + public string ShieldCode { get; set; } + public string WeaponCode { get; set; } public byte[] GetBytes() { - var result = new List(); - 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(nameBytes.Length)); - result.AddRange(nameBytes); - result.AddRange(LocationDetails.GetBytes()); - result.AddRange(UID.ToByteArray()); - return result.ToArray(); + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + writer.Write((byte)Hero); + writer.Write((byte)WeaponClass); + writer.Write((byte)ArmorType); + writer.Write((byte)MobMode); + writer.Write(Name); + writer.Write(ShieldCode != null ? ShieldCode : ""); + writer.Write(WeaponCode != null ? WeaponCode : ""); + writer.Write(LocationDetails.GetBytes()); + writer.Write(UID.ToByteArray()); + + return stream.ToArray(); + } } public static PlayerInfo FromBytes(byte[] data, int offset = 0) { - var result = new PlayerInfo - { - Hero = (eHero)data[offset], - WeaponClass = (eWeaponClass)data[offset + 1], - ArmorType = (eArmorType)data[offset + 2], - 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, uidBytes, 0, 16); - result.UID = new Guid(uidBytes); - return result; + using (var stream = new MemoryStream(data)) + using (var reader = new BinaryReader(stream)) + { + reader.ReadBytes(offset); // Skip + + var result = new PlayerInfo + { + Hero = (eHero)reader.ReadByte(), + WeaponClass = (eWeaponClass)reader.ReadByte(), + ArmorType = (eArmorType)reader.ReadByte(), + MobMode = (eMobMode)reader.ReadByte(), + Name = reader.ReadString(), + ShieldCode = reader.ReadString(), + WeaponCode = reader.ReadString(), + LocationDetails = PlayerLocationDetails.FromBytes(reader.ReadBytes(PlayerLocationDetails.SizeInBytes)), + UID = new Guid(reader.ReadBytes(16)) + }; + + return result; + } } - public int SizeInBytes => 8 + Encoding.UTF8.GetByteCount(Name) + PlayerLocationDetails.SizeInBytes + 16; + public int SizeInBytes => 8 + Encoding.UTF8.GetByteCount(Name) + PlayerLocationDetails.SizeInBytes + 16; } @@ -86,7 +99,9 @@ namespace OpenDiablo2.Common.Models Name = source.Name, WeaponClass = source.WeaponClass, ArmorType = source.ArmorType, - MobMode = source.MobMode + MobMode = source.MobMode, + ShieldCode = source.ShieldCode, + WeaponCode = source.WeaponCode }; } } diff --git a/OpenDiablo2.Common/OpenDiablo2.Common.csproj b/OpenDiablo2.Common/OpenDiablo2.Common.csproj index c5a5cd50..b639eb84 100644 --- a/OpenDiablo2.Common/OpenDiablo2.Common.csproj +++ b/OpenDiablo2.Common/OpenDiablo2.Common.csproj @@ -87,6 +87,7 @@ + @@ -100,6 +101,7 @@ + diff --git a/OpenDiablo2.Core/GameState/GameState.cs b/OpenDiablo2.Core/GameState/GameState.cs index f0fa7309..0c4e8a93 100644 --- a/OpenDiablo2.Core/GameState/GameState.cs +++ b/OpenDiablo2.Core/GameState/GameState.cs @@ -40,7 +40,7 @@ namespace OpenDiablo2.Core.GameState_ public int Seed { get; internal set; } - public Item SelectedItem { get; internal set; } + public ItemInstance SelectedItem { get; internal set; } public object ThreadLocker { get; } = new object(); public int CameraOffset { get; set; } = 0; @@ -288,7 +288,7 @@ namespace OpenDiablo2.Core.GameState_ throw new NotImplementedException(); } - public void SelectItem(Item item) + public void SelectItem(ItemInstance item) { if (item == null) { @@ -296,7 +296,7 @@ namespace OpenDiablo2.Core.GameState_ } else { - var cursorsprite = renderWindow.LoadSprite(ResourcePaths.GeneratePathForItem(item.InvFile), Palettes.Units); + var cursorsprite = renderWindow.LoadSprite(ResourcePaths.GeneratePathForItem(item.Item.InvFile), Palettes.Units); renderWindow.MouseCursor = renderWindow.LoadCursor(cursorsprite, 0, new Point(cursorsprite.FrameSize.Width / 2, cursorsprite.FrameSize.Height / 2)); } diff --git a/OpenDiablo2.Core/ItemManager.cs b/OpenDiablo2.Core/ItemManager.cs index cf500608..627c7a23 100644 --- a/OpenDiablo2.Core/ItemManager.cs +++ b/OpenDiablo2.Core/ItemManager.cs @@ -36,5 +36,10 @@ namespace OpenDiablo2.Core return item; } + public ItemInstance getItemInstance(string code) + { + return new ItemInstance(getItem(code)); + } + } } diff --git a/OpenDiablo2.Core/Map Engine/MapEngine.cs b/OpenDiablo2.Core/Map Engine/MapEngine.cs index 0b3e77e3..7fbb8af6 100644 --- a/OpenDiablo2.Core/Map Engine/MapEngine.cs +++ b/OpenDiablo2.Core/Map Engine/MapEngine.cs @@ -92,6 +92,8 @@ namespace OpenDiablo2.Core.Map_Engine cr.WeaponClass = info.WeaponClass; cr.Hero = info.Hero; cr.ArmorType = info.ArmorType; + cr.ShieldCode = info.ShieldCode; + cr.WeaponCode = info.WeaponCode; characterRenderers.Add(cr); cr.ResetAnimationData(); } diff --git a/OpenDiablo2.Core/ResourceManager.cs b/OpenDiablo2.Core/ResourceManager.cs index a26ba708..1ab68c8f 100644 --- a/OpenDiablo2.Core/ResourceManager.cs +++ b/OpenDiablo2.Core/ResourceManager.cs @@ -63,11 +63,11 @@ namespace OpenDiablo2.Core public MPQDT1 GetMPQDT1(string resourcePath) => cache.AddOrGetExisting($"DT1::{resourcePath}", () => new MPQDT1(mpqProvider.GetStream(resourcePath))); - public MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode) + public MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode, string shieldCode, string weaponCode) => cache.AddOrGetExisting($"COF::{hero}::{weaponClass}::{mobMode}", () => { var path = $"{ResourcePaths.PlayerAnimationBase}\\{hero.ToToken()}\\COF\\{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}.cof"; - return MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, weaponClass, mobMode); + return MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, weaponClass, mobMode, shieldCode, weaponCode); }); public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette) diff --git a/OpenDiablo2.Core/UI/InventoryPanel.cs b/OpenDiablo2.Core/UI/InventoryPanel.cs index 009f0e9f..64ecd8f1 100644 --- a/OpenDiablo2.Core/UI/InventoryPanel.cs +++ b/OpenDiablo2.Core/UI/InventoryPanel.cs @@ -70,51 +70,39 @@ namespace OpenDiablo2.Core.UI helmContainer = createItemContainer(eItemContainerType.Helm); helmContainer.Location = panelSprite.Location + new Size(135, 5); - helmContainer.SetContainedItem(itemManager.getItem("cap")); amuletContainer = createItemContainer(eItemContainerType.Amulet); amuletContainer.Location = panelSprite.Location + new Size(209, 34); - amuletContainer.SetContainedItem(itemManager.getItem("vip")); armorContainer = createItemContainer(eItemContainerType.Armor); armorContainer.Location = panelSprite.Location + new Size(135, 75); - armorContainer.SetContainedItem(itemManager.getItem("hla")); leftHandContainer = createItemContainer(eItemContainerType.Weapon); leftHandContainer.Location = panelSprite.Location + new Size(20, 47); - leftHandContainer.SetContainedItem(itemManager.getItem("ame")); rightHandContainer = createItemContainer(eItemContainerType.Weapon); rightHandContainer.Location = panelSprite.Location + new Size(253, 47); - rightHandContainer.SetContainedItem(itemManager.getItem("paf")); secondaryLeftHandContainer = createItemContainer(eItemContainerType.Weapon); secondaryLeftHandContainer.Location = panelSprite.Location + new Size(24, 45); - secondaryLeftHandContainer.SetContainedItem(itemManager.getItem("crs")); secondaryRightHandContainer = createItemContainer(eItemContainerType.Weapon); secondaryRightHandContainer.Location = panelSprite.Location + new Size(257, 45); - secondaryRightHandContainer.SetContainedItem(itemManager.getItem("kit")); beltContainer = createItemContainer(eItemContainerType.Belt); beltContainer.Location = panelSprite.Location + new Size(136, 178); - beltContainer.SetContainedItem(itemManager.getItem("vbl")); ringtLeftContainer = createItemContainer(eItemContainerType.Ring); ringtLeftContainer.Location = panelSprite.Location + new Size(95, 179); - ringtLeftContainer.SetContainedItem(itemManager.getItem("rin")); ringtRightContainer = createItemContainer(eItemContainerType.Ring); ringtRightContainer.Location = panelSprite.Location + new Size(209, 179); - ringtRightContainer.SetContainedItem(itemManager.getItem("rin")); gloveContainer = createItemContainer(eItemContainerType.Glove); gloveContainer.Location = panelSprite.Location + new Size(20, 179); - gloveContainer.SetContainedItem(itemManager.getItem("tgl")); bootsContainer = createItemContainer(eItemContainerType.Boots); bootsContainer.Location = panelSprite.Location + new Size(251, 178); - bootsContainer.SetContainedItem(itemManager.getItem("lbt")); } public eButtonType PanelType => eButtonType.MinipanelInventory; diff --git a/OpenDiablo2.Core/UI/ItemContainer.cs b/OpenDiablo2.Core/UI/ItemContainer.cs index 874e0819..7de07d7d 100644 --- a/OpenDiablo2.Core/UI/ItemContainer.cs +++ b/OpenDiablo2.Core/UI/ItemContainer.cs @@ -17,7 +17,7 @@ namespace OpenDiablo2.Core.UI private readonly ItemContainerLayout itemContainerLayout; private readonly IMouseInfoProvider mouseInfoProvider; - public Item ContainedItem { get; internal set; } + public ItemInstance ContainedItem { get; internal set; } private readonly Dictionary sprites = new Dictionary(); @@ -52,13 +52,13 @@ namespace OpenDiablo2.Core.UI this.Size = placeholderSprite.FrameSize; // For all but generic size is equal to the placeholder size. Source: me. } - public void SetContainedItem(Item containedItem) + public void SetContainedItem(ItemInstance containedItem) { ContainedItem = containedItem; if (ContainedItem != null) { - sprite = renderWindow.LoadSprite(ResourcePaths.GeneratePathForItem(this.ContainedItem.InvFile), Palettes.Units, true); + sprite = renderWindow.LoadSprite(ResourcePaths.GeneratePathForItem(this.ContainedItem.Item.InvFile), Palettes.Units, true); sprite.Location = new Point(location.X, location.Y + sprite.LocalFrameSize.Height); } } @@ -108,7 +108,10 @@ namespace OpenDiablo2.Core.UI public void Dispose() { - sprite.Dispose(); + if(sprite != null) + { + sprite.Dispose(); + } } } } diff --git a/OpenDiablo2.GameServer/GameServer.cs b/OpenDiablo2.GameServer/GameServer.cs index 4f543b7e..b2f264cb 100644 --- a/OpenDiablo2.GameServer/GameServer.cs +++ b/OpenDiablo2.GameServer/GameServer.cs @@ -12,17 +12,19 @@ namespace OpenDiablo2.GameServer_ private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private readonly IMobManager mobManager; - private readonly IEngineDataManager engineDataManager; - + private readonly IEngineDataManager engineDataManager; + private readonly IItemManager itemManager; + public int Seed { get; private set; } public IEnumerable Players => mobManager.Players; const double Deg2Rad = Math.PI / 180.0; - public GameServer(IMobManager mobManager, IEngineDataManager engineDataManager) + public GameServer(IMobManager mobManager, IEngineDataManager engineDataManager, IItemManager itemManager) { this.mobManager = mobManager; this.engineDataManager = engineDataManager; + this.itemManager = itemManager; } public void InitializeNewGame() @@ -53,8 +55,9 @@ namespace OpenDiablo2.GameServer_ else { log.Error("Error: Hero Config not loaded for '" + heroType.ToString() + "'."); - heroConfig = new HeroTypeConfig(10, 10, 10, 10, 10, 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 9, - 1, 10, 10, 10, 10, 10, 10, 0, "hth", new List(), new List(), new List()); + // Do we even need a default? + //heroConfig = new HeroTypeConfig(10, 10, 10, 10, 10, 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 9, + // 1, 10, 10, 10, 10, 10, 10, 0, "hth"); // TODO: should we have a more robust default hero config? // or should we just fail in some way here? // ... we should probably just fail here @@ -63,6 +66,17 @@ namespace OpenDiablo2.GameServer_ var newPlayer = new PlayerState(clientHash, playerName, mobManager.GetNextAvailableMobId(), 1, 20.0f, 20.0f, 10, 10, 10, 10, 0, heroType, heroConfig, expConfig); + // This is probably not the right place to do this. + // Only add items with a location set, the other ones go into the inventory - that we do not support yet + foreach (var item in heroConfig.InitialEquipment) + { + if (item.location.Length > 0) + { + newPlayer.UpdateEquipment(item.location, itemManager.getItemInstance(item.name)); + } + } + + mobManager.AddPlayer(newPlayer); return newPlayer.Id; } diff --git a/OpenDiablo2.SDL2/SDL2CharacterRenderer.cs b/OpenDiablo2.SDL2/SDL2CharacterRenderer.cs index f2210a7a..7975826b 100644 --- a/OpenDiablo2.SDL2/SDL2CharacterRenderer.cs +++ b/OpenDiablo2.SDL2/SDL2CharacterRenderer.cs @@ -49,6 +49,8 @@ namespace OpenDiablo2.SDL2_ public eWeaponClass WeaponClass { get; set; } public eArmorType ArmorType { get; set; } public eMobMode MobMode { get; set; } + public string ShieldCode { get; set; } + public string WeaponCode { get; set; } private readonly IntPtr renderer; @@ -132,7 +134,7 @@ namespace OpenDiablo2.SDL2_ return; } - animationData = resourceManager.GetPlayerAnimation(Hero, WeaponClass, MobMode); + animationData = resourceManager.GetPlayerAnimation(Hero, WeaponClass, MobMode, ShieldCode, WeaponCode); if (animationData == null) throw new OpenDiablo2Exception("Could not locate animation for the character!"); diff --git a/OpenDiablo2.ServiceBus/Message Frames/Client/MFJoinGame.cs b/OpenDiablo2.ServiceBus/Message Frames/Client/MFJoinGame.cs index c8209ce1..a3100b07 100644 --- a/OpenDiablo2.ServiceBus/Message Frames/Client/MFJoinGame.cs +++ b/OpenDiablo2.ServiceBus/Message Frames/Client/MFJoinGame.cs @@ -1,5 +1,8 @@ using System; +using System.Collections; +using System.IO; using System.Linq; +using System.Runtime.Serialization.Formatters.Binary; using System.Text; using OpenDiablo2.Common.Attributes; using OpenDiablo2.Common.Enums; @@ -16,19 +19,25 @@ namespace OpenDiablo2.ServiceBus.Message_Frames.Client public byte[] Data { - get => new byte[] { (byte)HeroType } - .Concat(BitConverter.GetBytes((UInt16)PlayerName.Length)) - .Concat(Encoding.UTF8.GetBytes(PlayerName)) - .ToArray(); - + get + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) { + writer.Write((byte)HeroType); + writer.Write(PlayerName); + + return stream.ToArray(); + } + } + set { - HeroType = (eHero)value[0]; - var playerNameLen = BitConverter.ToUInt16(value, 1); - PlayerName = Encoding.UTF8.GetString(value, 3, value.Length - 3); - - if (PlayerName.Length != playerNameLen) - throw new OpenDiablo2Exception("Invalid player length!"); + using(var stream = new MemoryStream(value)) + using(var reader = new BinaryReader(stream)) + { + HeroType = (eHero)reader.ReadByte(); + PlayerName = reader.ReadString(); + } } }