Skip to content

Combat System

Vairn edited this page Jun 2, 2026 · 9 revisions

Combat System

Navigation: Home - Combat - Combat System

Trace of the encounter/battle engine in mm2.capstone.asm. All addresses are code-hunk load addresses (base 0). A4 = data_base + 0x7FFE, so an A4 displacement -d maps to data-hunk offset 0x7FFE - d (see doc 16). Confidence is high for control flow and data layout; the exact HP/to-hit/damage arithmetic is only partially reduced (noted inline).

[TOC]

Top-level flow

encounter start
  -> 0x11C82  build battle arrays (per monster slot: 0x11C2C)
  -> 0x12A22  COMBAT ROUND LOOP  (repeats each round)
       -> per round: 0x135BE / 0x11D0C / 0x11C82 (display + recompute)
       -> initiative select (highest speed not-yet-acted)
            -> 0x119C2  PLAYER turn   (character -$4F5)
            -> 0x1064C  MONSTER turn  (monster slot -$4F7)
       -> 0x13282  round/over check
       -> 0x12430  VICTORY + rewards   (when -$77BE monster count == 0)
       -> 0x11646  party defeat / retreat

Battle data model

Monster slots (parallel arrays, index 0..10, max 11 on screen)

The encounter holds up to ~11 active monsters. Per-instance state is kept in parallel A4 arrays keyed by slot; full stats are re-derived from the type id by the unpacker at 0x4C8E when needed (see doc 16).

Array (A4) Element Meaning
-$11DE[i] byte monster type id (record index into monsters.dat)
-$53A[i] word current HP (set from unpacked -$11A0)
-$519[i] byte status flags (bit0 = awake/active; &0xFE = afflictions)
-$50E[i] byte speed / initiative (set from unpacked -$11B1, record byte 0x18)
-$503[i] byte secondary stat (from unpacked -$11BA, record byte 0x14 hi nibble)
-$77BE byte live monster count
-$524 byte active party count (clamped to 10 for menus)
-$5E4A[i] byte monster "has acted this round" flag
-$5E40[i] byte party "has acted this round" flag

0x11C2C instantiates one slot from the scratch unpack globals; 0x11C82 loops it over all monsters at battle/round start.

Party (character objects)

Characters are heap objects fetched by index via JSR -$7F20(A4) (-$4F5 = current character, -$7959 = party size, -$5E4D = front-rank cutoff). Field offsets observed in combat:

Off Field
$0F class (2 gets a ranged option)
$26 status bits (bit1 = silenced)
$4E has bow / ranged weapon
$58 spell points (word)
$6E speed (initiative)
$72 spellcaster flag

Round loop (0x12A22)

  1. Reset round flags; clear -$5E4A[0..9] and -$5E40[0..7] (acted flags).
  2. For each monster, roll RNG and update -$519[i] (wake/initiative bit).
  3. Initiative: scan monsters for the highest -$50E[i] among not-acted -> -$5E38 (best monster speed) / -$4F7 (slot); scan party for highest char.$6E among not-acted -> -$5E37 / -$4F5.
  4. If best character speed > best monster speed (and nonzero) -> 0x119C2 (player turn). Else if a monster can act -> 0x1064C (monster turn). Else nobody left -> advance.
  5. 0x13282 decides whether the round/combat continues.
  6. End: -$77BE==0 -> 0x12430 victory; else defeat/retreat via 0x11646.

RNG helper: JSR -$7BB4(A4) with (1, max) returns a roll in [1,max].

Player turn (0x119C2)

  • 0x11866 draws the command bar and sets capability flags for the active character:
    • -$5E36 melee/attack: character is within front-rank cutoff -$5E4D.
    • -$5E35 shoot: class $0F==2 or front rank, and $4E (bow) set.
    • -$5E34 cast: not silenced ($26 bit1) and caster ($72) and spell points $58 > 0.
    • Each available command is printed via 0x1181E (string pointer table at A4-$6F9C: "A-Attack F-Fight S-Shoot C-Cast U-Use B-Block R-Run E-Exch V-View", colors/labels at -$6F4B/-$6F54).
  • 0x1175C reads a key and accepts it only if valid (B D P Q V U E R always, A/F if -$5E36, S if -$5E35, C if -$5E34).
  • Dispatch (jump table at 0x11BD0, key normalised by -0x41):
Key Action Handler
A Attack (single target) 0xCD90
F Fight (auto/melee) 0xCFD0
C Cast spell spell select -> dispatch table 0xD000..0xD256
S Shoot (ranged) (ranged path)
B Block 0x11B0A (-$7D5E/-$7C3E)
R Run / retreat 0x11B1A (-$7CC2 party reorder)
U Use item 0x11B62 (0x133EC)
V View character 0x11B6E (-$7E00)

Target selection: 0x111DA ("which (A - x)?"), -$5E32 selects monster-side (-$77BE) vs party-side (-$524) targeting; chosen index -> -$51D. The Attack command has its own selector at 0xD43C/0xD390 (auto-targets when only one monster remains).

Spells (Cast)

0xD000..0xD256 is a jump table of per-spell handlers (JSR $bc3a, $bc7a, … in the 0xBC00..0xC800 block), indexed by spell number; entries 0xD186+ are the .w offsets.

Monster turn (0x1064C -> 0x106A0)

For monster slot -$4F7 (status -$519[i]):

  • Status byte >= 0x80 (special/disabled) -> handled separately.
  • Flee: -$11B6 (Oabil bits 5-6) indexes probability table A4-$6F1A; on a successful roll the monster runs (0x10DFC prints " runs").
  • Otherwise it acts:
    • Archer (-$11AE, Sabil bit6): ranged attack (0x10584).
    • Group/party attack when the Pabil effect -$11BC is in range: 0x10002 prints " " from the verb table at A4-$6E56 (index 29 = "frenzies"); see doc 16.
    • Melee otherwise: 0x10118, applying the single-target status effect -$11B9 (Sabil low5) to a party member.
  • Multiplies/breeds (-$11A1, Oabil bit7): 0x100B0 roughly doubles the on-screen count.
  • Adds friends (-$11B7, Oabil low nibble): 0x11F0A appends reinforcement monster ids to -$11DE[] (" adds friends!").

Status / afflictions

Battle status uses the -$519[] byte plus per-character bits. UI abbreviations (table at A4-$6FBC): Enca Mdls Held Aslp Afrd Weak Siln Hurt (encased / mindless / held / asleep / afraid / weak / silenced / hurt). Full victim-effect names are the single-attack message table (doc 16, master[10..39]).

Victory & rewards

On -$77BE==0, 0x12430 ends combat. Per defeated monster, the reward routine 0x10B74 decodes record byte 0x10:

  • bit2 -> add RNG(1..10) to -$3F12 (gems);
  • bits3-4 -> gold tier into -$3F10 (with /16 or /2 scaling + RNG);
  • bits0-1 -> item-drop level (-$5E28/-$5E29);
  • accumulated XP -$119E added to party total -$6FC6. "Experience has multiplied three-fold." (string 0x14300) handles the bonus case.

Key addresses

Addr Routine
0x12A22 round loop
0x119C2 player turn
0x11866 build command bar + capability flags
0x1175C read valid command key
0x1181E print one command-bar entry
0x111DA target selector ("which (A - x)?")
0x1064C / 0x106A0 monster turn / AI dispatch
0x10002 monster group attack (verb table A4-$6E56)
0xFEEA monster single attack
0x10118 melee resolution
0x10DFC flee ("runs")
0x100B0 multiply / breed
0x11F0A adds friends (reinforcements)
0x10B74 per-monster reward decode
0x12430 victory / end combat
0x11646 defeat / retreat
0x11C2C / 0x11C82 instantiate monster slot / all slots
0x4C8E monster stat unpacker (doc 16)
0xD000 spell-effect jump table

Open items (not fully reduced)

  • Exact to-hit / AC interaction and physical damage formula inside 0xCD90/0xCFD0/0x10118.
  • HP/XP code -> value tables (A4-$746C, A4-$7464) numeric contents.
  • Full per-spell handler semantics (0xBC00..0xC800).

See also

Page Why
Combat Overview Summary + mermaid
Spells and Item Use Combat spell flags
3D View and Game Screen Encounter sprites

🏠 Home


Getting started

Runtime & engine

Graphics

UI

Data formats (.dat)

Combat

Audio

Game systems

Might and Magic I (DOS)

Gallery

Tools

Clone this wiki locally