Combat Coding

Abstract

Problem: How should combat be architected in RPGs to handle the complex web of systems (animation, sound, UI, AI) that all need access to attack data?

Approach: Tim Cain walks through the evolution of his combat coding across Bard's Tale Construction Set, Fallout, Arcanum, and later projects — explaining why he settled on a combat packet manager combined with an event-driven architecture.

Findings: Centralizing attack data into manager-controlled "combat packets" that flow through a series of events eliminates memory fragmentation, decouples independent systems, and makes the entire combat pipeline cleaner and more extensible.

Key insight: Combat data should not live on the attacker — it should be a shared, manager-controlled packet that any system (UI, sound, AI, animation) can reference as it flows through a well-defined sequence of events.

Source: https://www.youtube.com/watch?v=ltO_rMJJdHE

The Core Architecture: Combat Packets + Event Handlers

Tim's preferred combat system has two pillars:

  • Combat Packets — data structures containing all information about a single attack (attacker, targets, weapon/ability, damage, flags, state). These are allocated and managed by a combat manager, not by individual attackers.
  • Event Handlers — a series of events that the packet flows through, allowing independent systems to react to each phase of combat.

Why a Manager Controls the Packets

Making packets global and manager-controlled solves several problems:

  • Memory fragmentation — packets are requested and freed constantly. A manager can pre-allocate and reuse them, avoiding garbage collection hiccups mid-combat.
  • Shared access — many systems beyond the attacker need packet data: UI needs to know attacker/victim/weapon for display, sound needs armor type to pick the right audio, animation needs weapon info for projectile creation.
  • Unity/Unreal GC danger — if you just new and free packets whenever, the engine's garbage collector will eventually run at the worst possible moment (middle of a big attack animation), causing frame drops.

What's in a Combat Packet

  • Target — can be an object (creature, chest, door, lock, trap) or a location (for area spells like Fireball)
  • Victims array — all objects that actually got hit
  • Damage fields — full damage rolled, damage done to each target
  • Ability/weapon used
  • State flags — filled in progressively as the packet moves through events

The Event Sequence

Tim outlines a specific sequence of events that every attack flows through:

Attack Start

The attacker requests a packet from the combat manager, which returns a pre-initialized one. The attacker fills in whatever they already know. Three common decision functions may be called:

  • "Who should I attack?" (target selection)
  • "What ability should I use?" (ability selection)
  • "Tell me what to do" (combined recommendation)

This is also where expensive calculations should be done if optimization is needed later.

Attack Begin

The packet is handed back to the manager and events start firing:

  • Attack animation starts
  • Sounds play
  • Skill rolls may happen here (if the animation differs on hit vs. miss, or if a weapon can jam)

Projectile Launch

For ranged attacks, this is often triggered by an animation event — "at this frame, create the projectile." The packet provides all the info needed (weapon type, attacker, target) to spawn the right projectile.

Attack In Progress

A per-frame/per-tick method handles:

  • Moving projectiles
  • Checking for early termination (e.g., globe of invulnerability stops the attack before impact)
  • Progress-based sounds

Attack Hit (or Miss)

The critical resolution phase:

  • On hit: Events fire on the attacker ("your attack hit") and on each target ("you were hit"). Targets apply damage resistance, roll saving throws. Area effects may have limits (e.g., "stun up to 3 targets") tracked via packet flags.
  • On miss: A separate miss event fires. UI and sound may behave differently. Each target that was missed gets notified individually — important for area attacks where some targets save and others don't.

Attack Finishing

Frame-based cleanup: missed projectiles keep moving, animations complete, triggered effects play out.

Attack Completed

Final event:

  • Cooldowns start
  • The packet is released back to the manager's pool for reuse

On Optimization

Tim emphasizes his rule: optimize later, not during initial coding. Example: if 10 of 15 event handlers look up the victim's armor, you might cache it in the packet at combat start. But if only 3-4 do it and it's cheap, don't bother — premature optimization wastes time, may do nothing measurable, and could have been spent fixing real bugs.

Evolution of Tim's Approach

Tim tried different approaches across his career:

  • Bard's Tale Construction Set — one approach to turn-based combat
  • Fallout — a different approach, still turn-based
  • Arcanum — yet another method
  • Post-Arcanum — settled on the packet manager + event handler pattern

His earlier code was "much more non-event-based, very deterministic coding" — which he now calls "a mistake." The event-driven approach is more flexible and cleaner.

References