Today we will be creating a traditional RPG (role-playing game) battle system featuring Mages, Warriors, Dragons, and Skeletons!
We will be breaking the project down into three primary parts: Heroes, Monsters, and Battles.
Heroes represent player characters, they can be one of four classes, Warrior, Mage, Cleric, or Rogue. Depinding on a hero's class they will have different stats and abilities, for example a Warrior is stronger and more durable than a mage and therefore they have a higher strength and constitution. These statistics effect how their various abilities work. As heroes defeat monsters they gain xp (experience points) and if they gain enough experience they level up and become stronger.
Monsters are what the heroes fight. They can be classified into monster families (like dragons and undead) as well as individual monster types within those families (like Red Dragons and Vampires). Monsters have their own abilities depending on a combination of their family and type.
Battles This is what brings everything together. Participants will perform actions in order of their speed
attribute - faster units go before slower units. Hero abilities are user determined, monster abilities are used according to their command queue (described below) and target heroes randomly. Battle continues until either all the heroes or all the monsters are wiped out.
Features common to all Heroes: When initialising a Hero you may pass a
level
parameter to specify what level of hero you would like.
- Stats:
strength
constitution
intelligence
speed
hp
- current hit pointsmaxhp
- maximum hit pointsmp
- current magic pointsmaxmp
- maximum magic pointslevel
- current character levelxp
- current experience pointsxp_for_next_level()
- when xp == this it causes the hero to level up (should be equal tolevel * 10
)gain_xp(xp)
- causes the hero to gainxp
experience points, should also handle the process of levelling upfight(target)
- Basic attack dealing damage equal to the hero'sstrength
to thetarget
abilities
- a collection of strings representing the hero's combat abilities. For example aMage
'sabilities
would be something like('fight', 'fireball', 'frostbolt')
take_damage(damage)
- hero takesdamage
amount of damage, having it subtracted from their current hpheal_damage(healing)
- hero healshealing
hit points, but not beyond theirmaxhp
is_dead()
- if the hero's hp have been completely depleted this returnsTrue
Stats Base level stats (strength, constitution, intelligence, speed) for a level 1 Hero start at 6 and may be modified by child classes.
Hit Points Base level hit points for a level 1 Hero start at 100 + 1/2 of the hero's
constitution
, dropping any fractions.
Magic Points Base level magic points for a level 1 Hero start at 50 + 1/2 of the hero's
intelligence
, dropping any fractions.
Levelling Up When a hero has
xp
greater than or equal toxp_for_next_level
they level up. Any excess xp is carried over after their xp is set to 0. Upon level up all stats are increased by one (or more if the hero has a stat modifier, this will be explained later). Theirmaxhp
is increased by 1/2 their newconstitution
and theirhp
is brought back up to full.maxmp
is also increased by 1/2 the newintelligence
, mp also being filled. When increasingmaxhp
ormaxmp
remember to drop any fractions.
Stat Modifiers Subclasses can apply modifiers to the hero's stats, either increasing or decreasing them. Modifiers should be added directly to the base value of the appropriate stat. At level up if the modifier is positive it is added to to the standard +1 received at level up. Example: If a subclass had a +1
strength
modifier it would have 7strength
at level 1 and would gain 2strength
each time it levelled up.
Special Abilities Each subclass has its own unique special abilities. Many of these abilities have an
mp
cost, if the hero does not have enough mp to use the ability anInsufficientMP
error should be raised. When calculating damage, drop fractions from final damage number.
Stat Modifiers
strength
+1intelligence
-2constitution
+2speed
-1
shield_slam(target)
- Cost: 5 MP
- Damage: 1.5x
strength
reckless_charge(target)
- Cost: 4 HP
- Damage: 2x
strength
Stat Modifiers
strength
-2intelligence
+3constitution
-2
fireball(target)
- Cost: 8 MP
- Damage: 6 + 0.5x
intelligence
frostbolt(target)
- Cost: 3 MP
- Damage: 3 +
level
Stat Modifiers
speed
-1constitution
+1
heal(target)
- Cost: 4 MP
- Heals target for
constitution
smite(target)
- Cost: 7 MP
- Damage: 4 + 0.5x (
intelligence + constitution
)
Stat Modifiers
speed
+2strength
+1intelligence
-1constitution
-2
backstab(target)
- Cost: None
- Special requirement: Target must be undamaged. If target is damaged must raise
InvalidTarget
- Damage: 2x
strength
rapid_strike(target)
- Cost: 5 MP
- Damage: 4 +
speed
Features common to all monsters: When initialising a Monster you may pass a
level
parameter to specify what level of monster you would like.
- Stats:
strength
constitution
intelligence
speed
maxhp
hp
level
xp()
- returns the xp that would be earned if this monster were to be defeatedfight(target)
take_damage(damage)
heal_damage(healing)
is_dead()
attack(target)
- Attacks the target using the next ability in the monster's command queue (explained later) Note: Monsters do not use MP
Stats Monsters start with a base stat level of 8 versus the 6 that heroes receive. Instead of receiving stat modifiers like heroes, monsters may receive stat multiplier instead. These are applied in the following manner: At level 1 a stat is set to 8x the stat multiplier. For levelled monsters set the stat to 8X multiplier +
(level - 1
)x multiplier, dropping fractions. Monster have a base HP of 10 (this may be overidden by monster families and subtypes, more on this later) to calculate their actualmaxhp
use the base hp + (level - 1) x (0.5xconstitution
), dropping fractions as usual.
XP A monster's xp value may be calculated by taking the average of their stats (
strength
,constitution
,intelligence
,speed
) and adding it tomaxhp % 10
.
Command Queue The command queue determines in which order a Monster uses its abilities. When an ability is used it cycles out to the end of the queue. Example: If a monster had a queue of
['fight', 'bash', 'slash']
the first time it attacked it would usefight
, the next time it would usebash
and so on.
Monsters are divided into families before being separated into individual monster types. Monster families can have multipliers, abilities, and other special features that apply to all of their members.
Family features
- Base HP: 100
constitution
multiplier: 2- Special feature: Dragons have damage reduction, all damage they take is reduced by 5.
tail_swipe(target)
: Dealsstrength
+speed
damage
RedDragon
strength
multiplier: 2intelligence
multiplier: 1.5fire_breath(target)
: deals 2.5xintelligence
damage
GreenDragon
strength
multiplier: 1.5speed
multiplier: 1.5poison_breath(target)
: deals 1.5x (intelligence
+constitution
) damage
Family Features
constitution
multiplier: 0.25- Special feature: Undead take damage from attempts to heal them. Note: The Undead's own special healing abilities do not damage it
life_drain(target)
: Damages the target for 1.5xintelligence
, healing the Undead for the same amount.
Vampire
intelligence
multiplier: 2- Base HP: 30
bite(target)
: Deals 0.5xspeed
damage to the target, healing the Vampire for the same amount. Permanently lowers the target'smaxhp
by an amount equal to the damage dealt.
Skeleton
strength
multiplier: 1.25speed
multiplier: 0.5intelligence
multiplier: 0.25bash(target)
: deal 2xstrength
damage
Family Features
slash(target)
: dealstrength + speed
damage
Troll
strength
multiplier: 1.75constitution
multiplier: 1.5- Base HP: 20
regenerate(*args)
: Restoresconstitution
health to the Troll.
Orc
strength
multiplier: 1.75- Base HP: 16
blood_rage(target)
: Deals 2xstrength
damage to the target while dealing 0.5xconstitution
damage to the Orc
Preparing a battle should be as simple as creating a new Battle
while passing in a list of participants. The battle should handle sorting the participants in order of speed
and then cycle through the list, moving units to the end of the line as they take turns performing actions.
A Battle should have the following outward facing interface:
start()
: Starts the battle!current_attacker()
: returns the unit at the front of the initiative queueexecute_command(command, target)
: uses an ability on the target
Upon running the start
command the program should check to see if the current attacker is a monster, if it is it will pick a hero target at random and use the monster's next attack in its command queue. It will then repeat the process until it reaches a hero's turn. At this point it returns a multi-line string containing all the events that have happened so far and stating what hero's turn it is. (Details on event formatting below.) It is then up to the user to select a target and action and then execute_command
. This will run the corresponding command (throwing any appropriate errors, including InvalidCommand
if the command doesn't exist) and then resume the cycle of moving through the initiative queue until it reaches a Hero again. At the end of each unit's turn all dead units should be removed from the initiative queue and xp should be rewarded to all heroes for any monsters killed, triggering level ups if appropriate. If at any time all the monsters are wiped out the program should raise a Victory, if all the heroes are killed it should raise a Defeat.
Event formatting Each event should be recorded on a new line in the multi-line string that is returned
- *
fight
command used:{monster} attacks {target} for {damage}!
- other attack ability used:
{monster} hits {target} with {ability} for {damage} damage!
- unit dies:
{unit} dies!
- XP is rewarded:
{xp} XP rewarded!
- Hero levels up:
{hero} is now level {level}!
- Unit ability causes damage to self:
{unit} takes {damage} self-inflicted damage!
- Hero's turn:
{hero}'s turn!