From 4085dc493b2fbfa49a525b405de82818373e8c73 Mon Sep 17 00:00:00 2001 From: nicholasdechiara <30356480+nicholasdechiara@users.noreply.github.com> Date: Tue, 4 Dec 2018 23:20:31 -0500 Subject: [PATCH] Game loads experience config data (#34) * Game loads experience config data * Stored based on hero type, handed off during spawnnewplayer --- OpenDiablo2.Common.UT/UT_PlayerState.cs | 2 +- .../Interfaces/IEngineDataManager.cs | 3 ++ .../Interfaces/Mobs/ILevelExperienceConfig.cs | 2 +- .../Models/Mobs/LevelExperienceConfig.cs | 47 +++++++++++++++++-- OpenDiablo2.Common/Models/Mobs/PlayerState.cs | 10 ++-- OpenDiablo2.Common/ResourcePaths.cs | 3 ++ OpenDiablo2.Core/EngineDataManager.cs | 23 +++++++++ OpenDiablo2.GameServer/GameServer.cs | 18 ++++++- 8 files changed, 95 insertions(+), 13 deletions(-) diff --git a/OpenDiablo2.Common.UT/UT_PlayerState.cs b/OpenDiablo2.Common.UT/UT_PlayerState.cs index 73501165..e3dd2e82 100644 --- a/OpenDiablo2.Common.UT/UT_PlayerState.cs +++ b/OpenDiablo2.Common.UT/UT_PlayerState.cs @@ -18,7 +18,7 @@ namespace OpenDiablo2.Common.UT perlevelhealth: 3, perlevelmana: 1.5, perlevelstamina: 1, pervitalityhealth: 2, pervitalitystamina: 1, perenergymana: 1.5, baseatkrating: -30, basedefrating: -30, perdexterityatkrating: 5, perdexteritydefrating: 4); - LevelExperienceConfig expconfig = new LevelExperienceConfig(new List() + LevelExperienceConfig expconfig = new LevelExperienceConfig(new List() { 0, // level 0 0, // level 1 diff --git a/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs b/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs index f8e58892..f204f9c5 100644 --- a/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs +++ b/OpenDiablo2.Common/Interfaces/IEngineDataManager.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces.Mobs; using OpenDiablo2.Common.Models; namespace OpenDiablo2.Common.Interfaces @@ -9,5 +11,6 @@ namespace OpenDiablo2.Common.Interfaces List LevelTypes { get; } List LevelDetails { get; } List Items { get; } + Dictionary ExperienceConfigs { get; } } } diff --git a/OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs b/OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs index 0779909c..4ba5ac33 100644 --- a/OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs +++ b/OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs @@ -8,7 +8,7 @@ namespace OpenDiablo2.Common.Interfaces.Mobs { public interface ILevelExperienceConfig { - int GetTotalExperienceForLevel(int level); + long GetTotalExperienceForLevel(int level); int GetMaxLevel(); } } diff --git a/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs b/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs index abfb5c12..5c0c3691 100644 --- a/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs +++ b/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs @@ -1,4 +1,5 @@ -using OpenDiablo2.Common.Interfaces.Mobs; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces.Mobs; using System; using System.Collections.Generic; using System.Linq; @@ -9,14 +10,14 @@ namespace OpenDiablo2.Common.Models.Mobs { public class LevelExperienceConfig : ILevelExperienceConfig { - private List ExperiencePerLevel = new List(); + private List ExperiencePerLevel = new List(); - public LevelExperienceConfig(List expperlevel) + public LevelExperienceConfig(List expperlevel) { ExperiencePerLevel = expperlevel; } - public int GetTotalExperienceForLevel(int level) + public long GetTotalExperienceForLevel(int level) { if(ExperiencePerLevel.Count <= level) { @@ -30,4 +31,42 @@ namespace OpenDiablo2.Common.Models.Mobs return ExperiencePerLevel.Count - 1; } } + + public static class LevelExperienceConfigHelper + { + public static Dictionary ToLevelExperienceConfigs(this string[][] data) + { + Dictionary result = new Dictionary(); + for (int i = 1; i < data[0].Length; i++) + { + // i starts at 1 because we want to skip the first column + // the first column is just the row titles + string heroname = data[i][0]; // first row is the hero name + eHero herotype = default(eHero); + if(!Enum.TryParse(heroname, out herotype)) + { + continue; // skip this hero if we can't parse the name into a valid hero type + } + int maxlevel = -1; + if(!int.TryParse(data[1][i], out maxlevel)) + { + maxlevel = -1;// we don't need to fail in this case since maxlevel + // can be inferred from the number of experience listings + } + List expperlevel = new List(); + for (int o = 2; o < data.Length && (o-2 < maxlevel || maxlevel == -1); o++) + { + long exp = 0; + if(!long.TryParse(data[o][i], out exp)) + { + throw new Exception("Could not parse experience number '" + data[o][i] + "'."); + } + expperlevel.Add(exp); + } + result.Add(herotype, new LevelExperienceConfig(expperlevel)); + } + + return result; + } + } } diff --git a/OpenDiablo2.Common/Models/Mobs/PlayerState.cs b/OpenDiablo2.Common/Models/Mobs/PlayerState.cs index 4b3954a5..09e10e20 100644 --- a/OpenDiablo2.Common/Models/Mobs/PlayerState.cs +++ b/OpenDiablo2.Common/Models/Mobs/PlayerState.cs @@ -26,12 +26,12 @@ namespace OpenDiablo2.Common.Models.Mobs protected Stat Stamina; protected Stat Mana; - public int Experience { get; protected set; } + public long Experience { get; protected set; } public PlayerState() : base() { } public PlayerState(int clientHash, string name, int id, int level, float x, float y, - int vitality, int strength, int energy, int dexterity, int experience, eHero herotype, + int vitality, int strength, int energy, int dexterity, long experience, eHero herotype, IHeroTypeConfig heroconfig, ILevelExperienceConfig expconfig) : base(name, id, level, 0, x, y) { @@ -63,11 +63,11 @@ namespace OpenDiablo2.Common.Models.Mobs } #region Level and Experience - public int GetExperienceToLevel() + public long GetExperienceToLevel() { return GetExperienceTotalToLevel() - Experience; } - public int GetExperienceTotalToLevel() + public long GetExperienceTotalToLevel() { return ExperienceConfig.GetTotalExperienceForLevel(Level + 1); } @@ -75,7 +75,7 @@ namespace OpenDiablo2.Common.Models.Mobs { return ExperienceConfig.GetMaxLevel(); } - public bool AddExperience(int amount) + public bool AddExperience(long amount) { // returns true if you level up from this Experience += amount; diff --git a/OpenDiablo2.Common/ResourcePaths.cs b/OpenDiablo2.Common/ResourcePaths.cs index 16a965ef..fcd9573d 100644 --- a/OpenDiablo2.Common/ResourcePaths.cs +++ b/OpenDiablo2.Common/ResourcePaths.cs @@ -137,6 +137,9 @@ namespace OpenDiablo2.Common public static string Armor = "data\\global\\excel\\armor.txt"; public static string Misc = "data\\global\\excel\\misc.txt"; + // --- Character Data --- + public static string Experience = "data\\global\\excel\\experience.txt"; + public static string GeneratePathForItem(string spriteName) { return $"data\\global\\items\\{spriteName}.dc6"; diff --git a/OpenDiablo2.Core/EngineDataManager.cs b/OpenDiablo2.Core/EngineDataManager.cs index 4f904534..0e0be4d9 100644 --- a/OpenDiablo2.Core/EngineDataManager.cs +++ b/OpenDiablo2.Core/EngineDataManager.cs @@ -4,8 +4,11 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using OpenDiablo2.Common; +using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Interfaces; +using OpenDiablo2.Common.Interfaces.Mobs; using OpenDiablo2.Common.Models; +using OpenDiablo2.Common.Models.Mobs; namespace OpenDiablo2.Core { @@ -17,6 +20,7 @@ namespace OpenDiablo2.Core public List LevelTypes { get; internal set; } public List LevelDetails { get; internal set; } public List Items { get; internal set; } = new List(); + public Dictionary ExperienceConfigs { get; internal set; } = new Dictionary(); public EngineDataManager(IMPQProvider mpqProvider) { @@ -27,6 +31,8 @@ namespace OpenDiablo2.Core LoadLevelDetails(); LoadItemData(); + + LoadCharacterData(); } private void LoadLevelTypes() @@ -124,5 +130,22 @@ namespace OpenDiablo2.Core return data; } + + private void LoadCharacterData() + { + LoadExperienceConfig(); + } + + private void LoadExperienceConfig() + { + var data = mpqProvider + .GetTextFile(ResourcePaths.Experience) + .Where(x => !String.IsNullOrWhiteSpace(x)) + .Select(x => x.Split('\t')) + .ToArray() + .ToLevelExperienceConfigs(); + + ExperienceConfigs = data; + } } } diff --git a/OpenDiablo2.GameServer/GameServer.cs b/OpenDiablo2.GameServer/GameServer.cs index 38336fed..133f9c12 100644 --- a/OpenDiablo2.GameServer/GameServer.cs +++ b/OpenDiablo2.GameServer/GameServer.cs @@ -12,15 +12,17 @@ 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; public int Seed { get; private set; } public IEnumerable Players => mobManager.Players; const double Deg2Rad = Math.PI / 180.0; - public GameServer(IMobManager mobManager) + public GameServer(IMobManager mobManager, IEngineDataManager engineDataManager) { this.mobManager = mobManager; + this.engineDataManager = engineDataManager; } public void InitializeNewGame() @@ -31,8 +33,20 @@ namespace OpenDiablo2.GameServer_ public int SpawnNewPlayer(int clientHash, string playerName, eHero heroType) { + ILevelExperienceConfig expConfig = null; + try + { + expConfig = engineDataManager.ExperienceConfigs[heroType]; + } + catch(Exception e) + { + 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? + } 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), new LevelExperienceConfig(new List() { 100 })); + new HeroTypeConfig(10, 10, 10, 50, 50, 50, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), expConfig); mobManager.AddPlayer(newPlayer); return newPlayer.Id;