/ #mechanics 

Encounters

An encounter is an unexpected or casual meeting with someone or something. It’s the little adventures that provide texture to the larger narrative.

Most pen-and-paper role-playing games define encounters as one of the building blocks of the narrative structure: Actions -> Encounters -> Adventures -> Campaign.

In the case of Heroes of Might and Magic, an encounter is anything you find on the world map that you can interact with. The nature of the object determines the nature of the encounter:

  • Resources / Campfires: Gain some free stuff.
  • Treasures: Decide whether to gain gold or gain experience.
  • Armies: They may offer to join you, run away or fight you.
  • Items: Gain the item, or be offered to buy the item or be ambushed.
  • Gateway: Teleport across the map.
  • Mines: Capture them.
  • Dwellings: Gain free creatures or be offered to buy them.
  • Castles / Towns: Fight to take them if not yours, gain spells if they are.

This list is already long, but is by no means definitive. The point I am trying to illustrate is that encounters can be extremely diverse… anything interesting that can happen to the player while running around the map and interacting with stuff counts, so take the proposed design as a very rough example, because your encounter system should be tailored to what makes sense for your game.

ECS Design

Components

Encounter

  • delete: Boolean
  • active: Boolean

The delete flag indicates if this is a once-off encounter that should be deleted after being triggered.

The active flag indicates if this encounter has already been triggered. For example graveyards only give a battle with reward once, afterwards they damage your morale instead. Mills and windmills give a resource once a week, so they can become inactive while used and use a TimedEvent to reset every week.

EncounterType*

You will need a component per type of encounter to track what is gained or what state changes may be necessary (for example resolving a battle before giving a reward). This implies that you will have an Encounter system for resolving each different type of encounter.

EncounterTrigger

  • entity: Integer

These should be dropped on each tile by entities as they move across the map, allowing encounters to trigger and providing context about who triggered them in entity.

Entities

Each encounter tile on the map would have an Encounter and an EncounterType*. Encounters that reset after a certain amount of time will have a TimedEvent as well.

Systems

ResourceEncounterSystem

Example of a system for picking up resources:

encounters = world.get_entities_with_components(
  Encounter,
  EncounterResource,
  EncounterTrigger
)

for encounter in encounters:
  triggerer = encounter.encounter_trigger.entity
  owner = world.find_component(triggerer, Controllable)
  resource_type = encounter.encounter_resource.type
  resource = find_resource(owner, resource_type)
  resource.amount += encounter.encounter_resource.amount)
  
  # Clean up after resolution
  self.world.remove_component(encounter, EncounterTrigger)

  if encounter.encounter.delete:
    self.world.remove_component(encounter, Encounter)
    self.world.remove_component(encounter, EncounterResource)

BattleEncounterSystem

Example of a system that triggers a battle:

encounters = world.get_entities_with_components(
  Encounter,
  EncounterBattle,
  EncounterTrigger
)

for defender in encounters:
  attacker = encounter.encounter_trigger.entity
  attacker_army = find_army(trigger)
  defender_army = find_army(defender)

  if encounter.encounter.delete:
    self.world.remove_component(encounter, Encounter)
    self.world.remove_component(encounter, EncounterBattle)

  start_battle(attacker, attacker_army, defender, defender_army)

Notes:

  • find_army finds the army entity of an entity (I assume they will need to be nested in a hierarchy somehow).
  • start_battle changes the game state into the battle mini-game.

CleanEncounterTriggerSystem

This simple system should be run after all other encounter systems to clean up triggers that were not used.

triggers = world.get_entities_with_components(
  EncounterTrigger
)

for trigger in triggers:
  self.world.remove_component(trigger, EncounterTrigger)
Author

Matt Van Der Westhuizen

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