mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-26 13:15:56 -04:00
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
This commit is contained in:
parent
f3793e0a60
commit
55f8f3ef34
76
OpenDiablo2.Common.UT/OpenDiablo2.Common.UT.csproj
Normal file
76
OpenDiablo2.Common.UT/OpenDiablo2.Common.UT.csproj
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{77B00705-A2A5-4675-9A16-1FAB2692151B}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>OpenDiablo2.Common.UT</RootNamespace>
|
||||
<AssemblyName>OpenDiablo2.Common.UT</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
||||
<IsCodedUITest>False</IsCodedUITest>
|
||||
<TestProjectType>UnitTest</TestProjectType>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="UT_MobState.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="UT_PlayerState.cs" />
|
||||
<Compile Include="UT_Stat.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenDiablo2.Common\OpenDiablo2.Common.csproj">
|
||||
<Project>{B743160E-A0BB-45DC-9998-967A85E50562}</Project>
|
||||
<Name>OpenDiablo2.Common</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>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}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.targets')" />
|
||||
</Project>
|
20
OpenDiablo2.Common.UT/Properties/AssemblyInfo.cs
Normal file
20
OpenDiablo2.Common.UT/Properties/AssemblyInfo.cs
Normal file
@ -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")]
|
56
OpenDiablo2.Common.UT/UT_MobState.cs
Normal file
56
OpenDiablo2.Common.UT/UT_MobState.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
72
OpenDiablo2.Common.UT/UT_PlayerState.cs
Normal file
72
OpenDiablo2.Common.UT/UT_PlayerState.cs
Normal file
@ -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<int>()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
80
OpenDiablo2.Common.UT/UT_Stat.cs
Normal file
80
OpenDiablo2.Common.UT/UT_Stat.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
5
OpenDiablo2.Common.UT/packages.config
Normal file
5
OpenDiablo2.Common.UT/packages.config
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="MSTest.TestAdapter" version="1.2.0" targetFramework="net461" />
|
||||
<package id="MSTest.TestFramework" version="1.2.0" targetFramework="net461" />
|
||||
</packages>
|
19
OpenDiablo2.Common/Enums/Mobs/eDamageTypes.cs
Normal file
19
OpenDiablo2.Common/Enums/Mobs/eDamageTypes.cs
Normal file
@ -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,
|
||||
}
|
||||
}
|
15
OpenDiablo2.Common/Enums/Mobs/eMobFlags.cs
Normal file
15
OpenDiablo2.Common/Enums/Mobs/eMobFlags.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
16
OpenDiablo2.Common/Enums/Mobs/eStatModifierType.cs
Normal file
16
OpenDiablo2.Common/Enums/Mobs/eStatModifierType.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
19
OpenDiablo2.Common/Enums/eDamageTypes.cs
Normal file
19
OpenDiablo2.Common/Enums/eDamageTypes.cs
Normal file
@ -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,
|
||||
}
|
||||
}
|
15
OpenDiablo2.Common/Enums/eMobFlags.cs
Normal file
15
OpenDiablo2.Common/Enums/eMobFlags.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
16
OpenDiablo2.Common/Enums/eStatModifierType.cs
Normal file
16
OpenDiablo2.Common/Enums/eStatModifierType.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
35
OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs
Normal file
35
OpenDiablo2.Common/Interfaces/Mobs/IHeroTypeConfig.cs
Normal file
@ -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; }
|
||||
}
|
||||
}
|
14
OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs
Normal file
14
OpenDiablo2.Common/Interfaces/Mobs/ILevelExperienceConfig.cs
Normal file
@ -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();
|
||||
}
|
||||
}
|
14
OpenDiablo2.Common/Interfaces/Mobs/IMobCondition.cs
Normal file
14
OpenDiablo2.Common/Interfaces/Mobs/IMobCondition.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
13
OpenDiablo2.Common/Interfaces/Mobs/IMobManager.cs
Normal file
13
OpenDiablo2.Common/Interfaces/Mobs/IMobManager.cs
Normal file
@ -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:
|
||||
}
|
||||
}
|
19
OpenDiablo2.Common/Interfaces/Mobs/IStatModifier.cs
Normal file
19
OpenDiablo2.Common/Interfaces/Mobs/IStatModifier.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
21
OpenDiablo2.Common/Models/Mobs/EnemyState.cs
Normal file
21
OpenDiablo2.Common/Models/Mobs/EnemyState.cs
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
62
OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs
Normal file
62
OpenDiablo2.Common/Models/Mobs/HeroTypeConfig.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
33
OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs
Normal file
33
OpenDiablo2.Common/Models/Mobs/LevelExperienceConfig.cs
Normal file
@ -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<int> ExperiencePerLevel = new List<int>();
|
||||
|
||||
public LevelExperienceConfig(List<int> 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;
|
||||
}
|
||||
}
|
||||
}
|
97
OpenDiablo2.Common/Models/Mobs/MobCondition.cs
Normal file
97
OpenDiablo2.Common/Models/Mobs/MobCondition.cs
Normal file
@ -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<IMobCondition> Conditions = new List<IMobCondition>();
|
||||
|
||||
public MobConditionAnd(List<IMobCondition> 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<IMobCondition> Conditions = new List<IMobCondition>();
|
||||
|
||||
public MobConditionOr(List<IMobCondition> 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<eMobFlags, bool> Flags = new Dictionary<eMobFlags, bool>();
|
||||
|
||||
public MobConditionFlags(Dictionary<eMobFlags, bool> 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
|
||||
}
|
168
OpenDiablo2.Common/Models/Mobs/MobState.cs
Normal file
168
OpenDiablo2.Common/Models/Mobs/MobState.cs
Normal file
@ -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<eDamageTypes, StatDouble> Resistances = new Dictionary<eDamageTypes, StatDouble>();
|
||||
protected List<eDamageTypes> Immunities = new List<eDamageTypes>();
|
||||
|
||||
public int Level { get; protected set; }
|
||||
|
||||
protected Dictionary<eMobFlags, bool> Flags = new Dictionary<eMobFlags, bool>();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
208
OpenDiablo2.Common/Models/Mobs/PlayerState.cs
Normal file
208
OpenDiablo2.Common/Models/Mobs/PlayerState.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
159
OpenDiablo2.Common/Models/Mobs/Stat.cs
Normal file
159
OpenDiablo2.Common/Models/Mobs/Stat.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
77
OpenDiablo2.Common/Models/Mobs/StatBase.cs
Normal file
77
OpenDiablo2.Common/Models/Mobs/StatBase.cs
Normal file
@ -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<int> OrderedModifierKeys = new List<int>();
|
||||
protected Dictionary<int, List<IStatModifier>> Modifiers = new Dictionary<int, List<IStatModifier>>();
|
||||
|
||||
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<IStatModifier>() { 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
159
OpenDiablo2.Common/Models/Mobs/StatDouble.cs
Normal file
159
OpenDiablo2.Common/Models/Mobs/StatDouble.cs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
74
OpenDiablo2.Common/Models/Mobs/StatModifier.cs
Normal file
74
OpenDiablo2.Common/Models/Mobs/StatModifier.cs
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -63,6 +63,9 @@
|
||||
<Compile Include="Enums\eRenderCellType.cs" />
|
||||
<Compile Include="Enums\eTextAlign.cs" />
|
||||
<Compile Include="Enums\eWildBorder.cs" />
|
||||
<Compile Include="Enums\Mobs\eDamageTypes.cs" />
|
||||
<Compile Include="Enums\Mobs\eMobFlags.cs" />
|
||||
<Compile Include="Enums\Mobs\eStatModifierType.cs" />
|
||||
<Compile Include="Interfaces\IButton.cs" />
|
||||
<Compile Include="Interfaces\IPanelFrame.cs" />
|
||||
<Compile Include="Interfaces\IInventoryPanel.cs" />
|
||||
@ -89,6 +92,11 @@
|
||||
<Compile Include="Interfaces\ITextDictionary.cs" />
|
||||
<Compile Include="Interfaces\ITextLabel.cs" />
|
||||
<Compile Include="Interfaces\ITexture.cs" />
|
||||
<Compile Include="Interfaces\Mobs\IHeroTypeConfig.cs" />
|
||||
<Compile Include="Interfaces\Mobs\ILevelExperienceConfig.cs" />
|
||||
<Compile Include="Interfaces\Mobs\IMobCondition.cs" />
|
||||
<Compile Include="Interfaces\Mobs\IMobManager.cs" />
|
||||
<Compile Include="Interfaces\Mobs\IStatModifier.cs" />
|
||||
<Compile Include="Models\BitStream.cs" />
|
||||
<Compile Include="Models\ButtonLayout.cs" />
|
||||
<Compile Include="Models\LevelDetail.cs" />
|
||||
@ -96,6 +104,16 @@
|
||||
<Compile Include="Models\LevelType.cs" />
|
||||
<Compile Include="Models\MapCellInfo.cs" />
|
||||
<Compile Include="Models\MapInfo.cs" />
|
||||
<Compile Include="Models\Mobs\EnemyState.cs" />
|
||||
<Compile Include="Models\Mobs\HeroTypeConfig.cs" />
|
||||
<Compile Include="Models\Mobs\LevelExperienceConfig.cs" />
|
||||
<Compile Include="Models\Mobs\MobCondition.cs" />
|
||||
<Compile Include="Models\Mobs\MobState.cs" />
|
||||
<Compile Include="Models\Mobs\PlayerState.cs" />
|
||||
<Compile Include="Models\Mobs\Stat.cs" />
|
||||
<Compile Include="Models\Mobs\StatBase.cs" />
|
||||
<Compile Include="Models\Mobs\StatDouble.cs" />
|
||||
<Compile Include="Models\Mobs\StatModifier.cs" />
|
||||
<Compile Include="Models\MPQDS1.cs" />
|
||||
<Compile Include="Models\MPQDT1.cs" />
|
||||
<Compile Include="Models\MPQFont.cs" />
|
||||
@ -116,5 +134,6 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@ -3,7 +3,7 @@
|
||||
<Import Project="..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.1.2.0\build\net45\MSTest.TestAdapter.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x64</Platform>
|
||||
<ProjectGuid>{F187FACE-CBA2-4012-8308-06CCDC9D6AB1}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
@ -20,7 +20,7 @@
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
@ -30,7 +30,7 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
@ -49,8 +49,8 @@
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="UT_EngineDataManager.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="UT_MobManager.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
72
OpenDiablo2.Core.UT/UT_MobManager.cs
Normal file
72
OpenDiablo2.Core.UT/UT_MobManager.cs
Normal file
@ -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, bool> {
|
||||
{ eMobFlags.ENEMY, true }
|
||||
})).Count == 2);
|
||||
|
||||
Assert.IsTrue(mobman.FindMobs(new MobConditionFlags(
|
||||
new Dictionary<eMobFlags, bool> {
|
||||
{ eMobFlags.INVULNERABLE, true }
|
||||
})).Count == 1);
|
||||
|
||||
Assert.IsTrue(mobman.FindMobs(new MobConditionFlags(
|
||||
new Dictionary<eMobFlags, bool> {
|
||||
{ eMobFlags.PLAYER, true }
|
||||
})).Count == 0);
|
||||
|
||||
Assert.IsTrue(mobman.FindMobs(new MobConditionFlags(
|
||||
new Dictionary<eMobFlags, bool> {
|
||||
{ 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);
|
||||
}
|
||||
}
|
||||
}
|
141
OpenDiablo2.Core/GameState/MobManager.cs
Normal file
141
OpenDiablo2.Core/GameState/MobManager.cs
Normal file
@ -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<MobState> Mobs = new List<MobState>(); // all mobs (including players!)
|
||||
private List<PlayerState> Players = new List<PlayerState>();
|
||||
private List<EnemyState> Enemies = new List<EnemyState>();
|
||||
private List<int> IdsUsed = new List<int>();
|
||||
|
||||
#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<MobState> FilterMobs(IEnumerable<MobState> mobs, IMobCondition condition)
|
||||
{
|
||||
// note: if condition is null, returns full list
|
||||
List<MobState> filtered = new List<MobState>();
|
||||
foreach(MobState mob in mobs)
|
||||
{
|
||||
if (condition == null || condition.Evaluate(mob))
|
||||
{
|
||||
filtered.Add(mob);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public List<MobState> FindMobs(IMobCondition condition)
|
||||
{
|
||||
return FilterMobs(Mobs, condition);
|
||||
}
|
||||
public List<MobState> FindEnemies(IMobCondition condition)
|
||||
{
|
||||
return FilterMobs(Enemies, condition);
|
||||
}
|
||||
public List<MobState> FindPlayers(IMobCondition condition)
|
||||
{
|
||||
return FilterMobs(Players, condition);
|
||||
}
|
||||
|
||||
public List<MobState> FindInRadius(IEnumerable<MobState> mobs, float centerx, float centery, float radius)
|
||||
{
|
||||
List<MobState> filtered = new List<MobState>();
|
||||
foreach(MobState mob in mobs)
|
||||
{
|
||||
if(mob.GetDistance(centerx, centery) <= radius)
|
||||
{
|
||||
filtered.Add(mob);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public List<MobState> FindMobsInRadius(float centerx, float centery, float radius, IMobCondition condition)
|
||||
{
|
||||
return FilterMobs(FindInRadius(Mobs, centerx, centery, radius), condition);
|
||||
}
|
||||
public List<MobState> FindEnemiesInRadius(float centerx, float centery, float radius, IMobCondition condition)
|
||||
{
|
||||
return FilterMobs(FindInRadius(Enemies, centerx, centery, radius), condition);
|
||||
}
|
||||
public List<MobState> FindPlayersInRadius(float centerx, float centery, float radius, IMobCondition condition)
|
||||
{
|
||||
return FilterMobs(FindInRadius(Players, centerx, centery, radius), condition);
|
||||
}
|
||||
#endregion Searching and Filtering
|
||||
}
|
||||
}
|
@ -59,6 +59,7 @@
|
||||
<Compile Include="EngineDataManager.cs" />
|
||||
<Compile Include="GameEngine.cs" />
|
||||
<Compile Include="GameState\GameState.cs" />
|
||||
<Compile Include="GameState\MobManager.cs" />
|
||||
<Compile Include="Map Engine\MapEngine.cs" />
|
||||
<Compile Include="Map Engine\MapGenerator.cs" />
|
||||
<Compile Include="MPQProvider.cs" />
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user