Skip to main content

Combat

Combat Engine

A single Stellarch match resolves in deterministic phases. Once teams are validated and pre-battle abilities apply, the engine runs up to 50 rounds. Each round walks every alive fighter in action order, dispatches an attack, resolves the hit pipeline, applies damage, then handles death and reflects.

Battle pipeline

Every battle moves through these phases in order. The engine is a thin shell around five pure-function engines (SeededRng, Targeting, AttackResolver, DeathResolver, RoundResolver).

  battle_start
       |
       v
  pre_battle_phase    -- rulesets first, then abilities
       |
       v
  combat_loop  ------>+
       |              |
       |              v
       |        round_start
       |              |
       |              v
       |        per_turn ability hooks   (Heal, TankHeal)
       |              |
       |              v
       |        RoundResolver            (Targeting, AttackResolver,
       |              |                   DeathResolver per actor)
       |              v
       |        end_of_round rulesets    (Earthquake)
       |              |
       |              v
       |        poison_tick + cleanup
       |              |
       |              v
       |        round_end_summary  ------+
       |                                 |
       |   <--  exit when one side hits 0 alive
       v
  compute_result
       |
       v
  battle_end
  1. battle_start — validate both teams and record the opening snapshot.
  2. pre_battle_phase — apply ruleset hooks (Armored Up, Earthquake markers), then ability hooks (Rally-Cry, Bulwark, Reinforce).
  3. combat_loop — up to 50 rounds. Each round: round_start → per-turn ability hooks → RoundResolver → end-of-round rulesets → poison tick → cleanup.
  4. compute_result — winner is the team with alive fighters. Round-cap fallback breaks ties on total alive HP.
  5. battle_end — record winner, reason, rounds played.

Action order

Each round, alive fighters act in this priority:

  1. Primary: speed descending — faster acts first.
  2. Secondary: rarity descending — Legendary > Epic > Rare > Common on speed ties.
  3. Tertiary: deterministic RNG jitter — same seed + same teams produces the same order every time.

Eligibility ladder

Whether a fighter can attack at all depends on its EFFECTIVE position (its 1-indexed rank among alive teammates) and its attack type. When the front fighter dies, the next one slides up and inherits position 1.

Position Magic Ranged Melee
1 (front) Yes No Yes
2 Yes Yes Spearline only
3+ Yes Yes No

Melee Mayhem overrides this entire ladder — every melee fighter is eligible regardless of position.

The 5-stage hit pipeline

Every swing walks these stages in order. The pipeline only mutates target HP/armor and (on reflects) attacker HP. The returned event list is frozen — replay tools deserialize it byte-for-byte.

  1. 1. Hit roll — two-phase ladder

    Phase 1 (speed-difference): base chance starts at 1.0. If the target is faster, subtract (target.speed − attacker.speed) × 0.10. Floor at 0.50. The speed math alone never produces a sub-50% chance.

    Phase 2 (ability evasion): starting from Phase-1's floored chance, subtract 0.25 each for Lift-Drive (vs non-Lift-Drive physical), Slipstream (vs melee/ranged), and Phase (vs magic). This stack CAN push the final chance below 0.50. True Strike short-circuits to 1.0 before Phase 1.

  2. 2. Class mitigation

    Plate halves physical damage (melee + ranged), min 1. Null-Field halves magic damage, min 1. Aegis Bubble nullifies the first hit entirely, then breaks.

  3. 3. Armor absorb

    Melee and ranged damage absorbs through armor by default. Magic skips armor — unless the defender has Void Armor, which routes magic through the armor track too.

  4. 4. HP application

    Whatever damage remains hits HP. The damage_applied event fires even when remaining damage is 0.

  5. 5. Reflects

    Fired only when damage > 0 hit HP AND attacker is still alive. Recoil-Plate reflects fixed 2 vs melee. Return Fire reflects ceil(raw / 2) vs ranged. Magic Reflect reflects ceil(raw / 2) vs magic. Reflects use the raw pre-mitigation damage as the basis.

Death + on-death triggers

When a fighter's HP hits 0, the DeathResolver flips its alive flag, emits one on_death_trigger event per on-death ability the fighter carries, and checks whether the remaining team should activate Final Defiance.

  1. died — fighter is removed from action order.
  2. on_death_trigger — abilities like Killstreak (caller-side kill bonus) and Overrun (chain attack) fire here.
  3. last_stand_activated — if the death leaves a single alive teammate carrying Final Defiance, its stats scale 1.5x for the rest of the match.

End of round

After every fighter acts, the engine runs:

  1. End-of-round ruleset hooks — Earthquake deals 2 true damage to every non-Lift-Drive fighter.
  2. Hemobane tick — every Poisoned fighter takes 2 damage. Resolved once per round at the type level (not per-bearer).
  3. Solo-survivor sweep — confirms Final Defiance activations triggered by mid-round deaths.
  4. Round-end summary — alive counts on both teams. If either side hits 0, the loop exits.

Security

Determinism is the contract. Same seed + same teams = byte-identical events. See the security page for how to verify a replay against the source seed.

Security