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