In last week’s developer diary I talked about the various forms of combat you’ll see in Ships and Scurvy. In a nutshell, there are sea battles, hero battles and land battles – which are in fact the subject of today’s update!
I’ve always been a fan of the 1984 game Ancient Art of War’s battle system, mainly for its simplicity and visual appeal. Lots of little troops running at each other, you give them basic orders during the combat. I’ve tried to emulate this battle system a few times in the past but never quite nailed it – when I built Swords & Sandals Crusader, I ended up having to make combat turn based instead of realtime because with the vast number of troops it just became unwieldy and too chaotic. See the image below: Troops would move into the center of the field, attack, then retreat to their positions in formation. It worked fairly well, but some battles felt like they went for far too long – combat was one of the central parts of the game, so I didn’t want it to feel tedious.
For Ships and Scurvy, I took what I’d learnt from previous battle systems and came up with the following key issues:
- The battle was to be on one screen, with no panning left to right – this means limited real estate
- In realtime combat, it’s too difficult to control individual soldiers – especially as they die so quickly
- More than 30 troops on either side becomes visually too busy and combat becomes hard to follow
- There’s a technical limitation of about 100 troops on each side before framerate drops, but battles of these size were too chaotic in early tests
- Controlling groups of troops with general orders makes combat a much more tactical affair
Knowing all of this, I started work on creating a basic sailor class – this extends my character class (which controls animations, basic movement etc ) and adding to this some battle specific AI functions. I then grouped the sailor into either Heavy, Ranged or Light troop class and created a hero and enemy array full of such sailors.
Each sailor has five statistics:
- Speed affects how fast the character moves in battle
- Attack is how likely the character is to hit their enemy
- Defence is the character’s chance of defending an attack
- Health represents how many hits the player can take before falling in battle
- Morale represents how likely the character is to flee the battle
The basic premise of the groups (and the heart of the tactics ) is thus:
- HEAVY troops have an advantage against LIGHT troops
- LIGHT troops have an advantage against RANGED troops
- RANGED troops have an advantage against HEAVY troops
It’s essentially a variation on rock paper scissors, each troop is stronger or weaker than another troop. Once these battle groups have been created, I then ask the player to select battle tactics from an optional menu. Each troop can change their behaviour by either cautiously or furiously attacking a specific group. In the below example, the light troops will cautiously attack the enemy’s ranged troops, giving them an advantage in attack accuracy but a disadvantage in firing speed.
Once battle tactics have been selected, the battle begins. I start battle by having the troops rush out on the the field (from the left and the right) and then looping through each group array and assigning them orders based on their tactics. Each sailor has the following basic battle AI functions.
FindTarget: The sailor seaches for an enemy who is a) alive and b) in the group their are tactically programmed to attack. If they fail to find one, they will try to attack any enemy on the field.
Move: The soldier then moves towards their target. Using the cos/sine method, I get the player to look at a target, then move their x and y velocity according to the cos/sin of the looking angle. It’s pretty basic but it works well. The characters move per pixel at their speed stat , but furious characters will move twice as fast.
PrepareAttack: Launches the soldier into their attack animation. If the character is ranged, it actually fires a projectile at their enemy and waits half a second. Once the animation is complete, I then launch into DoAttack.
DoAttack: The heart of combat. Here, the character ‘flips a virtual coin’ once per attack stat. Each coinflip that returns ‘tails’ is considered a successful attack. The defensive character then flips coins a total amount of times per defence stat. If the defensive character rolls more tails than the attacking character, they defend the attack; otherwise they lose health. If their health reaches zero, they of course invariably perish on the field.
So, for example: Soldier A has 3 attack, and solider B has 2 defence. Soldier A flips a coin 3 times and gets (heads/tails/tails) for a total of 2 hits. Soldier B flips 2 coins and comes up with (heads/tails), defending one of the hits. They take 1 damage.
CheckMorale: Each second that passes in battle, I force each soldier to do a morale check. Morale is a number between 1-100 , determined by the ship’s crew going into battle. Underfed, poorly treated sailors will go into battle with low morale, for example. Upon performing this function, the character rolls a number between 1>100 . If this number is lower then their morale, they will flee the field. Each death or sailor that flees reduces the squad’s morale by a number, meaning that the longer goes on and the poorer your squad fares, the more likely they are to flee.
Battle continues until all the soldiers on one side have either fled or died. At this point the winning side may either free the captive enemies, invite them into the ship’s crew (for a fee), enslave them or even put them to the sword. The player’s reputation through the game will of course depend on actions like this. Nobody likes a slaver ( except perhaps another slaver.)
Anyway, thus end’s today’s development diary – as a bonus, here’s some a video of two squads in battle on a beach. Enjoy!
As always, happy journeys!