From 55f8f3ef34bbf6026d54e70e1022f73b0fa0305f Mon Sep 17 00:00:00 2001 From: nicholasdechiara <30356480+nicholasdechiara@users.noreply.github.com> Date: Thu, 29 Nov 2018 21:20:29 -0500 Subject: [PATCH] Mob and Playerstate (#31) * Filled out eLevelId enum - Added OpenDiablo2.Core.UT unit test project - Added ELevelIdHelper class which contains code that generates the enum from the mpq data - Added a unit test that verifies EngineDataManager works - Added a unit test that runs the ELevelIdHelper generate function - Renamed some enum values for constistency (e.g. Act1_Town -> Act1_Town1, as it is in the mpq) * Retargeted OpenDiablo2.Core.UT to .net Framework 4.6.1 * Added TestConsole TestConsole currently only supports writing the elevelids enum to a file Also, removed elevelids generation unit test * PlayerState and MobState --- .../OpenDiablo2.Common.UT.csproj | 76 ++++++ .../Properties/AssemblyInfo.cs | 20 ++ OpenDiablo2.Common.UT/UT_MobState.cs | 56 ++++ OpenDiablo2.Common.UT/UT_PlayerState.cs | 72 +++++ OpenDiablo2.Common.UT/UT_Stat.cs | 80 ++++++ OpenDiablo2.Common.UT/packages.config | 5 + OpenDiablo2.Common/Enums/Mobs/eDamageTypes.cs | 19 ++ OpenDiablo2.Common/Enums/Mobs/eMobFlags.cs | 15 + .../Enums/Mobs/eStatModifierType.cs | 16 ++ OpenDiablo2.Common/Enums/eButtonType.cs | 42 +-- OpenDiablo2.Common/Enums/eDamageTypes.cs | 19 ++ OpenDiablo2.Common/Enums/eMobFlags.cs | 15 + OpenDiablo2.Common/Enums/eStatModifierType.cs | 16 ++ OpenDiablo2.Common/Interfaces/IPanelFrame.cs | 2 +- .../Interfaces/Mobs/IHeroTypeConfig.cs | 35 +++ .../Interfaces/Mobs/ILevelExperienceConfig.cs | 14 + .../Interfaces/Mobs/IMobCondition.cs | 14 + .../Interfaces/Mobs/IMobManager.cs | 13 + .../Interfaces/Mobs/IStatModifier.cs | 19 ++ OpenDiablo2.Common/Models/ButtonLayout.cs | 78 +++--- OpenDiablo2.Common/Models/Mobs/EnemyState.cs | 21 ++ .../Models/Mobs/HeroTypeConfig.cs | 62 +++++ .../Models/Mobs/LevelExperienceConfig.cs | 33 +++ .../Models/Mobs/MobCondition.cs | 97 +++++++ OpenDiablo2.Common/Models/Mobs/MobState.cs | 168 ++++++++++++ OpenDiablo2.Common/Models/Mobs/PlayerState.cs | 208 ++++++++++++++ OpenDiablo2.Common/Models/Mobs/Stat.cs | 159 +++++++++++ OpenDiablo2.Common/Models/Mobs/StatBase.cs | 77 ++++++ OpenDiablo2.Common/Models/Mobs/StatDouble.cs | 159 +++++++++++ .../Models/Mobs/StatModifier.cs | 74 +++++ OpenDiablo2.Common/OpenDiablo2.Common.csproj | 257 ++++++++++-------- .../OpenDiablo2.Core.UT.csproj | 8 +- OpenDiablo2.Core.UT/UT_MobManager.cs | 72 +++++ OpenDiablo2.Core/GameState/GameState.cs | 20 +- OpenDiablo2.Core/GameState/MobManager.cs | 141 ++++++++++ OpenDiablo2.Core/OpenDiablo2.Core.csproj | 167 ++++++------ OpenDiablo2.Core/UI/CharacterPanel.cs | 88 +++--- OpenDiablo2.Core/UI/InventoryPanel.cs | 84 +++--- OpenDiablo2.Core/UI/MiniPanel.cs | 202 +++++++------- OpenDiablo2.Core/UI/PanelFrame.cs | 134 ++++----- OpenDiablo2.sln | 12 + 41 files changed, 2338 insertions(+), 531 deletions(-) create mode 100644 OpenDiablo2.Common.UT/OpenDiablo2.Common.UT.csproj create mode 100644 OpenDiablo2.Common.UT/Properties/AssemblyInfo.cs create mode 100644 OpenDiablo2.Common.UT/UT_MobState.cs create mode 100644 OpenDiablo2.Common.UT/UT_PlayerState.cs create mode 100644 OpenDiablo2.Common.UT/UT_Stat.cs create mode 100644 OpenDiablo2.Common.UT/packages.config create mode 100644 OpenDiablo2.Common/Enums/Mobs/eDamageTypes.cs create mode 100644 OpenDiablo2.Common/Enums/Mobs/eMobFlags.cs create mode 100644 OpenDiablo2.Common/Enums/Mobs/eStatModifierType.cs create mode 100644 OpenDiablo2.Common/Enums/eDamageTypes.cs create mode 100644 OpenDiablo2.Common/Enums/eMobFlags.cs create mode 100644 OpenDiablo2.Common/Enums/eStatModifierType.cs create mode 100644 OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs create mode 100644 OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs create mode 100644 OpenDiablo2.Common/Interfaces/Mobs/IMobCondition.cs create mode 100644 OpenDiablo2.Common/Interfaces/Mobs/IMobManager.cs create mode 100644 OpenDiablo2.Common/Interfaces/Mobs/IStatModifier.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/EnemyState.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/MobCondition.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/MobState.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/PlayerState.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/Stat.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/StatBase.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/StatDouble.cs create mode 100644 OpenDiablo2.Common/Models/Mobs/StatModifier.cs create mode 100644 OpenDiablo2.Core.UT/UT_MobManager.cs create mode 100644 OpenDiablo2.Core/GameState/MobManager.cs diff --git a/OpenDiablo2.Common.UT/OpenDiablo2.Common.UT.csproj b/OpenDiablo2.Common.UT/OpenDiablo2.Common.UT.csproj new file mode 100644 index 00000000..66aecd00 --- /dev/null +++ b/OpenDiablo2.Common.UT/OpenDiablo2.Common.UT.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {77B00705-A2A5-4675-9A16-1FAB2692151B} + Library + Properties + OpenDiablo2.Common.UT + OpenDiablo2.Common.UT + v4.6.1 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + + + + + + {B743160E-A0BB-45DC-9998-967A85E50562} + OpenDiablo2.Common + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/OpenDiablo2.Common.UT/Properties/AssemblyInfo.cs b/OpenDiablo2.Common.UT/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..014947b1 --- /dev/null +++ b/OpenDiablo2.Common.UT/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("OpenDiablo2.Common.UT")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenDiablo2.Common.UT")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("77b00705-a2a5-4675-9a16-1fab2692151b")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenDiablo2.Common.UT/UT_MobState.cs b/OpenDiablo2.Common.UT/UT_MobState.cs new file mode 100644 index 00000000..7c83585d --- /dev/null +++ b/OpenDiablo2.Common.UT/UT_MobState.cs @@ -0,0 +1,56 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenDiablo2.Common.Enums.Mobs; +using OpenDiablo2.Common.Models.Mobs; + +namespace OpenDiablo2.Common.UT +{ + [TestClass] + public class UT_MobState + { + [TestMethod] + public void MobDeathTest() + { + MobState mob = new MobState("test1", 1, 1, 100, 0, 0); + mob.TakeDamage(1000, Enums.Mobs.eDamageTypes.NONE); + Assert.AreEqual(false, mob.Alive); + } + + [TestMethod] + public void MobFlagsTest() + { + MobState mob = new MobState("test1", 1, 1, 100, 0, 0); + mob.AddFlag(Enums.Mobs.eMobFlags.ENEMY); + Assert.AreEqual(true, mob.HasFlag(Enums.Mobs.eMobFlags.ENEMY)); + Assert.AreEqual(false, mob.HasFlag(Enums.Mobs.eMobFlags.INVULNERABLE)); + mob.RemoveFlag(Enums.Mobs.eMobFlags.ENEMY); + Assert.AreEqual(false, mob.HasFlag(Enums.Mobs.eMobFlags.ENEMY)); + } + + [TestMethod] + public void MobResistancesTest() + { + MobState mob = new MobState("test1", 1, 1, 100, 0, 0); + mob.SetResistance(eDamageTypes.COLD, 0.5); + int dam = mob.TakeDamage(100, eDamageTypes.COLD); // now try 100 cold damage + Assert.AreEqual(50, dam); // b/c mob has 50% resistance, should only take 50 damage + Assert.IsTrue(mob.Alive); // mob should not have taken enough damage to die + int dam2 = mob.TakeDamage(100, eDamageTypes.FIRE); // now try 100 fire damage + Assert.AreEqual(100, dam2); // b/c mob has no fire resistance, should take full damage + Assert.IsFalse(mob.Alive); // mob should be dead + } + + [TestMethod] + public void MobImmunityTest() + { + MobState mob = new MobState("test1", 1, 1, 100, 0, 0); + mob.AddImmunitiy(eDamageTypes.COLD); + int dam = mob.TakeDamage(100, eDamageTypes.COLD); // now try 100 cold damage + Assert.AreEqual(0, dam); // b/c mob has immunity, should not take damage + Assert.IsTrue(mob.Alive); // mob should not have taken enough damage to die + int dam2 = mob.TakeDamage(100, eDamageTypes.FIRE); // now try 100 fire damage + Assert.AreEqual(100, dam2); // b/c mob has no fire immunity, should take full damage + Assert.IsFalse(mob.Alive); // mob should be dead + } + } +} diff --git a/OpenDiablo2.Common.UT/UT_PlayerState.cs b/OpenDiablo2.Common.UT/UT_PlayerState.cs new file mode 100644 index 00000000..02c6ba5a --- /dev/null +++ b/OpenDiablo2.Common.UT/UT_PlayerState.cs @@ -0,0 +1,72 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenDiablo2.Common.Models.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.UT +{ + [TestClass] + public class UT_PlayerState + { + private PlayerState MakePlayer() + { + HeroTypeConfig herotypeconfig = new HeroTypeConfig(vitality: 20, strength: 20, dexterity: 20, + energy: 20, health: 50, mana: 40, stamina: 30, + 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() + { + 0, // level 0 + 0, // level 1 + 500, // level 2 + 1500, // level 3 + 2250, + 4125, + }); + PlayerState ps = new PlayerState("player1", id: 1, level: 1, x: 0, y: 0, + vitality: herotypeconfig.StartingVitality, + strength: herotypeconfig.StartingStrength, + energy: herotypeconfig.StartingEnergy, + dexterity: herotypeconfig.StartingDexterity, + experience: 0, + herotype: Enums.eHero.Amazon, + heroconfig: herotypeconfig, + expconfig: expconfig); + return ps; + } + + [TestMethod] + public void PlayerLevelUpTest() + { + PlayerState ps = MakePlayer(); + + int level1hp = ps.GetHealthMax(); + int level1mana = ps.GetManaMax(); + int level1stamina = ps.GetStaminaMax(); + Assert.IsFalse(ps.AddExperience(499)); // not quite enough + Assert.IsTrue(ps.AddExperience(1)); // there we go + int level2hp = ps.GetHealthMax(); + int level2mana = ps.GetManaMax(); + int level2stamina = ps.GetStaminaMax(); + Assert.IsFalse(ps.AddExperience(999)); // not quite enough + Assert.IsTrue(ps.AddExperience(1)); // there we go + int level3hp = ps.GetHealthMax(); + int level3mana = ps.GetManaMax(); + int level3stamina = ps.GetStaminaMax(); + + // this should update the player's health, mana, and stamina + Assert.AreEqual(level1hp + 3, level2hp); + Assert.AreEqual(level1mana + 1, level2mana); + Assert.AreEqual(level1stamina + 1, level2stamina); + + Assert.AreEqual(level2hp + 3, level3hp); + Assert.AreEqual(level2mana + 2, level3mana); // because 2->3 is an odd levelup, should get 2 + // more mana on this level (1.5 = +1 on even levels, +2 on odd) + Assert.AreEqual(level2stamina + 1, level3stamina); + } + } +} diff --git a/OpenDiablo2.Common.UT/UT_Stat.cs b/OpenDiablo2.Common.UT/UT_Stat.cs new file mode 100644 index 00000000..2d343e63 --- /dev/null +++ b/OpenDiablo2.Common.UT/UT_Stat.cs @@ -0,0 +1,80 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenDiablo2.Common.Models.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.UT +{ + [TestClass] + public class UT_Stat + { + [TestMethod] + public void BasicStatTest() + { + Stat hp = new Stat(0, 100, 100, false); + Assert.AreEqual(100, hp.GetCurrent()); + Assert.AreEqual(100, hp.GetMax()); + Assert.AreEqual(0, hp.GetMin()); + + hp.AddCurrent(10); + Assert.AreEqual(100, hp.GetCurrent()); // not allowed to go over max + + hp.AddCurrent(-30); + Assert.AreEqual(70, hp.GetCurrent()); + + hp.SetCurrent(10); + Assert.AreEqual(10, hp.GetCurrent()); + + hp.AddCurrent(-30); + Assert.AreEqual(0, hp.GetCurrent()); // not allowed to go under min + } + + [TestMethod] + public void ModifierStatTest() + { + Stat hp = new Stat(0, 100, 100, false); + + StatModifierAddition mod1 = new StatModifierAddition("mod1", 10); + hp.AddModifier(mod1); + + Assert.AreEqual(100, hp.GetCurrent()); // not allowed to overflow from mods + hp.AllowedToOverflowFromModifiers = true; + Assert.AreEqual(110, hp.GetCurrent()); // if we flip overflow to true, should overflow from mods + + StatModifierAddition mod2 = new StatModifierAddition("mod2", -20); + hp.AddModifier(mod2); + Assert.AreEqual(90, hp.GetCurrent()); + + hp.RemoveModifier("mod1"); + Assert.AreEqual(80, hp.GetCurrent()); // if we remove mod1, we should see its effect go away + + hp.RemoveModifier("mod2"); + Assert.AreEqual(100, hp.GetCurrent()); // similarly with mod2 + } + + [TestMethod] + public void ModifierPriorityTest() + { + Stat hp = new Stat(0, 100, 50, true); + + StatModifierMultiplication mod1 = new StatModifierMultiplication("mod1", 0.5, priority: 1); + hp.AddModifier(mod1); + Assert.AreEqual(75, hp.GetCurrent()); + + StatModifierMultiplication mod2 = new StatModifierMultiplication("mod2", 0.5, priority: 1); + hp.AddModifier(mod2); + Assert.AreEqual(100, hp.GetCurrent()); // because these have the same priority, + // they don't see eachother's effects (e.g. we get 50 + 50*0.5 + 50*0.5, NOT 50 + 50*0.5 + 75*0.5 + + hp.RemoveModifier("mod2"); + Assert.AreEqual(75, hp.GetCurrent()); + StatModifierMultiplication mod3 = new StatModifierMultiplication("mod3", -0.2, priority: 0); + hp.AddModifier(mod3); + Assert.AreEqual(60, hp.GetCurrent()); // because this one has a lower priority, it happens AFTER mod1 + // so we get 50 + 50*0.5 = 75, then 75 + 75*-0.2 = 60 + } + } +} diff --git a/OpenDiablo2.Common.UT/packages.config b/OpenDiablo2.Common.UT/packages.config new file mode 100644 index 00000000..cf2d094f --- /dev/null +++ b/OpenDiablo2.Common.UT/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/OpenDiablo2.Common/Enums/Mobs/eDamageTypes.cs b/OpenDiablo2.Common/Enums/Mobs/eDamageTypes.cs new file mode 100644 index 00000000..e59e35c8 --- /dev/null +++ b/OpenDiablo2.Common/Enums/Mobs/eDamageTypes.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Enums.Mobs +{ + public enum eDamageTypes + { + NONE, // no resistances apply + PHYSICAL, + MAGIC, + FIRE, + COLD, + LIGHTNING, + POISON, + } +} diff --git a/OpenDiablo2.Common/Enums/Mobs/eMobFlags.cs b/OpenDiablo2.Common/Enums/Mobs/eMobFlags.cs new file mode 100644 index 00000000..69af6816 --- /dev/null +++ b/OpenDiablo2.Common/Enums/Mobs/eMobFlags.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Enums.Mobs +{ + public enum eMobFlags + { + PLAYER, + ENEMY, + INVULNERABLE + } +} diff --git a/OpenDiablo2.Common/Enums/Mobs/eStatModifierType.cs b/OpenDiablo2.Common/Enums/Mobs/eStatModifierType.cs new file mode 100644 index 00000000..3c2dc3fe --- /dev/null +++ b/OpenDiablo2.Common/Enums/Mobs/eStatModifierType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Enums.Mobs +{ + public enum eStatModifierType + { + // does it affect the current value, min value, or max value? + MIN, + MAX, + CURRENT + } +} diff --git a/OpenDiablo2.Common/Enums/eButtonType.cs b/OpenDiablo2.Common/Enums/eButtonType.cs index bff008cb..089467be 100644 --- a/OpenDiablo2.Common/Enums/eButtonType.cs +++ b/OpenDiablo2.Common/Enums/eButtonType.cs @@ -1,21 +1,21 @@ -namespace OpenDiablo2.Common.Enums -{ - public enum eButtonType - { - Wide, - Medium, - Narrow, - Cancel, - Tall, - // Game UI - Run, - Menu, - MinipanelCharacter, - MinipanelInventory, - MinipanelSkill, - MinipanelAutomap, - MinipanelMessage, - MinipanelQuest, - MinipanelMenu - } -} +namespace OpenDiablo2.Common.Enums +{ + public enum eButtonType + { + Wide, + Medium, + Narrow, + Cancel, + Tall, + // Game UI + Run, + Menu, + MinipanelCharacter, + MinipanelInventory, + MinipanelSkill, + MinipanelAutomap, + MinipanelMessage, + MinipanelQuest, + MinipanelMenu + } +} diff --git a/OpenDiablo2.Common/Enums/eDamageTypes.cs b/OpenDiablo2.Common/Enums/eDamageTypes.cs new file mode 100644 index 00000000..e59e35c8 --- /dev/null +++ b/OpenDiablo2.Common/Enums/eDamageTypes.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Enums.Mobs +{ + public enum eDamageTypes + { + NONE, // no resistances apply + PHYSICAL, + MAGIC, + FIRE, + COLD, + LIGHTNING, + POISON, + } +} diff --git a/OpenDiablo2.Common/Enums/eMobFlags.cs b/OpenDiablo2.Common/Enums/eMobFlags.cs new file mode 100644 index 00000000..69af6816 --- /dev/null +++ b/OpenDiablo2.Common/Enums/eMobFlags.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Enums.Mobs +{ + public enum eMobFlags + { + PLAYER, + ENEMY, + INVULNERABLE + } +} diff --git a/OpenDiablo2.Common/Enums/eStatModifierType.cs b/OpenDiablo2.Common/Enums/eStatModifierType.cs new file mode 100644 index 00000000..3c2dc3fe --- /dev/null +++ b/OpenDiablo2.Common/Enums/eStatModifierType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Enums.Mobs +{ + public enum eStatModifierType + { + // does it affect the current value, min value, or max value? + MIN, + MAX, + CURRENT + } +} diff --git a/OpenDiablo2.Common/Interfaces/IPanelFrame.cs b/OpenDiablo2.Common/Interfaces/IPanelFrame.cs index 799eaede..f3c284c5 100644 --- a/OpenDiablo2.Common/Interfaces/IPanelFrame.cs +++ b/OpenDiablo2.Common/Interfaces/IPanelFrame.cs @@ -1,4 +1,4 @@ -using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Enums; using System; namespace OpenDiablo2.Common.Interfaces diff --git a/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs b/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs new file mode 100644 index 00000000..cbab772e --- /dev/null +++ b/OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Interfaces.Mobs +{ + public interface IHeroTypeConfig + { + int StartingVitality { get; } + int StartingStrength { get; } + int StartingDexterity { get; } + int StartingEnergy { get; } + int StartingHealth { get; } + int StartingMana { get; } + int StartingStamina { 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) + // and 2 mana on odd levels (e.g. -> 3) + double PerLevelMana { get; } + double PerLevelStamina { get; } + + double PerVitalityHealth { get; } + double PerVitalityStamina { get; } + double PerEnergyMana { get; } + + int BaseAttackRating { get; } + int PerDexterityAttackRating { get; } + + int BaseDefenseRating { get; } + int PerDexterityDefenseRating { get; } + } +} diff --git a/OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs b/OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs new file mode 100644 index 00000000..0779909c --- /dev/null +++ b/OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Interfaces.Mobs +{ + public interface ILevelExperienceConfig + { + int GetTotalExperienceForLevel(int level); + int GetMaxLevel(); + } +} diff --git a/OpenDiablo2.Common/Interfaces/Mobs/IMobCondition.cs b/OpenDiablo2.Common/Interfaces/Mobs/IMobCondition.cs new file mode 100644 index 00000000..5266ef48 --- /dev/null +++ b/OpenDiablo2.Common/Interfaces/Mobs/IMobCondition.cs @@ -0,0 +1,14 @@ +using OpenDiablo2.Common.Models.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Interfaces.Mobs +{ + public interface IMobCondition + { + bool Evaluate(MobState mob); + } +} diff --git a/OpenDiablo2.Common/Interfaces/Mobs/IMobManager.cs b/OpenDiablo2.Common/Interfaces/Mobs/IMobManager.cs new file mode 100644 index 00000000..21122bbd --- /dev/null +++ b/OpenDiablo2.Common/Interfaces/Mobs/IMobManager.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Interfaces.Mobs +{ + public interface IMobManager + { + // TODO: + } +} diff --git a/OpenDiablo2.Common/Interfaces/Mobs/IStatModifier.cs b/OpenDiablo2.Common/Interfaces/Mobs/IStatModifier.cs new file mode 100644 index 00000000..970153b9 --- /dev/null +++ b/OpenDiablo2.Common/Interfaces/Mobs/IStatModifier.cs @@ -0,0 +1,19 @@ +using OpenDiablo2.Common.Enums.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Interfaces.Mobs +{ + public interface IStatModifier + { + string Name { get; } + int Priority { get; } // what priority does this modifier happen in? higher = occurs before other modifiers + // modifiers at the same priority level occur simultaneously + eStatModifierType ModifierType { get; } // does it affect current, min or max? + int GetValue(int min, int max, int current); // what does this modifier add to the stat's current value? + double GetValue(double min, double max, double current); + } +} diff --git a/OpenDiablo2.Common/Models/ButtonLayout.cs b/OpenDiablo2.Common/Models/ButtonLayout.cs index 1863cb80..9c200391 100644 --- a/OpenDiablo2.Common/Models/ButtonLayout.cs +++ b/OpenDiablo2.Common/Models/ButtonLayout.cs @@ -1,39 +1,39 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using OpenDiablo2.Common.Enums; - -namespace OpenDiablo2.Common.Models -{ - public class ButtonLayout - { - public int XSegments { get; internal set; } - public string ResourceName { get; internal set; } - public string PaletteName { get; internal set; } - public bool Toggleable { get; internal set; } = false; - public int BaseFrame { get; internal set; } = 0; - - public static Dictionary Values = new Dictionary - { - {eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = Palettes.Units } }, - {eButtonType.Medium, new ButtonLayout{ XSegments = 1, ResourceName=ResourcePaths.MediumButtonBlank, PaletteName = Palettes.Units } }, - {eButtonType.Narrow, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.NarrowButtonBlank,PaletteName = Palettes.Units } }, - {eButtonType.Tall, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.TallButtonBlank, PaletteName = Palettes.Units } }, - {eButtonType.Cancel, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.CancelButton,PaletteName = Palettes.Units } }, - // Minipanel - {eButtonType.MinipanelCharacter, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 0 } }, - {eButtonType.MinipanelInventory, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 2 } }, - {eButtonType.MinipanelSkill, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 4 } }, - {eButtonType.MinipanelAutomap, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 8 } }, - {eButtonType.MinipanelMessage, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 10 } }, - {eButtonType.MinipanelQuest, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 12 } }, - {eButtonType.MinipanelMenu, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 14 } }, - - {eButtonType.Run, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.RunButton,PaletteName = Palettes.Units, Toggleable = true } }, - {eButtonType.Menu, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MenuButton,PaletteName = Palettes.Units, Toggleable = true } }, - }; - } - -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenDiablo2.Common.Enums; + +namespace OpenDiablo2.Common.Models +{ + public class ButtonLayout + { + public int XSegments { get; internal set; } + public string ResourceName { get; internal set; } + public string PaletteName { get; internal set; } + public bool Toggleable { get; internal set; } = false; + public int BaseFrame { get; internal set; } = 0; + + public static Dictionary Values = new Dictionary + { + {eButtonType.Wide, new ButtonLayout { XSegments = 2, ResourceName = ResourcePaths.WideButtonBlank, PaletteName = Palettes.Units } }, + {eButtonType.Medium, new ButtonLayout{ XSegments = 1, ResourceName=ResourcePaths.MediumButtonBlank, PaletteName = Palettes.Units } }, + {eButtonType.Narrow, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.NarrowButtonBlank,PaletteName = Palettes.Units } }, + {eButtonType.Tall, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.TallButtonBlank, PaletteName = Palettes.Units } }, + {eButtonType.Cancel, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.CancelButton,PaletteName = Palettes.Units } }, + // Minipanel + {eButtonType.MinipanelCharacter, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 0 } }, + {eButtonType.MinipanelInventory, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 2 } }, + {eButtonType.MinipanelSkill, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 4 } }, + {eButtonType.MinipanelAutomap, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 8 } }, + {eButtonType.MinipanelMessage, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 10 } }, + {eButtonType.MinipanelQuest, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 12 } }, + {eButtonType.MinipanelMenu, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MinipanelButton,PaletteName = Palettes.Units, BaseFrame = 14 } }, + + {eButtonType.Run, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.RunButton,PaletteName = Palettes.Units, Toggleable = true } }, + {eButtonType.Menu, new ButtonLayout {XSegments = 1, ResourceName = ResourcePaths.MenuButton,PaletteName = Palettes.Units, Toggleable = true } }, + }; + } + +} diff --git a/OpenDiablo2.Common/Models/Mobs/EnemyState.cs b/OpenDiablo2.Common/Models/Mobs/EnemyState.cs new file mode 100644 index 00000000..96210943 --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/EnemyState.cs @@ -0,0 +1,21 @@ +using OpenDiablo2.Common.Enums.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class EnemyState : MobState + { + public int ExperienceGiven { get; protected set; } + + public EnemyState(string name, int id, int level, int maxhealth, float x, float y, int experiencegiven) + : base(name, id, level, maxhealth, x, y) + { + ExperienceGiven = experiencegiven; + AddFlag(eMobFlags.ENEMY); + } + } +} diff --git a/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs b/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs new file mode 100644 index 00000000..e6a9c711 --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs @@ -0,0 +1,62 @@ +using OpenDiablo2.Common.Interfaces.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class HeroTypeConfig : IHeroTypeConfig + { + public int StartingVitality { get; protected set; } + public int StartingStrength { get; protected set; } + public int StartingDexterity { get; protected set; } + public int StartingEnergy { get; protected set; } + public int StartingHealth { get; protected set; } + public int StartingMana { get; protected set; } + public int StartingStamina { get; protected set; } + + public double PerLevelHealth { get; protected set; } + public double PerLevelMana { get; protected set; } + public double PerLevelStamina { get; protected set; } + + public double PerVitalityHealth { get; protected set; } + public double PerVitalityStamina { get; protected set; } + public double PerEnergyMana { get; protected set; } + + public int BaseAttackRating { get; protected set; } + public int PerDexterityAttackRating { get; protected set; } + + public int BaseDefenseRating { get; protected set; } + public int PerDexterityDefenseRating { get; protected set; } + + public HeroTypeConfig(int vitality, int strength, int dexterity, int energy, + int health, int mana, int stamina, + double perlevelhealth, double perlevelmana, double perlevelstamina, + double pervitalityhealth, double pervitalitystamina, double perenergymana, + int baseatkrating, int basedefrating, int perdexterityatkrating, int perdexteritydefrating) + { + StartingDexterity = dexterity; + StartingVitality = vitality; + StartingStrength = strength; + StartingEnergy = energy; + StartingMana = mana; + StartingHealth = health; + StartingStamina = stamina; + + PerLevelHealth = perlevelhealth; + PerLevelMana = perlevelmana; + PerLevelStamina = perlevelstamina; + + PerVitalityHealth = pervitalityhealth; + PerVitalityStamina = pervitalitystamina; + PerEnergyMana = perenergymana; + + BaseAttackRating = baseatkrating; + BaseDefenseRating = basedefrating; + PerDexterityAttackRating = perdexterityatkrating; + PerDexterityDefenseRating = perdexteritydefrating; + } + } +} diff --git a/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs b/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs new file mode 100644 index 00000000..abfb5c12 --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs @@ -0,0 +1,33 @@ +using OpenDiablo2.Common.Interfaces.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class LevelExperienceConfig : ILevelExperienceConfig + { + private List ExperiencePerLevel = new List(); + + public LevelExperienceConfig(List expperlevel) + { + ExperiencePerLevel = expperlevel; + } + + public int GetTotalExperienceForLevel(int level) + { + if(ExperiencePerLevel.Count <= level) + { + return -1; // note: a value of -1 means this level is unattainable! + } + return ExperiencePerLevel[level]; + } + + public int GetMaxLevel() + { + return ExperiencePerLevel.Count - 1; + } + } +} diff --git a/OpenDiablo2.Common/Models/Mobs/MobCondition.cs b/OpenDiablo2.Common/Models/Mobs/MobCondition.cs new file mode 100644 index 00000000..aa4437d1 --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/MobCondition.cs @@ -0,0 +1,97 @@ +using OpenDiablo2.Common.Enums.Mobs; +using OpenDiablo2.Common.Interfaces.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class MobConditionAnd : IMobCondition + { + public List Conditions = new List(); + + public MobConditionAnd(List conditions) + { + Conditions = conditions; + } + + public bool Evaluate(MobState mob) + { + foreach(IMobCondition condition in Conditions) + { + if (!condition.Evaluate(mob)) + { + return false; + } + } + return true; + } + } + + public class MobConditionOr : IMobCondition + { + public List Conditions = new List(); + + public MobConditionOr(List conditions) + { + Conditions = conditions; + } + + public bool Evaluate(MobState mob) + { + foreach (IMobCondition condition in Conditions) + { + if (condition.Evaluate(mob)) + { + return true; + } + } + return false; + } + } + + public class MobConditionNot : IMobCondition + { + public IMobCondition Condition = null; + + public MobConditionNot(IMobCondition condition) + { + Condition = condition; + } + + public bool Evaluate(MobState mob) + { + if(Condition == null) + { + return false; + } + return !Condition.Evaluate(mob); + } + } + + public class MobConditionFlags : IMobCondition + { + public Dictionary Flags = new Dictionary(); + + public MobConditionFlags(Dictionary flags) + { + Flags = flags; + } + + public bool Evaluate(MobState mob) + { + foreach(eMobFlags flag in Flags.Keys) + { + if(Flags[flag] != mob.HasFlag(flag)) + { + return false; + } + } + return true; + } + } + + // TODO: implement more of these +} diff --git a/OpenDiablo2.Common/Models/Mobs/MobState.cs b/OpenDiablo2.Common/Models/Mobs/MobState.cs new file mode 100644 index 00000000..f4cd3ce7 --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/MobState.cs @@ -0,0 +1,168 @@ +using OpenDiablo2.Common.Enums.Mobs; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class MobState + { + public readonly string Name; + public readonly int Id; + public bool Alive { get; protected set; } = true; + + protected float X = 0; + protected float Y = 0; + + protected Stat Health; + + protected Dictionary Resistances = new Dictionary(); + protected List Immunities = new List(); + + public int Level { get; protected set; } + + protected Dictionary Flags = new Dictionary(); + + public MobState(string name, int id, int level, int maxhealth, float x, float y) + { + Name = name; + Id = id; + Level = level; + Health = new Stat(0, maxhealth, maxhealth, true); + X = x; + Y = y; + } + + #region Position and Movement + public void Move(PointF pos) + { + Move(pos.X, pos.Y); + } + public void Move(float x, float y) + { + X = x; + Y = y; + } + public PointF GetPosition() + { + return new PointF(X, Y); + } + public float GetDistance(float x, float y) + { + // note: does not consider pathfinding! + return (float)Math.Sqrt(((X - x) * (X - x)) + ((Y - y) * (Y - y))); + } + #endregion Position and Movement + + #region Combat and Damage + public void SetResistance(eDamageTypes damagetype, double val) + { + if (!Resistances.ContainsKey(damagetype)) + { + Resistances.Add(damagetype, new StatDouble(0, 100.0, val, false)); + } + else + { + Resistances[damagetype].SetCurrent(val); + } + } + public void AddImmunitiy(eDamageTypes damagetype) + { + if (!Immunities.Contains(damagetype)) + { + Immunities.Add(damagetype); + } + } + public void RemoveImmunity(eDamageTypes damagetype) + { + if (Immunities.Contains(damagetype)) + { + Immunities.Remove(damagetype); + } + } + + public int GetHealth() + { + return Health.GetCurrent(); + } + public int GetHealthMax() + { + return Health.GetMax(); + } + public void RecoverHealth(int health) + { + Health.AddCurrent(health); + } + public int TakeDamage(int damage, eDamageTypes damagetype, MobState source = null) + { + // returns the actual amount of damage taken + damage = HandleResistances(damage, damagetype, source); + Health.AddCurrent(-1 * damage); + int newhp = Health.GetCurrent(); + if(newhp <= 0) + { + Die(source); + } + return damage; + } + protected int HandleResistances(int damage, eDamageTypes damagetype, MobState source = null) + { + if(damagetype == eDamageTypes.NONE) + { + return damage; + } + if (Immunities.Contains(damagetype)) + { + return 0; + } + if (!Resistances.ContainsKey(damagetype)) + { + return damage; + } + + // TODO: need to verify 1) is damage integer? and 2) if so, how is this rounding down? + // e.g. is it always 'round down' / 'round up' or does it use 'math.round' + damage = (int)(damage * Resistances[damagetype].GetCurrent()); + return damage; + } + + public void Die(MobState source = null) + { + // TODO: how do we want to tackle this? + Alive = false; + } + #endregion Combat and Damage + + #region Flags + public void AddFlag(eMobFlags flag, bool on = true) + { + if (Flags.ContainsKey(flag)) + { + Flags[flag] = on; + } + else + { + Flags.Add(flag, on); + } + } + public void RemoveFlag(eMobFlags flag) + { + if (Flags.ContainsKey(flag)) + { + Flags.Remove(flag); + } + } + public bool HasFlag(eMobFlags flag) + { + if (Flags.ContainsKey(flag)) + { + return Flags[flag]; + } + return false; + } + #endregion Flags + } +} diff --git a/OpenDiablo2.Common/Models/Mobs/PlayerState.cs b/OpenDiablo2.Common/Models/Mobs/PlayerState.cs new file mode 100644 index 00000000..1c6e07e7 --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/PlayerState.cs @@ -0,0 +1,208 @@ +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Enums.Mobs; +using OpenDiablo2.Common.Interfaces.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class PlayerState : MobState + { + public eHero HeroType { get; protected set; } + private IHeroTypeConfig HeroTypeConfig; + private ILevelExperienceConfig ExperienceConfig; + + // Player character stats + protected Stat Vitality; + protected Stat Strength; + protected Stat Energy; + protected Stat Dexterity; + + protected Stat DefenseRating; + protected Stat AttackRating; + + protected Stat Stamina; + protected Stat Mana; + + public int Experience { get; protected set; } + + public PlayerState(string name, int id, int level, float x, float y, + int vitality, int strength, int energy, int dexterity, int experience, eHero herotype, + IHeroTypeConfig heroconfig, ILevelExperienceConfig expconfig) + : base(name, id, level, 0, x, y) + { + Stamina = new Stat(0, 0, 0, true); + Mana = new Stat(0, 0, 0, true); + + Vitality = new Stat(0, vitality, vitality, true); + Strength = new Stat(0, strength, strength, true); + Energy = new Stat(0, energy, energy, true); + Dexterity = new Stat(0, dexterity, dexterity, true); + + AttackRating = new Stat(0, 0, 0, false); + DefenseRating = new Stat(0, 0, 0, false); + + Experience = experience; // how much total exp do they have + + HeroType = herotype; + HeroTypeConfig = heroconfig; + ExperienceConfig = expconfig; + + AddFlag(eMobFlags.PLAYER); + + RefreshMaxes(); // initialize the max health / mana / energy + Health.SetCurrent(Health.GetMax()); + Mana.SetCurrent(Mana.GetMax()); + Energy.SetCurrent(Energy.GetMax()); + RefreshDerived(); + } + + #region Level and Experience + public int GetExperienceToLevel() + { + return GetExperienceTotalToLevel() - Experience; + } + public int GetExperienceTotalToLevel() + { + return ExperienceConfig.GetTotalExperienceForLevel(Level + 1); + } + public int GetMaxLevel() + { + return ExperienceConfig.GetMaxLevel(); + } + public bool AddExperience(int amount) + { + // returns true if you level up from this + Experience += amount; + return TryLevelUp(); + } + public bool TryLevelUp() + { + if(Level >= GetMaxLevel()) + { + return false; // can't level up anymore + } + if(GetExperienceToLevel() > 0) + { + return false; // not enough exp + } + LevelUp(); + return true; + } + public void LevelUp() + { + Level += 1; + + // instead of adding to max here, let's just refresh all the maxes upon level up + //Health.AddMax(CalculatePerLevelStatIncrease(HeroTypeConfig.PerLevelHealth)); + //Mana.AddMax(CalculatePerLevelStatIncrease(HeroTypeConfig.PerLevelMana)); + //Stamina.AddMax(CalculatePerLevelStatIncrease(HeroTypeConfig.PerLevelStamina)); + RefreshMaxes(); + } + private int CalculatePerLevelStatIncrease(double increase) + { + // on even levels, e.g. 2, 4, 6, you gain 1 from an increase of 1.5 + // on odd levels, you gain 2 from an increase of 1.5 + return (int)(((Level % 2) * Math.Ceiling(increase)) + ((1 - (Level % 2)) * Math.Floor(increase))); + } + #endregion Level and Experience + + #region Stat Calculation + public void RefreshDerived() + { + RefreshDexterityDerived(); + } + // determine attack rating / defense rating + public void RefreshDexterityDerived() + { + // this needs to be called whenever dexterity changes + RefreshAttackRating(); + RefreshDefenseRating(); + } + public void RefreshAttackRating() + { + int atk = HeroTypeConfig.BaseAttackRating + Dexterity.GetCurrent() * HeroTypeConfig.PerDexterityAttackRating; + AttackRating.SetMax(atk); + AttackRating.SetCurrent(atk); + } + public void RefreshDefenseRating() + { + int def = HeroTypeConfig.BaseDefenseRating + Dexterity.GetCurrent() * HeroTypeConfig.PerDexterityDefenseRating; + DefenseRating.SetMax(def); + DefenseRating.SetCurrent(def); + } + // determine the max health/ mana/ stamina + public void RefreshMaxes() + { + RefreshMaxHealth(); + RefreshMaxStamina(); + RefreshMaxMana(); + } + public void RefreshMaxHealth() + { + int health = HeroTypeConfig.StartingHealth; + health += (int)Math.Floor((Level - 1) * HeroTypeConfig.PerLevelHealth); + health += (int)Math.Floor((Vitality.GetCurrent() - HeroTypeConfig.StartingVitality) * HeroTypeConfig.PerVitalityHealth); + Health.SetMax(health); + } + public void RefreshMaxStamina() + { + int stamina = HeroTypeConfig.StartingStamina; + stamina += (int)Math.Floor((Level - 1) * HeroTypeConfig.PerLevelStamina); + stamina += (int)Math.Floor((Vitality.GetCurrent() - HeroTypeConfig.StartingVitality) * HeroTypeConfig.PerVitalityStamina); + Stamina.SetMax(stamina); + } + public void RefreshMaxMana() + { + int mana = HeroTypeConfig.StartingMana; + mana += (int)Math.Floor((Level - 1) * HeroTypeConfig.PerLevelMana); + mana += (int)Math.Floor((Energy.GetCurrent() - HeroTypeConfig.StartingEnergy) * HeroTypeConfig.PerEnergyMana); + Mana.SetMax(mana); + } + #endregion Stat Calculation + + #region Mana + public int GetMana() + { + return Mana.GetCurrent(); + } + public int GetManaMax() + { + return Mana.GetMax(); + } + public void RecoverMana(int mana) + { + Mana.AddCurrent(mana); + } + public void UseMana(int mana) + { + Mana.AddCurrent(-mana); + } + #endregion Mana + + #region Stamina + public int GetStamina() + { + return Stamina.GetCurrent(); + } + public int GetStaminaMax() + { + return Stamina.GetMax(); + } + public void RecoverStamina(int stamina) + { + Stamina.AddCurrent(stamina); + } + public void UseStamina(int stamina) + { + Stamina.AddCurrent(-stamina); + } + #endregion Stamina + + // 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/Models/Mobs/Stat.cs b/OpenDiablo2.Common/Models/Mobs/Stat.cs new file mode 100644 index 00000000..6e2fdf56 --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/Stat.cs @@ -0,0 +1,159 @@ +using OpenDiablo2.Common.Enums.Mobs; +using OpenDiablo2.Common.Interfaces.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class Stat : StatBase + { + protected int Min = 0; + protected int Max = 0; + protected int Current = 0; // the current value BEFORE modifiers + + public bool AllowedToOverflowFromModifiers = false; // if true, can return a value greater than Max + // if a modifier is increasing the current value + + public Stat(int min, int max, int current, bool allowedToOverflowFromModifiers) + { + Min = min; + Max = max; + Current = current; + AllowedToOverflowFromModifiers = allowedToOverflowFromModifiers; + } + + public void AddCurrent(int value) + { + Current += value; + ClampCurrent(); + } + + public void SetCurrent(int value) + { + Current = value; + ClampCurrent(); + } + + private void ClampCurrent() + { + int currentmax = GetMax(); + int currentmin = GetMin(); + if (Current > currentmax) + { + Current = currentmax; + } + if (Current < currentmin) + { + Current = currentmin; + } + } + + public void AddMax(int value) + { + Max += value; + ClampCurrent(); + } + + public void SetMax(int value) + { + Max = value; + ClampCurrent(); + } + + public void AddMin(int value) + { + Min += value; + ClampCurrent(); + } + + public void SetMin(int value) + { + Min = value; + ClampCurrent(); + } + + public int GetCurrent() + { + int val = Current; + // take the current value and apply each modifier to it + // the modifiers are done in priority order + // OrderedModifierKeys holds the list of priorities in order from highest to lowest + // highest is operated first + // Items on the same priority level don't see eachother + // e.g. if Health is 20 and we have two modifiers with priority 5 that add 50% health, + // then we would get 20 + 10 + 10, NOT 20 + 10 + 15 + // However, if one of the modifiers now has a different priority, say 4, then we would get + // 20 + 10 + 15 + foreach(int k in OrderedModifierKeys) + { + int newval = val; + foreach(IStatModifier mod in Modifiers[k]) + { + if(mod.ModifierType == eStatModifierType.CURRENT) + { + newval += mod.GetValue(Min, Max, val); + } + } + val = newval; + } + + if (!AllowedToOverflowFromModifiers) + { + // if we aren't allowed to go over max, even with modifiers, we must double check + // that we're within our bounds + int currentmax = GetMax(); + int currentmin = GetMin(); + if (val > currentmax) + { + val = currentmax; + } + if (val < currentmin) + { + val = currentmin; + } + } + + return val; + } + + public int GetMin() + { + int val = Min; + foreach (int k in OrderedModifierKeys) + { + int newval = val; + foreach (IStatModifier mod in Modifiers[k]) + { + if (mod.ModifierType == eStatModifierType.MIN) + { + newval += mod.GetValue(val, Max, Current); + } + } + val = newval; + } + return val; + } + + public int GetMax() + { + int val = Max; + foreach (int k in OrderedModifierKeys) + { + int newval = val; + foreach (IStatModifier mod in Modifiers[k]) + { + if (mod.ModifierType == eStatModifierType.MAX) + { + newval += mod.GetValue(Min, val, Current); + } + } + val = newval; + } + + return val; + } + } +} diff --git a/OpenDiablo2.Common/Models/Mobs/StatBase.cs b/OpenDiablo2.Common/Models/Mobs/StatBase.cs new file mode 100644 index 00000000..863257be --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/StatBase.cs @@ -0,0 +1,77 @@ +using OpenDiablo2.Common.Interfaces.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public abstract class StatBase + { + protected List OrderedModifierKeys = new List(); + protected Dictionary> Modifiers = new Dictionary>(); + + public void AddModifier(IStatModifier mod) + { + int priority = mod.Priority; + if (!OrderedModifierKeys.Contains(priority)) + { + // if this priority doesn't exist yet, create it + // we must add the new key in order of highest to lowest + int i = 0; + while (i < OrderedModifierKeys.Count) + { + if (OrderedModifierKeys[i] < priority) + { + OrderedModifierKeys.Insert(i, priority); + break; + } + i++; + } + if (i >= OrderedModifierKeys.Count) + { + OrderedModifierKeys.Add(priority); + } + Modifiers.Add(priority, new List() { mod }); + } + else + { + // if priority already exists, just add it to the existing list + Modifiers[priority].Add(mod); + } + } + + public void RemoveModifier(string name) + { + foreach (int k in Modifiers.Keys) + { + int i = 0; + while (i < Modifiers[k].Count) + { + IStatModifier mod = Modifiers[k][i]; + if (mod.Name == name) + { + Modifiers[k].RemoveAt(i); + continue; // don't increment i + } + i++; + } + } + + // now do cleanup: if any modifier list is empty, remove it and its priority + int o = 0; + while (o < Modifiers.Keys.Count) + { + int k = Modifiers.Keys.ElementAt(o); + if (Modifiers[k].Count <= 0) + { + Modifiers.Remove(k); + OrderedModifierKeys.Remove(k); + continue; // don't increment o + } + o++; + } + } + } +} diff --git a/OpenDiablo2.Common/Models/Mobs/StatDouble.cs b/OpenDiablo2.Common/Models/Mobs/StatDouble.cs new file mode 100644 index 00000000..bc5eedaa --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/StatDouble.cs @@ -0,0 +1,159 @@ +using OpenDiablo2.Common.Enums.Mobs; +using OpenDiablo2.Common.Interfaces.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class StatDouble : StatBase + { + protected double Min = 0; + protected double Max = 0; + protected double Current = 0; // the current value BEFORE modifiers + + public bool AllowedToOverflowFromModifiers = false; // if true, can return a value greater than Max + // if a modifier is increasing the current value + + public StatDouble(double min, double max, double current, bool allowedToOverflowFromModifiers) + { + Min = min; + Max = max; + Current = current; + AllowedToOverflowFromModifiers = allowedToOverflowFromModifiers; + } + + public void AddCurrent(double value) + { + Current += value; + ClampCurrent(); + } + + public void SetCurrent(double value) + { + Current = value; + ClampCurrent(); + } + + private void ClampCurrent() + { + double currentmax = GetMax(); + double currentmin = GetMin(); + if (Current > currentmax) + { + Current = currentmax; + } + if (Current < currentmin) + { + Current = currentmin; + } + } + + public void AddMax(double value) + { + Max += value; + ClampCurrent(); + } + + public void SetMax(double value) + { + Max = value; + ClampCurrent(); + } + + public void AddMin(double value) + { + Min += value; + ClampCurrent(); + } + + public void SetMin(double value) + { + Min = value; + ClampCurrent(); + } + + public double GetCurrent() + { + double val = Current; + // take the current value and apply each modifier to it + // the modifiers are done in priority order + // OrderedModifierKeys holds the list of priorities in order from highest to lowest + // highest is operated first + // Items on the same priority level don't see eachother + // e.g. if Health is 20 and we have two modifiers with priority 5 that add 50% health, + // then we would get 20 + 10 + 10, NOT 20 + 10 + 15 + // However, if one of the modifiers now has a different priority, say 4, then we would get + // 20 + 10 + 15 + foreach (int k in OrderedModifierKeys) + { + double newval = val; + foreach (IStatModifier mod in Modifiers[k]) + { + if (mod.ModifierType == eStatModifierType.CURRENT) + { + newval += mod.GetValue(Min, Max, val); + } + } + val = newval; + } + + if (!AllowedToOverflowFromModifiers) + { + // if we aren't allowed to go over max, even with modifiers, we must double check + // that we're within our bounds + double currentmax = GetMax(); + double currentmin = GetMin(); + if (val > currentmax) + { + val = currentmax; + } + if (val < currentmin) + { + val = currentmin; + } + } + + return val; + } + + public double GetMin() + { + double val = Min; + foreach (int k in OrderedModifierKeys) + { + double newval = val; + foreach (IStatModifier mod in Modifiers[k]) + { + if (mod.ModifierType == eStatModifierType.MIN) + { + newval += mod.GetValue(val, Max, Current); + } + } + val = newval; + } + return val; + } + + public double GetMax() + { + double val = Max; + foreach (int k in OrderedModifierKeys) + { + double newval = val; + foreach (IStatModifier mod in Modifiers[k]) + { + if (mod.ModifierType == eStatModifierType.MAX) + { + newval += mod.GetValue(Min, val, Current); + } + } + val = newval; + } + + return val; + } + } +} diff --git a/OpenDiablo2.Common/Models/Mobs/StatModifier.cs b/OpenDiablo2.Common/Models/Mobs/StatModifier.cs new file mode 100644 index 00000000..29bfeb45 --- /dev/null +++ b/OpenDiablo2.Common/Models/Mobs/StatModifier.cs @@ -0,0 +1,74 @@ +using OpenDiablo2.Common.Enums.Mobs; +using OpenDiablo2.Common.Interfaces.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Common.Models.Mobs +{ + public class StatModifierAddition : IStatModifier + { + public double Value = 0; + public int Priority { get; private set; } + public string Name { get; private set; } + public eStatModifierType ModifierType { get; private set; } + public StatModifierAddition(string name, double value, int priority = 0, eStatModifierType modifiertype = eStatModifierType.CURRENT) + { + Value = value; + Priority = priority; + Name = name; + ModifierType = modifiertype; + } + public int GetValue(int min, int max, int current) + { + return (int)Value; + } + public double GetValue(double min, double max, double current) + { + return Value; + } + } + + public class StatModifierMultiplication : IStatModifier + { + public double Value = 0; + public int Priority { get; private set; } + public string Name { get; private set; } + public eStatModifierType ModifierType { get; private set; } + public StatModifierMultiplication(string name, double value, int priority = 0, eStatModifierType modifiertype = eStatModifierType.CURRENT) + { + Value = value; + Priority = priority; + Name = name; + ModifierType = modifiertype; + } + public int GetValue(int min, int max, int current) + { + switch (ModifierType) + { + case eStatModifierType.CURRENT: + return (int)(current * Value); + case eStatModifierType.MAX: + return (int)(max * Value); + case eStatModifierType.MIN: + return (int)(min * Value); + } + return 0; // shouldn't reach this + } + public double GetValue(double min, double max, double current) + { + switch (ModifierType) + { + case eStatModifierType.CURRENT: + return (current * Value); + case eStatModifierType.MAX: + return (max * Value); + case eStatModifierType.MIN: + return (min * Value); + } + return 0; // shouldn't reach this + } + } +} diff --git a/OpenDiablo2.Common/OpenDiablo2.Common.csproj b/OpenDiablo2.Common/OpenDiablo2.Common.csproj index b0477340..c4f490ed 100644 --- a/OpenDiablo2.Common/OpenDiablo2.Common.csproj +++ b/OpenDiablo2.Common/OpenDiablo2.Common.csproj @@ -1,120 +1,139 @@ - - - - - Debug - AnyCPU - {B743160E-A0BB-45DC-9998-967A85E50562} - Library - Properties - OpenDiablo2.Common - OpenDiablo2.Common - v4.6.1 - 512 - true - - - true - bin\x64\Debug\ - DEBUG;TRACE - full - x64 - prompt - MinimumRecommendedRules.ruleset - - - bin\x64\Release\ - TRACE - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - - - - ..\packages\DotNetZip.1.12.0\lib\net20\DotNetZip.dll - - - ..\packages\NetSword.Common.ICSharpCode.SharpZipLib.0.84.0\lib\ICSharpCode.SharpZipLib.dll - - - ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Debug + AnyCPU + {B743160E-A0BB-45DC-9998-967A85E50562} + Library + Properties + OpenDiablo2.Common + OpenDiablo2.Common + v4.6.1 + 512 + true + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\DotNetZip.1.12.0\lib\net20\DotNetZip.dll + + + ..\packages\NetSword.Common.ICSharpCode.SharpZipLib.0.84.0\lib\ICSharpCode.SharpZipLib.dll + + + ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenDiablo2.Core.UT/OpenDiablo2.Core.UT.csproj b/OpenDiablo2.Core.UT/OpenDiablo2.Core.UT.csproj index 417ea394..3b6f8bda 100644 --- a/OpenDiablo2.Core.UT/OpenDiablo2.Core.UT.csproj +++ b/OpenDiablo2.Core.UT/OpenDiablo2.Core.UT.csproj @@ -3,7 +3,7 @@ Debug - AnyCPU + x64 {F187FACE-CBA2-4012-8308-06CCDC9D6AB1} Library Properties @@ -20,7 +20,7 @@ - + true full false @@ -30,7 +30,7 @@ 4 x64 - + pdbonly true bin\Release\ @@ -49,8 +49,8 @@ - + diff --git a/OpenDiablo2.Core.UT/UT_MobManager.cs b/OpenDiablo2.Core.UT/UT_MobManager.cs new file mode 100644 index 00000000..1b4108c5 --- /dev/null +++ b/OpenDiablo2.Core.UT/UT_MobManager.cs @@ -0,0 +1,72 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenDiablo2.Common.Enums.Mobs; +using OpenDiablo2.Common.Models.Mobs; +using OpenDiablo2.Core.GameState_; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Core.UT +{ + [TestClass] + public class UT_MobManager + { + [TestMethod] + public void FindMobsTest() + { + MobManager mobman = new MobManager(); + MobState mob1 = new MobState("test1", mobman.GetNextAvailableMobId(), 1, 100, 0, 0); + MobState mob2 = new MobState("test2", mobman.GetNextAvailableMobId(), 1, 100, 0, 0); + MobState mob3 = new MobState("test3", mobman.GetNextAvailableMobId(), 1, 100, 0, 0); + mob1.AddFlag(eMobFlags.ENEMY); + mob2.AddFlag(eMobFlags.ENEMY); + mob2.AddFlag(eMobFlags.INVULNERABLE); + mobman.AddMob(mob1); + mobman.AddMob(mob2); + mobman.AddMob(mob3); + + Assert.IsTrue(mobman.FindMobs(new MobConditionFlags( + new Dictionary { + { eMobFlags.ENEMY, true } + })).Count == 2); + + Assert.IsTrue(mobman.FindMobs(new MobConditionFlags( + new Dictionary { + { eMobFlags.INVULNERABLE, true } + })).Count == 1); + + Assert.IsTrue(mobman.FindMobs(new MobConditionFlags( + new Dictionary { + { eMobFlags.PLAYER, true } + })).Count == 0); + + Assert.IsTrue(mobman.FindMobs(new MobConditionFlags( + new Dictionary { + { eMobFlags.PLAYER, false } + })).Count == 3); + } + + [TestMethod] + public void FindMobsInRadiusTest() + { + MobManager mobman = new MobManager(); + MobState mob1 = new MobState("test1", mobman.GetNextAvailableMobId(), 1, 100, 0, 0); + MobState mob2 = new MobState("test2", mobman.GetNextAvailableMobId(), 1, 100, 10, 10); + MobState mob3 = new MobState("test3", mobman.GetNextAvailableMobId(), 1, 100, 3, 1); + mob1.AddFlag(eMobFlags.ENEMY); + mob2.AddFlag(eMobFlags.ENEMY); + mob2.AddFlag(eMobFlags.INVULNERABLE); + mobman.AddMob(mob1); + mobman.AddMob(mob2); + mobman.AddMob(mob3); + + Assert.IsTrue(mobman.FindMobsInRadius(0, 0, 1, null).Count == 1); + Assert.IsTrue(mobman.FindMobsInRadius(0, 0, 7, null).Count == 2); + Assert.IsTrue(mobman.FindMobsInRadius(0, 0, 20, null).Count == 3); + Assert.IsTrue(mobman.FindMobsInRadius(10, 10, 1, null).Count == 1); + } + } +} diff --git a/OpenDiablo2.Core/GameState/GameState.cs b/OpenDiablo2.Core/GameState/GameState.cs index 317d3512..2f765c2b 100644 --- a/OpenDiablo2.Core/GameState/GameState.cs +++ b/OpenDiablo2.Core/GameState/GameState.cs @@ -226,18 +226,18 @@ namespace OpenDiablo2.Core.GameState_ } - public bool ToggleShowInventoryPanel() - { - ShowInventoryPanel = !ShowInventoryPanel; - - return ShowInventoryPanel; + public bool ToggleShowInventoryPanel() + { + ShowInventoryPanel = !ShowInventoryPanel; + + return ShowInventoryPanel; } - public bool ToggleShowCharacterPanel() - { - ShowCharacterPanel = !ShowCharacterPanel; - - return ShowCharacterPanel; + public bool ToggleShowCharacterPanel() + { + ShowCharacterPanel = !ShowCharacterPanel; + + return ShowCharacterPanel; } diff --git a/OpenDiablo2.Core/GameState/MobManager.cs b/OpenDiablo2.Core/GameState/MobManager.cs new file mode 100644 index 00000000..1665033c --- /dev/null +++ b/OpenDiablo2.Core/GameState/MobManager.cs @@ -0,0 +1,141 @@ +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces.Mobs; +using OpenDiablo2.Common.Models.Mobs; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenDiablo2.Core.GameState_ +{ + public class MobManager : IMobManager + { + private List Mobs = new List(); // all mobs (including players!) + private List Players = new List(); + private List Enemies = new List(); + private List IdsUsed = new List(); + + #region Player Controls + public void AddPlayer(PlayerState player) + { + Players.Add(player); + AddMob(player); + } + public void RemovePlayer(PlayerState player) + { + Players.Remove(player); + RemoveMob(player); + } + #endregion Player Controls + + #region Mob Controls + public void AddMob(MobState mob) + { + Mobs.Add(mob); + // add id to idsused in order + int i = 0; + while(i < IdsUsed.Count) + { + if(IdsUsed[i] > mob.Id) + { + IdsUsed.Insert(i, mob.Id); + break; + } + i++; + } + if(i == IdsUsed.Count) + { + // didn't get added + IdsUsed.Add(mob.Id); + } + } + public void RemoveMob(MobState mob) + { + Mobs.Remove(mob); + IdsUsed.Remove(mob.Id); + } + public int GetNextAvailableMobId() + { + int i = 0; + while(i < IdsUsed.Count) + { + if(IdsUsed[i] != i) + { + return i; + } + i++; + } + return IdsUsed.Count; + } + #endregion Mob Controls + + #region Enemy Controls + public void AddEnemy(EnemyState enemy) + { + Enemies.Add(enemy); + AddMob(enemy); + } + public void RemoveEnemy(EnemyState enemy) + { + Enemies.Remove(enemy); + RemoveMob(enemy); + } + #endregion Enemy Controls + + #region Searching and Filtering + public List FilterMobs(IEnumerable mobs, IMobCondition condition) + { + // note: if condition is null, returns full list + List filtered = new List(); + foreach(MobState mob in mobs) + { + if (condition == null || condition.Evaluate(mob)) + { + filtered.Add(mob); + } + } + return filtered; + } + + public List FindMobs(IMobCondition condition) + { + return FilterMobs(Mobs, condition); + } + public List FindEnemies(IMobCondition condition) + { + return FilterMobs(Enemies, condition); + } + public List FindPlayers(IMobCondition condition) + { + return FilterMobs(Players, condition); + } + + public List FindInRadius(IEnumerable mobs, float centerx, float centery, float radius) + { + List filtered = new List(); + foreach(MobState mob in mobs) + { + if(mob.GetDistance(centerx, centery) <= radius) + { + filtered.Add(mob); + } + } + return filtered; + } + + public List FindMobsInRadius(float centerx, float centery, float radius, IMobCondition condition) + { + return FilterMobs(FindInRadius(Mobs, centerx, centery, radius), condition); + } + public List FindEnemiesInRadius(float centerx, float centery, float radius, IMobCondition condition) + { + return FilterMobs(FindInRadius(Enemies, centerx, centery, radius), condition); + } + public List FindPlayersInRadius(float centerx, float centery, float radius, IMobCondition condition) + { + return FilterMobs(FindInRadius(Players, centerx, centery, radius), condition); + } + #endregion Searching and Filtering + } +} diff --git a/OpenDiablo2.Core/OpenDiablo2.Core.csproj b/OpenDiablo2.Core/OpenDiablo2.Core.csproj index 0e57cf3f..f9f023fd 100644 --- a/OpenDiablo2.Core/OpenDiablo2.Core.csproj +++ b/OpenDiablo2.Core/OpenDiablo2.Core.csproj @@ -1,85 +1,86 @@ - - - - - Debug - AnyCPU - {8FC6BF7D-835A-47C1-A6B2-125495FA0900} - Library - Properties - OpenDiablo2.Core - OpenDiablo2.Core - v4.6.1 - 512 - true - - - true - bin\x64\Debug\ - DEBUG;TRACE - full - x64 - prompt - MinimumRecommendedRules.ruleset - - - bin\x64\Release\ - TRACE - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - - - - ..\packages\Autofac.4.8.1\lib\net45\Autofac.dll - - - ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll - - - ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - ..\packages\Xabe.FFmpeg.3.1.4\lib\netstandard2.0\Xabe.FFmpeg.dll - - - - - - - - + + + + + Debug + AnyCPU + {8FC6BF7D-835A-47C1-A6B2-125495FA0900} + Library + Properties + OpenDiablo2.Core + OpenDiablo2.Core + v4.6.1 + 512 + true + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\Autofac.4.8.1\lib\net45\Autofac.dll + + + ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll + + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + ..\packages\Xabe.FFmpeg.3.1.4\lib\netstandard2.0\Xabe.FFmpeg.dll + + + + + + + + + - - - - - - - - - - - - - - {b743160e-a0bb-45dc-9998-967a85e50562} - OpenDiablo2.Common - - - - - - + + + + + + + + + + + + + + {b743160e-a0bb-45dc-9998-967a85e50562} + OpenDiablo2.Common + + + + + + \ No newline at end of file diff --git a/OpenDiablo2.Core/UI/CharacterPanel.cs b/OpenDiablo2.Core/UI/CharacterPanel.cs index 43c46e6a..b6fbac47 100644 --- a/OpenDiablo2.Core/UI/CharacterPanel.cs +++ b/OpenDiablo2.Core/UI/CharacterPanel.cs @@ -1,44 +1,44 @@ -using System; -using System.Drawing; -using OpenDiablo2.Common; -using OpenDiablo2.Common.Enums; -using OpenDiablo2.Common.Interfaces; - -namespace OpenDiablo2.Core.UI -{ - public sealed class CharacterPanel : ICharacterPanel - { - private readonly IRenderWindow renderWindow; - private ISprite sprite; - private IPanelFrame panelFrame; - - public Point Location { get; set; } - - public CharacterPanel(IRenderWindow renderWindow, Func createPanelFrame) - { - this.renderWindow = renderWindow; - this.panelFrame = createPanelFrame(ePanelFrameType.Left); - - sprite = renderWindow.LoadSprite(ResourcePaths.InventoryCharacterPanel, Palettes.Units, new Point(79,61)); - Location = new Point(0, 0); - - - } - - public void Update() - { - - } - - public void Render() - { - panelFrame.Render(); - renderWindow.Draw(sprite, 2, 2, 0); - } - - public void Dispose() - { - sprite.Dispose(); - } - } -} +using System; +using System.Drawing; +using OpenDiablo2.Common; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces; + +namespace OpenDiablo2.Core.UI +{ + public sealed class CharacterPanel : ICharacterPanel + { + private readonly IRenderWindow renderWindow; + private ISprite sprite; + private IPanelFrame panelFrame; + + public Point Location { get; set; } + + public CharacterPanel(IRenderWindow renderWindow, Func createPanelFrame) + { + this.renderWindow = renderWindow; + this.panelFrame = createPanelFrame(ePanelFrameType.Left); + + sprite = renderWindow.LoadSprite(ResourcePaths.InventoryCharacterPanel, Palettes.Units, new Point(79,61)); + Location = new Point(0, 0); + + + } + + public void Update() + { + + } + + public void Render() + { + panelFrame.Render(); + renderWindow.Draw(sprite, 2, 2, 0); + } + + public void Dispose() + { + sprite.Dispose(); + } + } +} diff --git a/OpenDiablo2.Core/UI/InventoryPanel.cs b/OpenDiablo2.Core/UI/InventoryPanel.cs index 89d8159d..4293525a 100644 --- a/OpenDiablo2.Core/UI/InventoryPanel.cs +++ b/OpenDiablo2.Core/UI/InventoryPanel.cs @@ -1,42 +1,42 @@ -using System; -using System.Drawing; -using OpenDiablo2.Common; -using OpenDiablo2.Common.Enums; -using OpenDiablo2.Common.Interfaces; - -namespace OpenDiablo2.Core.UI -{ - public sealed class InventoryPanel : IInventoryPanel - { - private readonly IRenderWindow renderWindow; - private ISprite sprite; - private IPanelFrame panelFrame; - - public Point Location { get; set; } - - public InventoryPanel(Func createPanelFrame, IRenderWindow renderWindow) - { - this.renderWindow = renderWindow; - this.panelFrame = createPanelFrame(ePanelFrameType.Right); - - sprite = renderWindow.LoadSprite(ResourcePaths.InventoryCharacterPanel, Palettes.Units, new Point(402,61)); - Location = new Point(400, 0); - } - - public void Update() - { - - } - - public void Render() - { - panelFrame.Render(); - renderWindow.Draw(sprite, 2, 2, 1); - } - - public void Dispose() - { - sprite.Dispose(); - } - } -} +using System; +using System.Drawing; +using OpenDiablo2.Common; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces; + +namespace OpenDiablo2.Core.UI +{ + public sealed class InventoryPanel : IInventoryPanel + { + private readonly IRenderWindow renderWindow; + private ISprite sprite; + private IPanelFrame panelFrame; + + public Point Location { get; set; } + + public InventoryPanel(Func createPanelFrame, IRenderWindow renderWindow) + { + this.renderWindow = renderWindow; + this.panelFrame = createPanelFrame(ePanelFrameType.Right); + + sprite = renderWindow.LoadSprite(ResourcePaths.InventoryCharacterPanel, Palettes.Units, new Point(402,61)); + Location = new Point(400, 0); + } + + public void Update() + { + + } + + public void Render() + { + panelFrame.Render(); + renderWindow.Draw(sprite, 2, 2, 1); + } + + public void Dispose() + { + sprite.Dispose(); + } + } +} diff --git a/OpenDiablo2.Core/UI/MiniPanel.cs b/OpenDiablo2.Core/UI/MiniPanel.cs index 14bd23ff..714b591d 100644 --- a/OpenDiablo2.Core/UI/MiniPanel.cs +++ b/OpenDiablo2.Core/UI/MiniPanel.cs @@ -1,101 +1,101 @@ -using System; -using System.Drawing; -using OpenDiablo2.Common; -using OpenDiablo2.Common.Enums; -using OpenDiablo2.Common.Interfaces; - -namespace OpenDiablo2.Core.UI -{ - // TODO: Self-align when side panels are open - public sealed class MiniPanel : IMiniPanel - { - private readonly IRenderWindow renderWindow; - private ISprite sprite; - - private IButton characterBtn, inventoryBtn, skillBtn, automapBtn, messageBtn, questBtn, menuBtn; - - private Point location = new Point(); - public Point Location - { - get => location; - set - { - if (location == value) - return; - location = value; - - sprite.Location = new Point(value.X, value.Y + sprite.LocalFrameSize.Height); - } - } - - public MiniPanel(IRenderWindow renderWindow, IGameState gameState, Func createButton) - { - this.renderWindow = renderWindow; - - sprite = renderWindow.LoadSprite(ResourcePaths.MinipanelSmall, Palettes.Units); - Location = new Point(800/2-sprite.LocalFrameSize.Width/2, 526); - - characterBtn = createButton(eButtonType.MinipanelCharacter); - characterBtn.Location = new Point(3 + Location.X, 3 + Location.Y); - characterBtn.OnActivate = () => gameState.ToggleShowCharacterPanel(); - - inventoryBtn = createButton(eButtonType.MinipanelInventory); - inventoryBtn.Location = new Point(24 + Location.X, 3 + Location.Y); - inventoryBtn.OnActivate = () => gameState.ToggleShowInventoryPanel(); - - skillBtn = createButton(eButtonType.MinipanelSkill); - skillBtn.Location = new Point(45 + Location.X, 3 + Location.Y); - - automapBtn = createButton(eButtonType.MinipanelAutomap); - automapBtn.Location = new Point(66 + Location.X, 3 + Location.Y); - - messageBtn = createButton(eButtonType.MinipanelMessage); - messageBtn.Location = new Point(87 + Location.X, 3 + Location.Y); - - questBtn = createButton(eButtonType.MinipanelQuest); - questBtn.Location = new Point(108 + Location.X, 3 + Location.Y); - - menuBtn = createButton(eButtonType.MinipanelMenu); - menuBtn.Location = new Point(129 + Location.X, 3 + Location.Y); - } - - - public void Update() - { - characterBtn.Update(); - inventoryBtn.Update(); - skillBtn.Update(); - automapBtn.Update(); - messageBtn.Update(); - questBtn.Update(); - menuBtn.Update(); - - } - - public void Render() - { - renderWindow.Draw(sprite); - - characterBtn.Render(); - inventoryBtn.Render(); - skillBtn.Render(); - automapBtn.Render(); - messageBtn.Render(); - questBtn.Render(); - menuBtn.Render(); - } - - public void Dispose() - { - characterBtn.Dispose(); - inventoryBtn.Dispose(); - skillBtn.Dispose(); - automapBtn.Dispose(); - messageBtn.Dispose(); - questBtn.Dispose(); - menuBtn.Dispose(); - - sprite.Dispose(); - } - } -} +using System; +using System.Drawing; +using OpenDiablo2.Common; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces; + +namespace OpenDiablo2.Core.UI +{ + // TODO: Self-align when side panels are open + public sealed class MiniPanel : IMiniPanel + { + private readonly IRenderWindow renderWindow; + private ISprite sprite; + + private IButton characterBtn, inventoryBtn, skillBtn, automapBtn, messageBtn, questBtn, menuBtn; + + private Point location = new Point(); + public Point Location + { + get => location; + set + { + if (location == value) + return; + location = value; + + sprite.Location = new Point(value.X, value.Y + sprite.LocalFrameSize.Height); + } + } + + public MiniPanel(IRenderWindow renderWindow, IGameState gameState, Func createButton) + { + this.renderWindow = renderWindow; + + sprite = renderWindow.LoadSprite(ResourcePaths.MinipanelSmall, Palettes.Units); + Location = new Point(800/2-sprite.LocalFrameSize.Width/2, 526); + + characterBtn = createButton(eButtonType.MinipanelCharacter); + characterBtn.Location = new Point(3 + Location.X, 3 + Location.Y); + characterBtn.OnActivate = () => gameState.ToggleShowCharacterPanel(); + + inventoryBtn = createButton(eButtonType.MinipanelInventory); + inventoryBtn.Location = new Point(24 + Location.X, 3 + Location.Y); + inventoryBtn.OnActivate = () => gameState.ToggleShowInventoryPanel(); + + skillBtn = createButton(eButtonType.MinipanelSkill); + skillBtn.Location = new Point(45 + Location.X, 3 + Location.Y); + + automapBtn = createButton(eButtonType.MinipanelAutomap); + automapBtn.Location = new Point(66 + Location.X, 3 + Location.Y); + + messageBtn = createButton(eButtonType.MinipanelMessage); + messageBtn.Location = new Point(87 + Location.X, 3 + Location.Y); + + questBtn = createButton(eButtonType.MinipanelQuest); + questBtn.Location = new Point(108 + Location.X, 3 + Location.Y); + + menuBtn = createButton(eButtonType.MinipanelMenu); + menuBtn.Location = new Point(129 + Location.X, 3 + Location.Y); + } + + + public void Update() + { + characterBtn.Update(); + inventoryBtn.Update(); + skillBtn.Update(); + automapBtn.Update(); + messageBtn.Update(); + questBtn.Update(); + menuBtn.Update(); + + } + + public void Render() + { + renderWindow.Draw(sprite); + + characterBtn.Render(); + inventoryBtn.Render(); + skillBtn.Render(); + automapBtn.Render(); + messageBtn.Render(); + questBtn.Render(); + menuBtn.Render(); + } + + public void Dispose() + { + characterBtn.Dispose(); + inventoryBtn.Dispose(); + skillBtn.Dispose(); + automapBtn.Dispose(); + messageBtn.Dispose(); + questBtn.Dispose(); + menuBtn.Dispose(); + + sprite.Dispose(); + } + } +} diff --git a/OpenDiablo2.Core/UI/PanelFrame.cs b/OpenDiablo2.Core/UI/PanelFrame.cs index 3a7e0d2f..a9fd6717 100644 --- a/OpenDiablo2.Core/UI/PanelFrame.cs +++ b/OpenDiablo2.Core/UI/PanelFrame.cs @@ -1,67 +1,67 @@ -using System; -using System.Drawing; -using OpenDiablo2.Common; -using OpenDiablo2.Common.Enums; -using OpenDiablo2.Common.Interfaces; - -namespace OpenDiablo2.Core.UI -{ - public sealed class PanelFrame : IPanelFrame - { - private readonly IRenderWindow renderWindow; - private ISprite sprite; - private ePanelFrameType panelFrameType; - - public Point Location { get; set; } - - public PanelFrame(IRenderWindow renderWindow, ePanelFrameType panelFrameType) - { - this.renderWindow = renderWindow; - this.panelFrameType = panelFrameType; - - sprite = renderWindow.LoadSprite(ResourcePaths.Frame, Palettes.Units, new Point(0, 0)); - - Location = new Point(0, 0); - - - } - - private void DrawPanel() - { - switch(this.panelFrameType) - { - case ePanelFrameType.Left: - renderWindow.Draw(sprite, 0, new Point(0, 256)); - renderWindow.Draw(sprite, 1, new Point(256, 66)); - renderWindow.Draw(sprite, 2, new Point(0, 256 + 231)); - renderWindow.Draw(sprite, 3, new Point(0, 256 + 231 + 66)); - renderWindow.Draw(sprite, 4, new Point(256, 256 + 231 + 66)); - break; - case ePanelFrameType.Right: - renderWindow.Draw(sprite, 5, new Point(400 + 0, 66)); - renderWindow.Draw(sprite, 6, new Point(400 + 145, 256)); - renderWindow.Draw(sprite, 7, new Point(400 + 145 + 169, 256 + 231)); - renderWindow.Draw(sprite, 8, new Point(400 + 145, 256 + 231 + 66)); - renderWindow.Draw(sprite, 9, new Point(400 + 0, 256 + 231 + 66)); - break; - } - } - - - public void Update() - { - - - } - - public void Render() - { - DrawPanel(); - } - - public void Dispose() - { - sprite.Dispose(); - } - } -} +using System; +using System.Drawing; +using OpenDiablo2.Common; +using OpenDiablo2.Common.Enums; +using OpenDiablo2.Common.Interfaces; + +namespace OpenDiablo2.Core.UI +{ + public sealed class PanelFrame : IPanelFrame + { + private readonly IRenderWindow renderWindow; + private ISprite sprite; + private ePanelFrameType panelFrameType; + + public Point Location { get; set; } + + public PanelFrame(IRenderWindow renderWindow, ePanelFrameType panelFrameType) + { + this.renderWindow = renderWindow; + this.panelFrameType = panelFrameType; + + sprite = renderWindow.LoadSprite(ResourcePaths.Frame, Palettes.Units, new Point(0, 0)); + + Location = new Point(0, 0); + + + } + + private void DrawPanel() + { + switch(this.panelFrameType) + { + case ePanelFrameType.Left: + renderWindow.Draw(sprite, 0, new Point(0, 256)); + renderWindow.Draw(sprite, 1, new Point(256, 66)); + renderWindow.Draw(sprite, 2, new Point(0, 256 + 231)); + renderWindow.Draw(sprite, 3, new Point(0, 256 + 231 + 66)); + renderWindow.Draw(sprite, 4, new Point(256, 256 + 231 + 66)); + break; + case ePanelFrameType.Right: + renderWindow.Draw(sprite, 5, new Point(400 + 0, 66)); + renderWindow.Draw(sprite, 6, new Point(400 + 145, 256)); + renderWindow.Draw(sprite, 7, new Point(400 + 145 + 169, 256 + 231)); + renderWindow.Draw(sprite, 8, new Point(400 + 145, 256 + 231 + 66)); + renderWindow.Draw(sprite, 9, new Point(400 + 0, 256 + 231 + 66)); + break; + } + } + + + public void Update() + { + + + } + + public void Render() + { + DrawPanel(); + } + + public void Dispose() + { + sprite.Dispose(); + } + } +} diff --git a/OpenDiablo2.sln b/OpenDiablo2.sln index ea71aad5..545ba0aa 100644 --- a/OpenDiablo2.sln +++ b/OpenDiablo2.sln @@ -15,6 +15,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.Scenes", "OpenD EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.TestConsole", "OpenDiablo2.TestConsole\OpenDiablo2.TestConsole.csproj", "{40BD2DDE-DC6F-4F6D-9050-9B423C631192}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.Common.UT", "OpenDiablo2.Common.UT\OpenDiablo2.Common.UT.csproj", "{77B00705-A2A5-4675-9A16-1FAB2692151B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenDiablo2.Core.UT", "OpenDiablo2.Core.UT\OpenDiablo2.Core.UT.csproj", "{F187FACE-CBA2-4012-8308-06CCDC9D6AB1}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{A1AAF640-AEFB-48E7-8BCE-E01D287B5286}" EndProject Global @@ -47,6 +51,14 @@ Global {40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Debug|x64.Build.0 = Debug|x64 {40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Release|x64.ActiveCfg = Release|x64 {40BD2DDE-DC6F-4F6D-9050-9B423C631192}.Release|x64.Build.0 = Release|x64 + {77B00705-A2A5-4675-9A16-1FAB2692151B}.Debug|x64.ActiveCfg = Debug|x64 + {77B00705-A2A5-4675-9A16-1FAB2692151B}.Debug|x64.Build.0 = Debug|x64 + {77B00705-A2A5-4675-9A16-1FAB2692151B}.Release|x64.ActiveCfg = Release|x64 + {77B00705-A2A5-4675-9A16-1FAB2692151B}.Release|x64.Build.0 = Release|x64 + {F187FACE-CBA2-4012-8308-06CCDC9D6AB1}.Debug|x64.ActiveCfg = Debug|x64 + {F187FACE-CBA2-4012-8308-06CCDC9D6AB1}.Debug|x64.Build.0 = Debug|x64 + {F187FACE-CBA2-4012-8308-06CCDC9D6AB1}.Release|x64.ActiveCfg = Release|x64 + {F187FACE-CBA2-4012-8308-06CCDC9D6AB1}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE