SlimeBattleSystem
SlimeBattleSystem copied to clipboard
A lightweight RPG combat system for Unity that emulates the battle formulas from the original Dragon Quest.
Slime Battle System 🐉⚔️🛡️
A lightweight RPG combat system for Unity that emulates the battle formulas from the original Dragon Quest.
Installation (Unity)
- Download the latest SlimeBattleSystem.dll release found here
- Drop it into your assets folder
- You should now be able to reference the SlimeBattleSystem namespace within your own scripts
API
Don't like reading documentation? Take a look at the sample unity project!
Battle System
A static class that handles all combat logic.
SetRandomizationSeed
Sets the randomization seed used by most formulas.
Params string
string seed = "New Seed";
BattleSystem.SetRandomizationSeed(seed);
CalculateTurnOrder
Calculates a participant's turn order with a random range using their Agility stat.
Params int
Returns int
foreach (var participant in participants) {
participant.TurnOrder = BattleSystem.CalculateTurnOrder(participant.Stats.Agility);
}
DetermineTurnOrder
Determines the order a group of Participants will attack in.
Params List<Participant>
Returns List<Participant>
, sorted by turn order
List<Participant> orderedParticipants = BattleSystem.DetermineTurnOrder(participants);
DetermineEnemyTarget
Determines the player participant an enemy will target during their turn.
Params List<Participant>
Returns Participant
Participant target = BattleSystem.DetermineEnemyTarget(playerParticipants);
DetermineAttackDamage
Determines whether the attacker hits the target and how much damage is dealt.
Params Participant (attacker)
, Participant (target)
Returns AttackResult
, contains Type (AttackType
enum) and Damage (int
).
var results = BattleSystem.DetermineAttackDamage(currentParticipant, target);
battleLog.UpdateLog($"{currentParticipant.Name} attacks!.\n");
switch (results.Type)
{
case AttackResults.AttackType.Hit:
battleLog.UpdateLog($"{target.Name}'s Hit Points have been reduced by {results.Damage}.\n");
break;
case AttackResults.AttackType.CriticalHit:
battleLog.UpdateLog($"Critical hit!!!\n");
battleLog.UpdateLog($"{target.Name}'s Hit Points have been reduced by {results.Damage}.\n");
break;
case AttackResults.AttackType.Missed:
battleLog.UpdateLog($"Missed! {target.Name} dodged the attack.\n");
break;
}
if (results.Damage > 0) {
target.InflictDamage(results.Damage);
}
DetermineParticipantFleeing
Determines whether the participant is able to flee or not.
Will use the highest Agility stat of the participants in the runningFrom list.
Params Participant (runner)
, List<Participant> (runningFrom)
Returns bool
.
bool result = BattleSystem.DetermineParticipantFleeing(currentParticipant, enemy);
if (!result)
{
battleLog.UpdateLog("But the escape route was cut off!\n");
}
else
{
battleLog.UpdateLog($"{currentParticipant.Name} escaped!\n");
battleState = BattleState.Ended;
}
GetPlayerParticipants
Gets a list of Player and NPC participants.
Params List<Participant>
Returns List<Participant>
var playerParticipants = BattleSystem.GetPlayerParticipants(participants);
GetEnemyParticipants
Gets a list of Enemy participants.
Params List<Participant>
Returns List<Participant>
var enemyParticipants = BattleSystem.GetEnemyParticipants(participants);
GetParticipantWithHighestAgility
Gets the participant with the highest agility.
Params List<Participant>
Returns Participant
.
var participant = GetParticipantWithHighestAgility(participants);
GetNumberOfRemainingParticipants
Gets the number of remaining participants with hit points greater than 0.
Params List<Participant>
Returns int
.
var enemiesRemaining = BattleSystem.GetNumberOfRemainingParticipants(enemyParticipants);
IsBattleOver
Gets whether the battle has ended or not.
Params List<Participant>
Returns bool
.
if (BattleSystem.IsBattleOver(participants))
{
battleState = BattleState.Ended;
soundManager.StopSound(soundManager.battleMusic);
}
DetermineExperiencePoints
Gets experience points gained from defeated participants.
Params List<Participant>
Returns int
.
var experiencePoints = BattleSystem.DetermineExperiencePoints(enemyParticipants);
battleLog.UpdateLog($"Thy Experience increases by {experiencePoints}.\n");
player.ExperiencePoints += experiencePoints;
DetermineGoldPoints
Gets gold points gained from defeated participants.
Params List<Participant>
Returns int
.
var gold = BattleSystem.DetermineGoldPoints(enemyParticipants);
battleLog.UpdateLog($"Thy GOLD increases by {goldPoints}.\n");
player.GoldPoints += gold;
DetermineItemsDropped
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.
Gets any items gained from defeated participants.
Params Dictionary<T, int>
, T
being the class or item object and int being the chance to drop out of 100
Returns List<T>
Dictionary<Item, int> droppableItems = new Dictionary<Item, int>();
foreach (var enemyCombatantDroppableItem in enemyCombatant.droppableItems) {
droppableItems.Add(enemyCombatantDroppableItem.item, enemyCombatantDroppableItem.chanceToDrop);
}
var itemsDropped = BattleSystem.DetermineItemsDropped<Item>(droppableItems);
foreach (Item item in itemsDropped) {
battleLog.UpdateLog($"{enemy.Name} dropped a {item.name}!\n");
playerCombatant.items.Add(item);
}
AttackResult
A simple class that is returned after a Participant
attacks.
public class AttackResult {
public enum AttackType {
Hit,
CriticalHit,
Missed
}
public AttackType Type;
public int Damage;
public AttackResult(AttackType type, int damage) {
Type = type;
Damage = damage;
}
}
Participant
An enemy, NPC, or player character that participates in battle.
CalculateAttackPower
Calculates participants attack power. Should be called after a new weapon is equipped.
Params int
Returns void
playerCombatant.weaponSlot = ironAxe;
playerParticipant.CalculateAttackPower(ironAxe.attackPower);
CalculateDefensePower
Calculates participants defense power. Should be called after a new piece of armor is equipped.
Params int
Returns void
playerCombatant.shieldSlot = leatherShield;
playerParticipant.CalculateDefensePower(leatherShield.defensePower);
InflictDamage
Inflicts allotted damage to the participant. Will floor hit points to 0.
Params int
Returns void
target.InflictDamage(results.damage);
Confused? Check out the sample project!
https://github.com/Joshalexjacobs/SlimeBattleSystemSample
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.
Formulas used
- Battle Turn Order
- Attacking
- Dodging
- Critical Hits
- Fleeing
- Awarding Experience Points
- Awarding Gold Points
- Determining items dropped
What's missing?
Spells, Items, and Status Ailments
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.
Leveling and name based stat growth
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.
For those interested: akiraslime posted a great Dragon Warrior stat growth FAQ here.
75% - 100% Enemy Health Formula
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.
Experience/Gold Multiplier
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.
Sources and additional reading material
Game Developer: How Final Fantasy and Dragon Quest handle combat math