/ #mechanics 

Combat

Combat is a fight or contest between individuals or groups. It is often matter of life or death. Fortunately it is a very rare occurrence for most of us in the modern world, yet it is a staple of games.

I would theorise that this is a manifestation of how games are fantasies that our brains indulge in to prepare us for situations that are often too dangerous to survive, but I’m sure some other people have written much more eloquently on the topic (this Reddit post seems relevant).

Much has also been written about combat systems, mostly in the context of real-time games and especially fighting games… I read in the region of a dozen articles while looking for inspiration for this article, but I’, only going to link a couple.

Firstly, Gamasutra: The Fundamental Pillars of a Combat System, while aimed more at real-time games, does address the fundamental concerns of designing interesting combat systems.

Secondly, Gamasutra: 12 ways to improve turn-based RPG combat systems, which although it is aimed at RPG games, does a very good job of putting some of the principles from the first article into a turn-based context.

One thing is very clear from the second article (and its follow-up) - a good combat system needs much more than just a system for entities to do damage to each other:

  • Movement System:
    • Use space (1)
    • Variable distance (5)
    • Directional facing (6)
    • Variable terrain (7)
    • Manipulable Terrain (8)
    • Elevation effects (13)
    • Movement focused special abilities (14)
  • Units System:
    • Give the player at least 6 characters (2)
    • Specialise the characters (3)
    • Specialise the enemies (4)
    • Death countdowns and revival (17)
    • Character-centric consequences (18)
  • Economy:
    • Resource management (9)
  • Victory:
    • Support multiple objectives (9)
  • Inventory & Items:
    • Item drops (15)
  • Combat:
    • Give units multiple attack options (10)
    • Allow delayed attackes (12)
    • Zones of danger (16)

You’ll find most of those are covered elsewhere on this site, so I will focus the remainder of this article on the ECS system you need to implement to accomplish the most basic bits under the Combat heading. That is: a way to calculate how much damage one entity does to another when an attack occurs.

Delayed attacks depends on the turn system and zones of danger would likely be a combination of the movement system and the turn system, so those those are clearly beyond the scope of basic combat.

ECS Design

There are many ways of implementing these damage dealing systems in interesting ways - the one shown is fairly simple and are intended mainly to serve as an example. It is based on what Heroes of Might and Magic does for calculating attack damage.

Components

AttackAbility

  • strength: Integer
  • min_damage: Integer
  • max_damage: Integer

The strength is how good the attacker is at offensive operations. The min_damage and max_damage values specify how much damage within a random range.

DefenseAbility

  • strength: Integer

The strength is how good the defender is at reducing incoming damage from attacks.

AttackCommand

  • attacker: Integer

Command component to indicate that an attack has been made.

Damage & Health

See Health System.

Entities

Each unit will have AttackAbility, DefenseAbility and Health.

When an attack is made, we add an AttackCommand on the target with the attacker entity ID initialised so that the attacker’s AttackAbility can be retrieved.

The attack resolution will remove the AttackCommand and replace it with a Damage for the health system to resolve.

Systems

CombatSystem

The combat system is responsible for calculating the damage from an attack and adding the Damage component

attacked = world.get_entities_with_components(
    AttackCommand,
    DefenseAbility
)

for unit in attacked:
    attacker = unit.attack_command.attacker
    attack_ability = world.get_component(attacker, AttackAbility)
    defend_ability = unit.defense_ability

    damage_amount = random_int(
        attack_ability.min_damage,
        attack_ability.max_damage
    )

    defense = defend_ability.strength - attack_ability.strength:
    multiplier = 1.0 - defense * 0.1
    multiplier = max(0.1, multiplier)
    damage_amount = damage_amount * multiplier

    damage = Damage(damage_amount)
    world.add_component(unit, damage)
    world.remove_component(AttackCommand)

Notes:

  • Range attacks are not shown in this example, but they are mainly based on the movement system which may calculate distance to decide if an attack can be made or to modify the damage…
  • … a slightly fancier approach might also apply damage drop-off when firing beyond a certain range.
  • This can easily be made Stack aware by simply multiplying the damage by the stack size (possibly in a separate system that runs before the damage system).
Author

Matt Van Der Westhuizen

Back-end service developer at Ubisoft Blue Byte by day - wannabe game designer & developer by night.