A lightweight RPG combat system for Unity that emulates the battle formulas from the original Dragon Quest.
- 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
Don't like reading documentation? Take a look at the sample unity project!
A static class that handles all combat logic.
Sets the randomization seed used by most formulas.
Params string
string seed = "New Seed";
BattleSystem.SetRandomizationSeed(seed);
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);
}
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);
Determines the player participant an enemy will target during their turn.
Params List<Participant>
Returns Participant
Participant target = BattleSystem.DetermineEnemyTarget(playerParticipants);
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);
}
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;
}
Gets a list of Player and NPC participants.
Params List<Participant>
Returns List<Participant>
var playerParticipants = BattleSystem.GetPlayerParticipants(participants);
Gets a list of Enemy participants.
Params List<Participant>
Returns List<Participant>
var enemyParticipants = BattleSystem.GetEnemyParticipants(participants);
Gets the participant with the highest agility.
Params List<Participant>
Returns Participant
.
var participant = GetParticipantWithHighestAgility(participants);
Gets the number of remaining participants with hit points greater than 0.
Params List<Participant>
Returns int
.
var enemiesRemaining = BattleSystem.GetNumberOfRemainingParticipants(enemyParticipants);
Gets whether the battle has ended or not.
Params List<Participant>
Returns bool
.
if (BattleSystem.IsBattleOver(participants))
{
battleState = BattleState.Ended;
soundManager.StopSound(soundManager.battleMusic);
}
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;
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;
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);
}
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;
}
}
An enemy, NPC, or player character that participates in battle.
Calculates participants attack power. Should be called after a new weapon is equipped.
Params int
Returns void
playerCombatant.weaponSlot = ironAxe;
playerParticipant.CalculateAttackPower(ironAxe.attackPower);
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);
Inflicts allotted damage to the participant. Will floor hit points to 0.
Params int
Returns void
target.InflictDamage(results.damage);
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.
- Battle Turn Order
- Attacking
- Dodging
- Critical Hits
- Fleeing
- Awarding Experience Points
- Awarding Gold Points
- Determining items dropped
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.
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.
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.
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.
Game Developer: How Final Fantasy and Dragon Quest handle combat math