├── UnityPackage ├── SlimeBattleSystem.asmdef ├── Libs │ ├── SlimeBattleSystem.dll │ └── SlimeBattleSystem.dll.meta ├── package.json.meta ├── Libs.meta ├── SlimeBattleSystem.asmdef.meta └── package.json ├── .github ├── FUNDING.yml └── workflows │ ├── test.yml │ └── release.workflow.yml ├── .gitignore ├── Makefile ├── SlimeBattleSystem ├── SlimeBattleSystem.csproj ├── Participant.cs ├── Stats.cs └── BattleSystem.cs ├── SlimeBattleSystem.Tests ├── SlimeBattleSystem.Tests.csproj ├── RandomMock.cs └── BattleSystemTests.cs ├── SlimeBattleSystem.sln.DotSettings.user ├── LICENSE ├── SlimeBattleSystem.sln └── README.md /UnityPackage/SlimeBattleSystem.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SlimeBattleSystemPackage" 3 | } 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | ko_fi: stumpheadgames 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | riderModule.iml 5 | /_ReSharper.Caches/ 6 | .idea/ 7 | **/.DS_Store -------------------------------------------------------------------------------- /UnityPackage/Libs/SlimeBattleSystem.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Joshalexjacobs/SlimeBattleSystem/HEAD/UnityPackage/Libs/SlimeBattleSystem.dll -------------------------------------------------------------------------------- /UnityPackage/package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c41bc4cae83fb4b1793c211fd298b489 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UnityPackage/Libs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d7887752d929244d885e0b30a51383dd 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UnityPackage/SlimeBattleSystem.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 869249e39d32a4358ba860b375b2c175 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | dotnet build SlimeBattleSystem/*.csproj --configuration Debug 3 | 4 | release: 5 | dotnet build SlimeBattleSystem/*.csproj --configuration Release 6 | 7 | test: 8 | dotnet test SlimeBattleSystem.Tests/*.csproj 9 | 10 | clean: 11 | git clean -xdf 12 | -------------------------------------------------------------------------------- /UnityPackage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.joshalexjacobs.slimebattlesystem", 3 | "displayName": "SlimeBattleSystem", 4 | "version": "1.0.0", 5 | "unity": "2021.3", 6 | "description": "An easy to use RPG combat system for Unity that utilizes formulas from the original Dragon Quest games" 7 | } 8 | -------------------------------------------------------------------------------- /SlimeBattleSystem/SlimeBattleSystem.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | netstandard2.0 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ main ] 7 | paths: 8 | - '**.cs' 9 | - '**.csproj' 10 | 11 | env: 12 | DOTNET_VERSION: '6.0' 13 | 14 | jobs: 15 | tests: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - name: Setup .NET Core 22 | uses: actions/setup-dotnet@v1 23 | with: 24 | dotnet-version: ${{ env.DOTNET_VERSION }} 25 | 26 | - name: Install dependencies 27 | run: dotnet restore 28 | 29 | - name: Test 30 | run: dotnet test --no-restore --verbosity normal 31 | -------------------------------------------------------------------------------- /SlimeBattleSystem.Tests/SlimeBattleSystem.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /SlimeBattleSystem.sln.DotSettings.user: -------------------------------------------------------------------------------- 1 | 2 | <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from BattleSystemTests.cs" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> 3 | <ProjectFile>CE4C4A23-930B-40CE-B6FC-246735A0769D/f:BattleSystemTests.cs</ProjectFile> 4 | </SessionState> -------------------------------------------------------------------------------- /UnityPackage/Libs/SlimeBattleSystem.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 21364c42edadf47578e5b12e0f0a0e95 3 | PluginImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | iconMap: {} 7 | executionOrder: {} 8 | defineConstraints: [] 9 | isPreloaded: 0 10 | isOverridable: 1 11 | isExplicitlyReferenced: 0 12 | validateReferences: 1 13 | platformData: 14 | - first: 15 | Any: 16 | second: 17 | enabled: 1 18 | settings: {} 19 | - first: 20 | Editor: Editor 21 | second: 22 | enabled: 0 23 | settings: 24 | DefaultValueInitialized: true 25 | - first: 26 | Windows Store Apps: WindowsStoreApps 27 | second: 28 | enabled: 0 29 | settings: 30 | CPU: AnyCPU 31 | userData: 32 | assetBundleName: 33 | assetBundleVariant: 34 | -------------------------------------------------------------------------------- /SlimeBattleSystem.Tests/RandomMock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SlimeBattleSystem.Tests; 6 | 7 | /// 8 | /// Overrides the Random class. Allows the user to pre-determine which values will be returned from any of the Next() 9 | /// functions. 10 | /// 11 | public class RandomMock : Random { 12 | private readonly List _valueStack; 13 | 14 | public RandomMock(int[] values) { 15 | _valueStack = values.ToList(); 16 | } 17 | 18 | /// 19 | /// Pops the next value from the stack of integers provided in the constructor. 20 | /// 21 | /// int 22 | public override int Next() { 23 | var item = _valueStack[_valueStack.Count - 1]; 24 | 25 | _valueStack.RemoveAt(_valueStack.Count - 1); 26 | 27 | return item; 28 | } 29 | 30 | public override int Next(int maxValue) { 31 | return Next(); 32 | } 33 | 34 | public override int Next(int minValue, int maxValue) { 35 | return Next(); 36 | } 37 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Josh Jacobs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/release.workflow.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ main ] 7 | paths: 8 | - '**.cs' 9 | - '**.csproj' 10 | 11 | env: 12 | DOTNET_VERSION: '6.0' 13 | 14 | jobs: 15 | tests: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | 21 | - name: Setup .NET Core 22 | uses: actions/setup-dotnet@v2 23 | with: 24 | dotnet-version: ${{ env.DOTNET_VERSION }} 25 | 26 | - name: Install dependencies 27 | run: dotnet restore 28 | 29 | - name: Build 30 | run: dotnet build SlimeBattleSystem/*.csproj --configuration Release 31 | 32 | - name: Release 33 | run: | 34 | yes | cp -r SlimeBattleSystem/bin/Release/netstandard2.0/SlimeBattleSystem.dll UnityPackage/Libs/ 35 | 36 | - name: Setup git 37 | run: | 38 | git config user.name "joshalexjacobs-bot" 39 | git config user.email "<>" 40 | 41 | - name: Git commit changes 42 | run: | 43 | git add UnityPackage/Libs/ 44 | git commit -m "Updated SlimeBattleSystem.dll [skip ci]" || exit 0 45 | git push origin main 46 | 47 | - name: Git push subtree 48 | run: | 49 | git push origin `git subtree split --prefix UnityPackage main`:upm --force 50 | -------------------------------------------------------------------------------- /SlimeBattleSystem.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlimeBattleSystem", "SlimeBattleSystem\SlimeBattleSystem.csproj", "{DA2A72E6-17ED-4DF2-9A9B-FB7AD08647C5}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlimeBattleSystem.Tests", "SlimeBattleSystem.Tests\SlimeBattleSystem.Tests.csproj", "{CE4C4A23-930B-40CE-B6FC-246735A0769D}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Release|Any CPU = Release|Any CPU 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {DA2A72E6-17ED-4DF2-9A9B-FB7AD08647C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 14 | {DA2A72E6-17ED-4DF2-9A9B-FB7AD08647C5}.Debug|Any CPU.Build.0 = Debug|Any CPU 15 | {DA2A72E6-17ED-4DF2-9A9B-FB7AD08647C5}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | {DA2A72E6-17ED-4DF2-9A9B-FB7AD08647C5}.Release|Any CPU.Build.0 = Release|Any CPU 17 | {CE4C4A23-930B-40CE-B6FC-246735A0769D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {CE4C4A23-930B-40CE-B6FC-246735A0769D}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {CE4C4A23-930B-40CE-B6FC-246735A0769D}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {CE4C4A23-930B-40CE-B6FC-246735A0769D}.Release|Any CPU.Build.0 = Release|Any CPU 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /SlimeBattleSystem/Participant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SlimeBattleSystem { 4 | [Serializable] 5 | public enum ParticipantType { 6 | Player, 7 | Npc, 8 | Enemy 9 | } 10 | 11 | /// 12 | /// An enemy, NPC, or player character that participates in battle. 13 | /// 14 | [Serializable] 15 | public class Participant { 16 | public int ExperiencePoints; 17 | 18 | public int GoldPoints; 19 | 20 | public string Name; 21 | 22 | public ParticipantType ParticipantType = ParticipantType.Enemy; 23 | 24 | public Stats Stats; 25 | 26 | public int TurnOrder; 27 | 28 | public Participant() { 29 | Stats = new Stats(); 30 | } 31 | 32 | public Participant(string name) { 33 | Name = name; 34 | 35 | Stats = new Stats(); 36 | } 37 | 38 | public Participant(string name, Stats stats) { 39 | Name = name; 40 | 41 | Stats = stats; 42 | } 43 | 44 | /// 45 | /// Recalculates participants attack power. Should occur after a new weapon is equipped. 46 | /// 47 | /// The attack power stat of the newly equipped weapon. 48 | /// void 49 | public void CalculateAttackPower(int weaponAttackPower) { 50 | Stats.AttackPower = Stats.Strength + weaponAttackPower; 51 | } 52 | 53 | /// 54 | /// Recalculates participants defense power. Should occur after a new piece of armor is equipped. 55 | /// 56 | /// The defense power stat of the newly equipped piece of armor. 57 | /// void 58 | public void CalculateDefensePower(int armorDefensePower) { 59 | Stats.DefensePower = Stats.Agility + armorDefensePower; 60 | } 61 | 62 | /// 63 | /// Inflicts allotted damage to the participant. Will floor hit points to 0. 64 | /// 65 | /// Amount of damage inflicted. 66 | /// void 67 | public void InflictDamage(int damage) { 68 | Stats.HitPoints -= damage; 69 | 70 | if (Stats.HitPoints < 0) Stats.HitPoints = 0; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /SlimeBattleSystem/Stats.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SlimeBattleSystem { 4 | /// 5 | /// Stats used to track HP, MP, Strength, Agility, Attack Power, Defense Power, and chance to Dodge (X/64). 6 | /// 7 | [Serializable] 8 | public class Stats { 9 | // player's unmodified strength 10 | public int Strength; 11 | 12 | // player's unmodified agility 13 | public int Agility; 14 | 15 | // current HP 16 | public int HitPoints; 17 | 18 | // max HP 19 | public int MaxHitPoints; 20 | 21 | // current MP 22 | public int MagicPoints; 23 | 24 | // max MP 25 | public int MaxMagicPoints; 26 | 27 | // weapons/items that contribute to attack 28 | public int AttackPower; 29 | 30 | // armor/items that contribute to defense 31 | public int DefensePower; 32 | 33 | // chance that a participant can dodge an attack out of 64 34 | public int Dodge; 35 | 36 | // current level 37 | public int Level; 38 | 39 | public Stats() { 40 | HitPoints = 1; 41 | 42 | MaxHitPoints = 1; 43 | 44 | MagicPoints = 1; 45 | 46 | MaxMagicPoints = 1; 47 | 48 | Strength = 1; 49 | 50 | Agility = 1; 51 | 52 | AttackPower = 1; 53 | 54 | DefensePower = 1; 55 | 56 | Dodge = 1; 57 | 58 | Level = 1; 59 | } 60 | 61 | public Stats(Stats stats) { 62 | HitPoints = stats.HitPoints; 63 | 64 | MaxHitPoints = stats.MaxHitPoints; 65 | 66 | MagicPoints = stats.MagicPoints; 67 | 68 | MaxMagicPoints = stats.MaxMagicPoints; 69 | 70 | Strength = stats.Strength; 71 | 72 | Agility = stats.Agility; 73 | 74 | AttackPower = stats.AttackPower; 75 | 76 | DefensePower = stats.DefensePower; 77 | 78 | Dodge = stats.Dodge; 79 | 80 | Level = stats.Level; 81 | } 82 | 83 | public Stats(int hitPoints, int maxHitPoints, int magicPoints, int maxMagicPoints, int strength, int agility, 84 | int attackPower, int defensePower, int dodge, int level = 1) { 85 | HitPoints = hitPoints; 86 | 87 | MaxHitPoints = maxHitPoints; 88 | 89 | MagicPoints = magicPoints; 90 | 91 | MaxMagicPoints = maxMagicPoints; 92 | 93 | Strength = strength; 94 | 95 | Agility = agility; 96 | 97 | AttackPower = attackPower; 98 | 99 | DefensePower = defensePower; 100 | 101 | Dodge = dodge; 102 | 103 | Level = level; 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /SlimeBattleSystem.Tests/BattleSystemTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace SlimeBattleSystem.Tests; 5 | 6 | public class BattleSystemTests { 7 | [Test] 8 | public void SetRandomizationSeed() { 9 | var seed = "New Seed"; 10 | 11 | BattleSystem.SetRandomizationSeed(seed); 12 | 13 | Assert.AreEqual(seed.GetHashCode(), BattleSystem.Seed); 14 | } 15 | 16 | [Test] 17 | public void CalculateTurnOrder() { 18 | var calculatedTurnOrder = BattleSystem.CalculateTurnOrder(9, new RandomMock(new[] { 150 })); 19 | 20 | Assert.AreEqual(5, calculatedTurnOrder); 21 | } 22 | 23 | [Test] 24 | public void DetermineTurnOrder() { 25 | var participantA = new Participant("Participant A"); 26 | 27 | participantA.Stats.Agility = 7; 28 | 29 | var participantB = new Participant("Participant B"); 30 | 31 | participantB.Stats.Agility = 11; 32 | 33 | var participantC = new Participant("Participant C"); 34 | 35 | participantC.Stats.Agility = 4; 36 | 37 | var participants = new List { participantA, participantB, participantC }; 38 | 39 | var orderedParticipants = BattleSystem.DetermineTurnOrder(participants, new RandomMock(new[] { 25, 150, 90 })); 40 | 41 | Assert.AreEqual(3, orderedParticipants.Count); 42 | 43 | Assert.AreEqual(participantB.Name, orderedParticipants[0].Name); 44 | 45 | Assert.AreEqual(participantA.Name, orderedParticipants[1].Name); 46 | 47 | Assert.AreEqual(participantC.Name, orderedParticipants[2].Name); 48 | } 49 | 50 | [Test] 51 | public void DetermineEnemyTarget() { 52 | var participantA = new Participant("Participant A"); 53 | 54 | participantA.Stats.Agility = 7; 55 | 56 | var participantB = new Participant("Participant B"); 57 | 58 | participantB.Stats.Agility = 11; 59 | 60 | var participants = new List { participantA, participantB }; 61 | 62 | var target = BattleSystem.DetermineEnemyTarget(participants, new RandomMock(new[] { 1 })); 63 | 64 | Assert.AreEqual(participantB, target); 65 | } 66 | 67 | [Test] 68 | public void DetermineParticipantFleeing() { 69 | var participantA = new Participant("Participant A", 70 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 71 | 72 | var participantB = new Participant("Participant B", 73 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 74 | 75 | Assert.IsTrue( 76 | BattleSystem.DetermineParticipantFleeing(participantA, participantB, new RandomMock(new[] { 0, 3 }))); 77 | 78 | Assert.IsFalse( 79 | BattleSystem.DetermineParticipantFleeing(participantA, participantB, new RandomMock(new[] { 255, 1 }))); 80 | } 81 | 82 | [Test] 83 | public void GetPlayerParticipants() { 84 | var participantA = new Participant("Participant A", 85 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 86 | 87 | var participantB = new Participant("Participant B", 88 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 89 | 90 | participantB.ParticipantType = ParticipantType.Player; 91 | 92 | var participantC = new Participant("Participant C", 93 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 94 | 95 | Assert.AreEqual(participantB, 96 | BattleSystem.GetPlayerParticipants(new List { participantA, participantB, participantC })[0]); 97 | } 98 | 99 | [Test] 100 | public void GetEnemyParticipants() { 101 | var participantA = new Participant("Participant A", 102 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 103 | 104 | var participantB = new Participant("Participant B", 105 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 106 | 107 | participantB.ParticipantType = ParticipantType.Player; 108 | 109 | var participantC = new Participant("Participant C", 110 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 111 | 112 | var participants = new List { participantA, participantB, participantC }; 113 | 114 | var enemyParticipants = BattleSystem.GetEnemyParticipants(participants); 115 | 116 | Assert.AreEqual(participantA, enemyParticipants[0]); 117 | 118 | Assert.AreEqual(participantC, enemyParticipants[1]); 119 | } 120 | 121 | [Test] 122 | public void GetParticipantWithHighestAgility() { 123 | var participantA = new Participant("Participant A", 124 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 125 | 126 | var participantB = new Participant("Participant B", 127 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 128 | 129 | participantB.ParticipantType = ParticipantType.Player; 130 | 131 | var participantC = new Participant("Participant C", 132 | new Stats(25, 25, 7, 7, 7, 7, 6, 5, 5)); 133 | 134 | Assert.AreEqual(participantC, 135 | BattleSystem.GetParticipantWithHighestAgility(new List 136 | { participantA, participantB, participantC })); 137 | } 138 | 139 | [Test] 140 | public void DetermineRemainingParticipants() { 141 | var participantA = new Participant("Participant A", 142 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 143 | 144 | var participantB = new Participant("Participant B", 145 | new Stats(0, 0, 7, 7, 7, 5, 6, 5, 5)); 146 | 147 | var participantC = new Participant("Participant C", 148 | new Stats(25, 25, 7, 7, 7, 7, 6, 5, 5)); 149 | 150 | Assert.AreEqual(2, 151 | BattleSystem.GetNumberOfRemainingParticipants(new List 152 | { participantA, participantB, participantC })); 153 | } 154 | 155 | public class DetermineAttackDamage { 156 | [Test] 157 | public void CriticalHit() { 158 | var participantA = new Participant("Participant A", 159 | new Stats(22, 22, 12, 12, 8, 6, 8, 8, 2)); 160 | 161 | var participantB = new Participant("Participant B", 162 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 163 | 164 | var results = BattleSystem.DetermineAttackDamage( 165 | participantA, 166 | participantB, 167 | new RandomMock(new[] { 1, 1, 64 })); 168 | 169 | // Critical hit 170 | Assert.AreEqual(8, results.Damage); 171 | } 172 | 173 | [Test] 174 | public void Dodge() { 175 | var participantA = new Participant("Participant A", 176 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 177 | 178 | var participantB = new Participant("Participant B", 179 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 180 | 181 | var results = BattleSystem.DetermineAttackDamage( 182 | participantA, 183 | participantB, 184 | new RandomMock(new[] { participantB.Stats.Dodge })); 185 | 186 | // Dodge 187 | Assert.AreEqual(0, results.Damage); 188 | } 189 | 190 | [Test] 191 | public void RegularHit() { 192 | var participantA = new Participant("Participant A", 193 | new Stats(22, 22, 12, 12, 4, 6, 4, 8, 2)); 194 | 195 | var participantB = new Participant("Participant B", 196 | new Stats(25, 25, 7, 7, 7, 5, 6, 5, 5)); 197 | 198 | var results = BattleSystem.DetermineAttackDamage( 199 | participantA, 200 | participantB, 201 | new RandomMock(new[] { 2, 2, 64 })); 202 | 203 | // Regular hit 204 | Assert.AreEqual(1, results.Damage); 205 | } 206 | } 207 | 208 | public class DetermineEndOfBattle { 209 | [Test] 210 | public void ExperiencePoints() { 211 | var participantA = new Participant("Participant A", 212 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 213 | 214 | participantA.ExperiencePoints = 12; 215 | 216 | Assert.AreEqual(participantA.ExperiencePoints, 217 | BattleSystem.DetermineExperiencePoints(new List { participantA })); 218 | } 219 | 220 | [Test] 221 | public void GoldPoints() { 222 | var participantA = new Participant("Participant A", 223 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 224 | 225 | participantA.GoldPoints = 12; 226 | 227 | Assert.AreEqual(10, 228 | BattleSystem.DetermineGoldPoints( 229 | new List { participantA }, 230 | new RandomMock(new[] { 32 }) 231 | ) 232 | ); 233 | } 234 | 235 | [Test] 236 | public void ItemsDropped() { 237 | var participantA = new Participant("Participant A", 238 | new Stats(22, 22, 12, 12, 8, 6, 4, 8, 2)); 239 | 240 | var potion = new object(); 241 | 242 | var droppableItems = new Dictionary(); 243 | 244 | droppableItems.Add(potion, 50); 245 | 246 | Assert.AreEqual(1, 247 | BattleSystem.DetermineItemsDropped( 248 | droppableItems, 249 | new RandomMock(new[] { 50 })).Count 250 | ); 251 | 252 | Assert.AreEqual(potion, 253 | BattleSystem.DetermineItemsDropped( 254 | droppableItems, 255 | new RandomMock(new[] { 50 }))[0] 256 | ); 257 | } 258 | } 259 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Slime Battle System 🐉⚔️🛡️ 2 | A lightweight RPG combat system for Unity that emulates the battle formulas from the original Dragon Quest. 3 | 4 | ## Installation (Unity) 5 | 6 | 1. Download the latest SlimeBattleSystem.dll release found [here](https://github.com/Joshalexjacobs/SlimeBattleSystem/releases) 7 | 2. Drop it into your assets folder 8 | 3. You should now be able to reference the SlimeBattleSystem namespace within your own scripts 9 | 10 | 11 | # API 12 | 13 | *Don't like reading documentation? Take a look at the [sample unity project!](https://github.com/Joshalexjacobs/SlimeBattleSystemSample)* 14 | 15 | ## Battle System 16 | A static class that handles all combat logic. 17 | 18 |
19 | 20 | ### SetRandomizationSeed 21 | Sets the randomization seed used by most formulas. 22 | 23 | Params `string` 24 | 25 | ```csharp 26 | string seed = "New Seed"; 27 | 28 | BattleSystem.SetRandomizationSeed(seed); 29 | ``` 30 | 31 |
32 | 33 | ### CalculateTurnOrder 34 | Calculates a participant's turn order with a random range using their Agility stat. 35 | 36 | Params `int` 37 | 38 | Returns `int` 39 | ```csharp 40 | foreach (var participant in participants) { 41 | participant.TurnOrder = BattleSystem.CalculateTurnOrder(participant.Stats.Agility); 42 | } 43 | ``` 44 | 45 |
46 | 47 | ### DetermineTurnOrder 48 | Determines the order a group of Participants will attack in. 49 | 50 | Params `List` 51 | 52 | Returns `List`, sorted by turn order 53 | ```csharp 54 | List orderedParticipants = BattleSystem.DetermineTurnOrder(participants); 55 | ``` 56 | 57 |
58 | 59 | ### DetermineEnemyTarget 60 | Determines the player participant an enemy will target during their turn. 61 | 62 | Params `List` 63 | 64 | Returns `Participant` 65 | ```csharp 66 | Participant target = BattleSystem.DetermineEnemyTarget(playerParticipants); 67 | ``` 68 | 69 |
70 | 71 | ### DetermineAttackDamage 72 | Determines whether the attacker hits the target and how much damage is dealt. 73 | 74 | Params `Participant (attacker)`, `Participant (target)` 75 | 76 | Returns `AttackResult`, contains Type (`AttackType` enum) and Damage (`int`). 77 | ```csharp 78 | var results = BattleSystem.DetermineAttackDamage(currentParticipant, target); 79 | 80 | battleLog.UpdateLog($"{currentParticipant.Name} attacks!.\n"); 81 | 82 | switch (results.Type) 83 | { 84 | case AttackResults.AttackType.Hit: 85 | battleLog.UpdateLog($"{target.Name}'s Hit Points have been reduced by {results.Damage}.\n"); 86 | 87 | break; 88 | case AttackResults.AttackType.CriticalHit: 89 | battleLog.UpdateLog($"Critical hit!!!\n"); 90 | 91 | battleLog.UpdateLog($"{target.Name}'s Hit Points have been reduced by {results.Damage}.\n"); 92 | 93 | break; 94 | case AttackResults.AttackType.Missed: 95 | battleLog.UpdateLog($"Missed! {target.Name} dodged the attack.\n"); 96 | 97 | break; 98 | } 99 | 100 | if (results.Damage > 0) { 101 | target.InflictDamage(results.Damage); 102 | } 103 | ``` 104 | 105 |
106 | 107 | ### DetermineParticipantFleeing 108 | Determines whether the participant is able to flee or not. 109 | 110 | Will use the highest Agility stat of the participants in the runningFrom list. 111 | 112 | Params `Participant (runner)`, `List (runningFrom)` 113 | 114 | Returns `bool`. 115 | ```csharp 116 | bool result = BattleSystem.DetermineParticipantFleeing(currentParticipant, enemy); 117 | 118 | if (!result) 119 | { 120 | battleLog.UpdateLog("But the escape route was cut off!\n"); 121 | } 122 | else 123 | { 124 | battleLog.UpdateLog($"{currentParticipant.Name} escaped!\n"); 125 | 126 | battleState = BattleState.Ended; 127 | } 128 | ``` 129 | 130 |
131 | 132 | ### GetPlayerParticipants 133 | Gets a list of Player and NPC participants. 134 | 135 | Params `List` 136 | 137 | Returns `List` 138 | ```csharp 139 | var playerParticipants = BattleSystem.GetPlayerParticipants(participants); 140 | ``` 141 | 142 |
143 | 144 | ### GetEnemyParticipants 145 | Gets a list of Enemy participants. 146 | 147 | Params `List` 148 | 149 | Returns `List` 150 | ```csharp 151 | var enemyParticipants = BattleSystem.GetEnemyParticipants(participants); 152 | ``` 153 | 154 |
155 | 156 | ### GetParticipantWithHighestAgility 157 | Gets the participant with the highest agility. 158 | 159 | Params `List` 160 | 161 | Returns `Participant`. 162 | ```csharp 163 | var participant = GetParticipantWithHighestAgility(participants); 164 | ``` 165 | 166 |
167 | 168 | ### GetNumberOfRemainingParticipants 169 | Gets the number of remaining participants with hit points greater than 0. 170 | 171 | Params `List` 172 | 173 | Returns `int`. 174 | ```csharp 175 | var enemiesRemaining = BattleSystem.GetNumberOfRemainingParticipants(enemyParticipants); 176 | ``` 177 | 178 |
179 | 180 | ### IsBattleOver 181 | Gets whether the battle has ended or not. 182 | 183 | Params `List` 184 | 185 | Returns `bool`. 186 | ```csharp 187 | if (BattleSystem.IsBattleOver(participants)) 188 | { 189 | battleState = BattleState.Ended; 190 | 191 | soundManager.StopSound(soundManager.battleMusic); 192 | } 193 | ``` 194 | 195 |
196 | 197 | ### DetermineExperiencePoints 198 | Gets experience points gained from defeated participants. 199 | 200 | Params `List` 201 | 202 | Returns `int`. 203 | ```csharp 204 | var experiencePoints = BattleSystem.DetermineExperiencePoints(enemyParticipants); 205 | 206 | battleLog.UpdateLog($"Thy Experience increases by {experiencePoints}.\n"); 207 | 208 | player.ExperiencePoints += experiencePoints; 209 | ``` 210 | 211 |
212 | 213 | ### DetermineGoldPoints 214 | Gets gold points gained from defeated participants. 215 | 216 | Params `List` 217 | 218 | Returns `int`. 219 | ```csharp 220 | var gold = BattleSystem.DetermineGoldPoints(enemyParticipants); 221 | 222 | battleLog.UpdateLog($"Thy GOLD increases by {goldPoints}.\n"); 223 | 224 | player.GoldPoints += gold; 225 | ``` 226 | 227 |
228 | 229 | ### DetermineItemsDropped 230 | *Note: I couldn't find a lot on item drop rates so I've taken some liberties here to make this as flexible as possible.* 231 | 232 | Gets any items gained from defeated participants. 233 | 234 | Params `Dictionary`, `T` being the class or item object and int being the chance to drop out of 100 235 | 236 | Returns `List` 237 | ```csharp 238 | Dictionary droppableItems = new Dictionary(); 239 | 240 | foreach (var enemyCombatantDroppableItem in enemyCombatant.droppableItems) { 241 | droppableItems.Add(enemyCombatantDroppableItem.item, enemyCombatantDroppableItem.chanceToDrop); 242 | } 243 | 244 | var itemsDropped = BattleSystem.DetermineItemsDropped(droppableItems); 245 | 246 | foreach (Item item in itemsDropped) { 247 | battleLog.UpdateLog($"{enemy.Name} dropped a {item.name}!\n"); 248 | 249 | playerCombatant.items.Add(item); 250 | } 251 | ``` 252 | 253 |
254 | 255 | ## AttackResult 256 | A simple class that is returned after a `Participant` attacks. 257 | 258 | ```csharp 259 | public class AttackResult { 260 | public enum AttackType { 261 | Hit, 262 | CriticalHit, 263 | Missed 264 | } 265 | 266 | public AttackType Type; 267 | 268 | public int Damage; 269 | 270 | public AttackResult(AttackType type, int damage) { 271 | Type = type; 272 | Damage = damage; 273 | } 274 | } 275 | ``` 276 | 277 | ## Participant 278 | An enemy, NPC, or player character that participates in battle. 279 | 280 |
281 | 282 | ### CalculateAttackPower 283 | Calculates participants attack power. Should be called after a new weapon is equipped. 284 | 285 | Params `int` 286 | 287 | Returns `void` 288 | ```csharp 289 | playerCombatant.weaponSlot = ironAxe; 290 | 291 | playerParticipant.CalculateAttackPower(ironAxe.attackPower); 292 | ``` 293 | 294 |
295 | 296 | ### CalculateDefensePower 297 | Calculates participants defense power. Should be called after a new piece of armor is equipped. 298 | 299 | Params `int` 300 | 301 | Returns `void` 302 | ```csharp 303 | playerCombatant.shieldSlot = leatherShield; 304 | 305 | playerParticipant.CalculateDefensePower(leatherShield.defensePower); 306 | ``` 307 | 308 |
309 | 310 | ### InflictDamage 311 | Inflicts allotted damage to the participant. Will floor hit points to 0. 312 | 313 | Params `int` 314 | 315 | Returns `void` 316 | ```csharp 317 | target.InflictDamage(results.damage); 318 | ``` 319 | 320 | # Confused? Check out the sample project! 321 | 322 | https://github.com/Joshalexjacobs/SlimeBattleSystemSample 323 | 324 | ![Unity Sample Gif](https://i.imgur.com/S3mjjGf.gif) 325 | 326 | 327 | *Note: This package was created with the first Dragon Quest game in mind. Most of the logic being handled here is intended for 1v1 battles, but I've done my best to try and support battles bigger than 2 combatants.* 328 | 329 | ---- 330 | 331 | # Formulas used 332 | 333 | - Battle Turn Order 334 | - Attacking 335 | - Dodging 336 | - Critical Hits 337 | - Fleeing 338 | - Awarding Experience Points 339 | - Awarding Gold Points 340 | - Determining items dropped 341 | 342 | ## What's missing? 343 | 344 | ### Spells, Items, and Status Ailments 345 | I originally planned on baking these into the package, but after some testing decided that this will be much more flexible if handled by the user. If you're looking for an easy way to do this, check out the [sample repo.](https://github.com/Joshalexjacobs/SlimeBattleSystemSample) 346 | 347 | ### Leveling and name based stat growth 348 | In the original Dragon Quest, your character's name determines how your stats will grow upon leveling. After reviewing the logic I figured this was a bit too complex at this time. For now I'm leaving all leveling and stat growth to the user, but may add this as an optional function in the future. 349 | 350 | *For those interested: akiraslime posted a great Dragon Warrior stat growth FAQ [here.](https://gamefaqs.gamespot.com/nes/563408-dragon-warrior/faqs/18342)* 351 | 352 | ### 75% - 100% Enemy Health Formula 353 | In the base game, enemy health is randomly determined between 75% - 100% of their max health (eg. a Magician has a max health pool of 13 hit points. When encountered, a Magician would have 10 - 13 hit points). For simplicity sake, I've decided to omit this logic for now as it could be easily added before beginning the encounter if desired. 354 | 355 | ### Experience/Gold Multiplier 356 | I did discover a formula for increasing experience points and gold points based on the number of characters in the player's party. For now I've omitted any multiplier due to the fact that this system is entirely based on the original Dragon Quest (in which there is only 1 member in the party). However, I would love to add some sort of configurable profiles in the future allowing for minor changes like this one. 357 | 358 | # Sources and additional reading material 359 | 360 | [Game Developer: How Final Fantasy and Dragon Quest handle combat math](https://www.gamedeveloper.com/design/number-punchers-how-i-final-fantasy-i-and-i-dragon-quest-i-handle-combat-math) 361 | 362 | [Formula Guide (NES) by Ryan8bit](https://gamefaqs.gamespot.com/nes/563408-dragon-warrior/faqs/61640) 363 | 364 | [Names/Stats/Levels FAQ (NES) by akiraslime](https://gamefaqs.gamespot.com/nes/563408-dragon-warrior/faqs/18342) 365 | -------------------------------------------------------------------------------- /SlimeBattleSystem/BattleSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SlimeBattleSystem { 6 | // reading material: 7 | // https://dragonquestcosmos.fandom.com/wiki/Formulas#Physical_Attack 8 | // https://www.gamedeveloper.com/design/number-punchers-how-i-final-fantasy-i-and-i-dragon-quest-i-handle-combat-math 9 | 10 | /// 11 | /// An easy to use RPG combat system for Unity that utilizes formulas from the original Dragon Quest. 12 | /// 13 | public static class BattleSystem { 14 | public static Random Random; 15 | 16 | public static int Seed; 17 | 18 | static BattleSystem() { 19 | Random = new Random(); 20 | } 21 | 22 | /// 23 | /// Sets the randomization seed used by most formulas. 24 | /// 25 | /// String used to seed randomization. 26 | /// void 27 | public static void SetRandomizationSeed(string seed) { 28 | Seed = seed.GetHashCode(); 29 | 30 | Random = new Random(Seed); 31 | } 32 | 33 | public static int CalculateTurnOrder(int agility) { 34 | return CalculateTurnOrder(agility, Random); 35 | } 36 | 37 | /// 38 | /// Calculates a participant's turn order with a random range using their Agility stat. 39 | /// 40 | /// Agility stat. 41 | /// Random class to use in generating turn order. 42 | /// int 43 | public static int CalculateTurnOrder(int agility, Random random) { 44 | // agility - ([0 - 255] * (agility - agility / 4)) / 256 45 | 46 | return agility - random.Next(0, 255) * (agility - agility / 4) / 256; 47 | } 48 | 49 | public static List DetermineTurnOrder(List participants) { 50 | return DetermineTurnOrder(participants, Random); 51 | } 52 | 53 | /// 54 | /// Determines the order a group of Participants will attack in. 55 | /// 56 | /// A list of battle participants. 57 | /// Random class to use in generating turn order. 58 | /// List 59 | public static List DetermineTurnOrder(List participants, Random random) { 60 | foreach (var participant in participants) 61 | participant.TurnOrder = CalculateTurnOrder(participant.Stats.Agility, random); 62 | 63 | participants.Sort((participantA, participantB) => participantB.TurnOrder.CompareTo(participantA.TurnOrder)); 64 | 65 | return participants; 66 | } 67 | 68 | public static Participant DetermineEnemyTarget(List playerParticipants) { 69 | return DetermineEnemyTarget(playerParticipants, Random); 70 | } 71 | 72 | /// 73 | /// Determines the player participant and enemy will target. 74 | /// 75 | /// A list of player participants. 76 | /// Random class to use in generating turn order. 77 | /// Participant 78 | public static Participant DetermineEnemyTarget(List playerParticipants, Random random) { 79 | // currently each player character has an equal chance to be hit by the enemy 80 | return playerParticipants[random.Next(0, playerParticipants.Count)]; 81 | } 82 | 83 | public static AttackResult DetermineAttackDamage(Participant attacker, Participant defender) { 84 | return DetermineAttackDamage(attacker, defender, Random); 85 | } 86 | 87 | /// 88 | /// Determines whether the attacker hits the target and how much damage is dealt. 89 | /// 90 | /// The attacking participant. 91 | /// The defending participant. 92 | /// Random class to use in generating turn order. 93 | /// AttackResults 94 | public static AttackResult DetermineAttackDamage(Participant attacker, Participant defender, Random random) { 95 | if (random.Next(defender.Stats.Dodge, 64) <= defender.Stats.Dodge) 96 | // attack was dodged 97 | 98 | return new AttackResult(AttackResult.AttackType.Missed, 0); 99 | 100 | if (random.Next(1, 32) == 1) { 101 | // critical hit 102 | 103 | var criticalHitAttackStrength = attacker.Stats.AttackPower; 104 | 105 | var criticalHitDamage = criticalHitAttackStrength / random.Next(1, 2); 106 | 107 | return new AttackResult(AttackResult.AttackType.CriticalHit, criticalHitDamage); 108 | } 109 | 110 | // perform regular attack 111 | 112 | var attackStrength = attacker.Stats.AttackPower; 113 | 114 | var targetDefense = defender.Stats.DefensePower; 115 | 116 | var damage = (attackStrength - targetDefense / 2) / random.Next(2, 4); 117 | 118 | return new AttackResult(AttackResult.AttackType.Hit, damage); 119 | } 120 | 121 | public static bool DetermineParticipantFleeing(Participant participant, Participant runningFrom) { 122 | return DetermineParticipantFleeing(participant, runningFrom, Random); 123 | } 124 | 125 | public static bool DetermineParticipantFleeing(Participant participant, List runningFrom) { 126 | return DetermineParticipantFleeing(participant, GetParticipantWithHighestAgility(runningFrom), Random); 127 | } 128 | 129 | public static bool DetermineParticipantFleeing(Participant participant, List runningFrom, 130 | Random random) { 131 | return DetermineParticipantFleeing(participant, GetParticipantWithHighestAgility(runningFrom), random); 132 | } 133 | 134 | /// 135 | /// Determines whether the participant is able to flee or not. 136 | /// 137 | /// The fleeing participant. 138 | /// The participant fleeing from. 139 | /// Random class to use in generating turn order. 140 | /// bool 141 | public static bool DetermineParticipantFleeing(Participant participant, Participant runningFrom, Random random) { 142 | // fleeing formula 143 | // this currently is missing the RunFactor which monsters have 144 | // the stronger the monster the higher the RunFactor: 145 | // https://dragonquestcosmos.fandom.com/wiki/Formulas#Fleeing_Battle 146 | // heroAgility * rand(0, 255) >= toughestMonsterAgility * rand(0, 255) * monsterRunFactor 147 | 148 | return participant.Stats.Agility * random.Next(0, 255) >= 149 | runningFrom.Stats.Agility * random.Next(0, 255); 150 | } 151 | 152 | /// 153 | /// Gets a list of player participants. 154 | /// 155 | /// A list of participants. 156 | /// List 157 | public static List GetPlayerParticipants(List participants) { 158 | return participants.Where(participant => 159 | participant.ParticipantType == ParticipantType.Player || 160 | participant.ParticipantType == ParticipantType.Npc).ToList(); 161 | } 162 | 163 | /// 164 | /// Gets a list of enemy participants. 165 | /// 166 | /// A list of participants. 167 | /// List 168 | public static List GetEnemyParticipants(List participants) { 169 | return participants.Where(participant => 170 | participant.ParticipantType == ParticipantType.Enemy).ToList(); 171 | } 172 | 173 | /// 174 | /// Returns the participant with the highest agility. 175 | /// 176 | /// A list of participants. 177 | /// Participant 178 | public static Participant GetParticipantWithHighestAgility(List participants) { 179 | return participants.OrderByDescending(participant => participant.Stats.Agility).ToList()[0]; 180 | } 181 | 182 | /// 183 | /// Gets the number of remaining participants with hit points greater than 0. 184 | /// 185 | /// A list of participants. 186 | /// int 187 | public static int GetNumberOfRemainingParticipants(List participants) { 188 | return participants.Count(participant => participant.Stats.HitPoints > 0); 189 | } 190 | 191 | /// 192 | /// Returns whether the battle is over. 193 | /// 194 | /// A list of participants. 195 | /// bool 196 | public static bool IsBattleOver(List participants) { 197 | var enemyParticipants = GetEnemyParticipants(participants); 198 | 199 | var playerParticipants = GetPlayerParticipants(participants); 200 | 201 | return GetNumberOfRemainingParticipants(enemyParticipants) == 0 202 | || GetNumberOfRemainingParticipants(playerParticipants) == 0; 203 | } 204 | 205 | /// 206 | /// Returns experience points gained from defeated participants. 207 | /// 208 | /// A participant. 209 | /// int 210 | public static int DetermineExperiencePoints(Participant defeatedParticipant) { 211 | return DetermineExperiencePoints(new List { defeatedParticipant }); 212 | } 213 | 214 | /// 215 | /// Returns experience points gained from defeated participants. 216 | /// 217 | /// A list of participants. 218 | /// int 219 | public static int DetermineExperiencePoints(List defeatedParticipants) { 220 | var xpSum = 0; 221 | 222 | foreach (var defeatedParticipant in defeatedParticipants) xpSum += defeatedParticipant.ExperiencePoints; 223 | 224 | return xpSum; 225 | } 226 | 227 | public static int DetermineGoldPoints(Participant defeatedParticipant) { 228 | return DetermineGoldPoints(new List { defeatedParticipant }, Random); 229 | } 230 | 231 | public static int DetermineGoldPoints(List defeatedParticipants) { 232 | return DetermineGoldPoints(defeatedParticipants, Random); 233 | } 234 | 235 | public static int DetermineGoldPoints(Participant defeatedParticipant, Random random) { 236 | return DetermineGoldPoints(new List { defeatedParticipant }, random); 237 | } 238 | 239 | /// 240 | /// Returns gold points gained from defeated participants. 241 | /// 242 | /// A list of participants. 243 | /// Random class to use in generating turn order. 244 | /// int 245 | public static int DetermineGoldPoints(List defeatedParticipants, Random random) { 246 | // (GP * RAND(0, 63) + 192) / 256 247 | 248 | var gpSum = 0; 249 | 250 | foreach (var defeatedParticipant in defeatedParticipants) 251 | gpSum += defeatedParticipant.GoldPoints * (random.Next(0, 63) + 192) / 256; 252 | 253 | return gpSum; 254 | } 255 | 256 | public static List DetermineItemsDropped(Dictionary droppableItems) { 257 | return DetermineItemsDropped(droppableItems, Random); 258 | } 259 | 260 | /// 261 | /// Returns items gained from defeated participants. 262 | /// 263 | /// A Dictionary of items and their chance to drop out of 100. 264 | /// Random class to use in generating turn order. 265 | /// List 266 | public static List DetermineItemsDropped(Dictionary droppableItems, Random random) { 267 | var itemsDropped = new List(); 268 | 269 | foreach (var droppableItem in droppableItems) 270 | if (random.Next(droppableItem.Value, 100) <= droppableItem.Value) 271 | itemsDropped.Add(droppableItem.Key); 272 | 273 | return itemsDropped; 274 | } 275 | } 276 | 277 | /// 278 | /// The results of an attack containing the type and damage amount. 279 | /// 280 | [Serializable] 281 | public class AttackResult { 282 | public enum AttackType { 283 | Hit, 284 | CriticalHit, 285 | Missed 286 | } 287 | 288 | public AttackType Type; 289 | 290 | public int Damage; 291 | 292 | public AttackResult(AttackType type, int damage) { 293 | Type = type; 294 | Damage = damage; 295 | } 296 | } 297 | } --------------------------------------------------------------------------------