diff --git a/OpenDiablo2.Common.UT/UT_PlayerState.cs b/OpenDiablo2.Common.UT/UT_PlayerState.cs index e3dd2e82..1fe24713 100644 --- a/OpenDiablo2.Common.UT/UT_PlayerState.cs +++ b/OpenDiablo2.Common.UT/UT_PlayerState.cs @@ -14,10 +14,13 @@ namespace OpenDiablo2.Common.UT private PlayerState MakePlayer() { HeroTypeConfig herotypeconfig = new HeroTypeConfig(vitality: 20, strength: 20, dexterity: 20, - energy: 20, health: 50, mana: 40, stamina: 30, + energy: 20, health: 50, mana: 40, stamina: 30, manaRegen: 20, perlevelhealth: 3, perlevelmana: 1.5, perlevelstamina: 1, pervitalityhealth: 2, pervitalitystamina: 1, perenergymana: 1.5, - baseatkrating: -30, basedefrating: -30, perdexterityatkrating: 5, perdexteritydefrating: 4); + baseatkrating: -30, basedefrating: -30, perdexterityatkrating: 5, perdexteritydefrating: 4, + walkVelocity: 6, runVelocity: 9, runDrain: 20, walkFrames: 8, runFrames: 8, swingFrames: 8, + spellFrames: 8, getHitFrames: 8, bowFrames: 8, startingSkill: 0, baseWeaponClass: "hth", + itemNames: new List(), itemLocs: new List(), itemCounts: new List()); LevelExperienceConfig expconfig = new LevelExperienceConfig(new List() { 0, // level 0 diff --git a/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs b/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs index f204f9c5..028f97a2 100644 --- a/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs +++ b/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs @@ -12,5 +12,6 @@ namespace OpenDiablo2.Common.Interfaces List LevelDetails { get; } List Items { get; } Dictionary ExperienceConfigs { get; } + Dictionary HeroTypeConfigs { get; } } } diff --git a/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs b/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs index cbab772e..236cebc2 100644 --- a/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs +++ b/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs @@ -15,6 +15,7 @@ namespace OpenDiablo2.Common.Interfaces.Mobs int StartingHealth { get; } int StartingMana { get; } int StartingStamina { get; } + int StartingManaRegen { get; } double PerLevelHealth { get; } // NOTE: these are doubles because some classes have // e.g. 1.5 mana per level, which means they get 1 mana on even levels (e.g. -> 2) @@ -31,5 +32,23 @@ namespace OpenDiablo2.Common.Interfaces.Mobs int BaseDefenseRating { get; } int PerDexterityDefenseRating { get; } + + int WalkVelocity { get; } + int RunVelocity { get; } + int RunDrain { get; } + + int WalkFrames { get; } + int RunFrames { get; } + int SwingFrames { get; } + int SpellFrames { get; } + int GetHitFrames { get; } + int BowFrames { get; } + + int StartingSkill { get; } + string BaseWeaponClass { get; } + + IEnumerable ItemNames { get; } + IEnumerable ItemLocs { get; } + IEnumerable ItemCounts { get; } } } diff --git a/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs b/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs index e6a9c711..56470342 100644 --- a/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs +++ b/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs @@ -16,6 +16,7 @@ namespace OpenDiablo2.Common.Models.Mobs public int StartingHealth { get; protected set; } public int StartingMana { get; protected set; } public int StartingStamina { get; protected set; } + public int StartingManaRegen { get; protected set; } public double PerLevelHealth { get; protected set; } public double PerLevelMana { get; protected set; } @@ -31,11 +32,34 @@ namespace OpenDiablo2.Common.Models.Mobs public int BaseDefenseRating { get; protected set; } public int PerDexterityDefenseRating { get; protected set; } + public int WalkVelocity { get; protected set; } + public int RunVelocity { get; protected set; } + public int RunDrain { get; protected set; } + + public int WalkFrames { get; protected set; } + public int RunFrames { get; protected set; } + public int SwingFrames { get; protected set; } + public int SpellFrames { get; protected set; } + public int GetHitFrames { get; protected set; } + public int BowFrames { get; protected set; } + + 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 HeroTypeConfig(int vitality, int strength, int dexterity, int energy, - int health, int mana, int stamina, + int health, int mana, int stamina, int manaRegen, double perlevelhealth, double perlevelmana, double perlevelstamina, double pervitalityhealth, double pervitalitystamina, double perenergymana, - int baseatkrating, int basedefrating, int perdexterityatkrating, int perdexteritydefrating) + int baseatkrating, int basedefrating, int perdexterityatkrating, int perdexteritydefrating, + int walkVelocity, int runVelocity, int runDrain, + int walkFrames, int runFrames, int swingFrames, int spellFrames, int getHitFrames, int bowFrames, + int startingSkill, + string baseWeaponClass, + List itemNames, List itemLocs, List itemCounts) { StartingDexterity = dexterity; StartingVitality = vitality; @@ -44,6 +68,7 @@ namespace OpenDiablo2.Common.Models.Mobs StartingMana = mana; StartingHealth = health; StartingStamina = stamina; + StartingManaRegen = manaRegen; PerLevelHealth = perlevelhealth; PerLevelMana = perlevelmana; @@ -57,6 +82,102 @@ namespace OpenDiablo2.Common.Models.Mobs BaseDefenseRating = basedefrating; PerDexterityAttackRating = perdexterityatkrating; PerDexterityDefenseRating = perdexteritydefrating; + + WalkVelocity = walkVelocity; + RunVelocity = runVelocity; + RunDrain = runDrain; + + WalkFrames = walkFrames; + RunFrames = runFrames; + SwingFrames = swingFrames; + SpellFrames = spellFrames; + GetHitFrames = getHitFrames; + BowFrames = bowFrames; + + StartingSkill = startingSkill; + BaseWeaponClass = baseWeaponClass; + + ItemNames = itemNames; + ItemLocs = itemLocs; + ItemCounts = itemCounts; + } + } + + public static class HeroTypeConfigHelper + { + public static IHeroTypeConfig ToHeroTypeConfig(this string[] row) + { + // rows: + + // 0 1 2 3 4 5 6 7 + // class str dex int vit tot stamina hpadd + // 8 9 10 11 + // PercentStr PercentDex PercentInt PercentVit + // 12 13 14 15 + // ManaRegen ToHitFactor WalkVelocity RunVelocity + // 16 17 18 19 20 + // RunDrain Comment LifePerLevel StaminaPerLevel ManaPerLevel + // 21 22 23 + // LifePerVitality StaminaPerVitality ManaPerMagic + // 24 25 26 27 28 29 + // #walk #run #swing #spell #gethit #bow + // 30 31 32 + // BlockFactor StartSkill baseWClass + // 33 34 35 36 37 38 + // item1 item1loc item1count item2 item2loc item2count + // 39 40 41 42 43 44 + // item3 item3loc item3count item4 item4loc item4count + // 45 46 47 48 49 50 + // item5 item5loc item5count item6 item6loc item6count + // 51 52 53 54 55 56 + // item7 item7loc item7count item8 item8loc item8count + // 57 58 59 60 61 62 + // item9 item9loc item9count item10 item10loc item10count + + List itemNames = new List(); + List itemLocs = new List(); + List itemCounts = 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])); + } + + return new HeroTypeConfig( + vitality: Convert.ToInt32(row[4]), + strength: Convert.ToInt32(row[1]), + dexterity: Convert.ToInt32(row[2]), + energy: Convert.ToInt32(row[3]), + health: Convert.ToInt32(row[7]), + mana: 0, + stamina: Convert.ToInt32(row[6]), + manaRegen: Convert.ToInt32(row[12]), + perlevelhealth: Convert.ToInt32(row[18]) / 4.0, + perlevelmana: Convert.ToInt32(row[20]) / 4.0, + perlevelstamina: Convert.ToInt32(row[19]) / 4.0, + pervitalityhealth: Convert.ToInt32(row[21]) / 4.0, + pervitalitystamina: Convert.ToInt32(row[22]) / 4.0, + perenergymana: Convert.ToInt32(row[23]) / 4.0, + baseatkrating: Convert.ToInt32(row[13]), + basedefrating: Convert.ToInt32(row[30]), + perdexterityatkrating: 5, + perdexteritydefrating: 4, + walkVelocity: Convert.ToInt32(row[14]), + runVelocity: Convert.ToInt32(row[15]), + runDrain: Convert.ToInt32(row[16]), + walkFrames: Convert.ToInt32(row[24]), + runFrames: Convert.ToInt32(row[25]), + swingFrames: Convert.ToInt32(row[26]), + spellFrames: Convert.ToInt32(row[27]), + getHitFrames: Convert.ToInt32(row[28]), + bowFrames: Convert.ToInt32(row[29]), + startingSkill: Convert.ToInt32(row[31]), + baseWeaponClass: row[32], + itemNames: itemNames, + itemLocs: itemLocs, + itemCounts: itemCounts); } } } diff --git a/OpenDiablo2.Common/Models/Mobs/PlayerState.cs b/OpenDiablo2.Common/Models/Mobs/PlayerState.cs index 09e10e20..cb6d8bdf 100644 --- a/OpenDiablo2.Common/Models/Mobs/PlayerState.cs +++ b/OpenDiablo2.Common/Models/Mobs/PlayerState.cs @@ -25,6 +25,13 @@ namespace OpenDiablo2.Common.Models.Mobs protected Stat Stamina; protected Stat Mana; + protected Stat ManaRegen; + + protected Stat WalkVelocity; + protected Stat RunVelocity; + protected Stat RunDrain; + + public long Experience { get; protected set; } @@ -38,6 +45,7 @@ namespace OpenDiablo2.Common.Models.Mobs this.ClientHash = clientHash; Stamina = new Stat(0, 0, 0, true); Mana = new Stat(0, 0, 0, true); + ManaRegen = new Stat(0, heroconfig.StartingManaRegen, heroconfig.StartingManaRegen, true); Vitality = new Stat(0, vitality, vitality, true); Strength = new Stat(0, strength, strength, true); @@ -47,6 +55,10 @@ namespace OpenDiablo2.Common.Models.Mobs AttackRating = new Stat(0, 0, 0, false); DefenseRating = new Stat(0, 0, 0, false); + WalkVelocity = new Stat(0, heroconfig.WalkVelocity, 100, false); // TODO: what should max velocity be? (replace the 100) + RunVelocity = new Stat(0, heroconfig.RunVelocity, 100, false); // TODO: what should max velocity be? + RunDrain = new Stat(0, heroconfig.RunDrain, heroconfig.RunDrain, true); + Experience = experience; // how much total exp do they have HeroType = herotype; @@ -183,6 +195,10 @@ namespace OpenDiablo2.Common.Models.Mobs { Mana.AddCurrent(-mana); } + public int GetManaRegen() + { + return ManaRegen.GetCurrent(); + } #endregion Mana #region Stamina @@ -204,6 +220,21 @@ namespace OpenDiablo2.Common.Models.Mobs } #endregion Stamina + #region Movement + public int GetRunVelocity() + { + return RunVelocity.GetCurrent(); + } + public int GetWalkVeloicty() + { + return WalkVelocity.GetCurrent(); + } + public int GetRunDrain() + { + return RunDrain.GetCurrent(); + } + #endregion Movement + // TODO: when a player equips an item, apply the relevant modifiers to their stats // TODO: when a player unequips an item, remove the relevant modifiers from their stats } diff --git a/OpenDiablo2.Common/ResourcePaths.cs b/OpenDiablo2.Common/ResourcePaths.cs index fcd9573d..d4bb9ad6 100644 --- a/OpenDiablo2.Common/ResourcePaths.cs +++ b/OpenDiablo2.Common/ResourcePaths.cs @@ -139,6 +139,7 @@ namespace OpenDiablo2.Common // --- Character Data --- public static string Experience = "data\\global\\excel\\experience.txt"; + public static string CharStats = "data\\global\\excel\\charstats.txt"; public static string GeneratePathForItem(string spriteName) { diff --git a/OpenDiablo2.Core/EngineDataManager.cs b/OpenDiablo2.Core/EngineDataManager.cs index 0e0be4d9..89470428 100644 --- a/OpenDiablo2.Core/EngineDataManager.cs +++ b/OpenDiablo2.Core/EngineDataManager.cs @@ -21,6 +21,7 @@ namespace OpenDiablo2.Core public List LevelDetails { get; internal set; } public List Items { get; internal set; } = new List(); public Dictionary ExperienceConfigs { get; internal set; } = new Dictionary(); + public Dictionary HeroTypeConfigs { get; internal set; } = new Dictionary(); public EngineDataManager(IMPQProvider mpqProvider) { @@ -134,6 +135,7 @@ namespace OpenDiablo2.Core private void LoadCharacterData() { LoadExperienceConfig(); + LoadHeroTypeConfig(); } private void LoadExperienceConfig() @@ -147,5 +149,19 @@ namespace OpenDiablo2.Core ExperienceConfigs = data; } + + private void LoadHeroTypeConfig() + { + var data = mpqProvider + .GetTextFile(ResourcePaths.CharStats) + .Skip(1) + .Where(x => !String.IsNullOrWhiteSpace(x)) + .Select(x => x.Split('\t')) + .Where(x => x[0] != "Expansion") + .ToArray() + .ToDictionary(x => (eHero)Enum.Parse(typeof(eHero),x[0]), x => x.ToHeroTypeConfig()); + + HeroTypeConfigs = data; + } } } diff --git a/OpenDiablo2.GameServer/GameServer.cs b/OpenDiablo2.GameServer/GameServer.cs index 133f9c12..57c595a8 100644 --- a/OpenDiablo2.GameServer/GameServer.cs +++ b/OpenDiablo2.GameServer/GameServer.cs @@ -34,19 +34,34 @@ namespace OpenDiablo2.GameServer_ public int SpawnNewPlayer(int clientHash, string playerName, eHero heroType) { ILevelExperienceConfig expConfig = null; - try + IHeroTypeConfig heroConfig = null; + if (engineDataManager.ExperienceConfigs.ContainsKey(heroType)) { expConfig = engineDataManager.ExperienceConfigs[heroType]; } - catch(Exception e) + else { log.Error("Error: Experience Config not loaded for '" + heroType.ToString() + "'."); expConfig = new LevelExperienceConfig(new List() { 100 }); // TODO: should we have a more robust default experience config? // or should we just fail in some way here? } + if (engineDataManager.HeroTypeConfigs.ContainsKey(heroType)) + { + heroConfig = engineDataManager.HeroTypeConfigs[heroType]; + } + 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()); + // 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 + } + var newPlayer = new PlayerState(clientHash, playerName, mobManager.GetNextAvailableMobId(), 1, 20.0f, 20.0f, 10, 10, 10, 10, 0, heroType, - new HeroTypeConfig(10, 10, 10, 50, 50, 50, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), expConfig); + heroConfig, expConfig); mobManager.AddPlayer(newPlayer); return newPlayer.Id;