Phaser AE

Combat Rules

Pro packv3.0.0-3Arcade

Damage, healing, armour, elemental resistance, shields and timed status effects, resolved in a fixed order with an event for each thing that happened. The rules hold the numbers; your game decides what they look like.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 14, 2026 · updated October 20, 2026

Combat Rules: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

What it does

You build combatants with hit points, armour, shields and resistances per element, and put them in a combat with a seed. Then you apply a list of effects from a source to a target: damage, a heal, a shield, a status or a cleanse. The whole list is checked before anything changes, and each effect lands in order. Damage takes the critical multiplier first, then flat armour, then the target's resistance to its element, then whatever shield is left, and only then hit points. A defeated combatant rejects further damage, healing and statuses, so healing never brings one back.

Statuses are data, not scripts. Each has a clock, seconds or turns, a duration, an interval, damage or healing per stack, a stack cap and a policy for being applied again: refresh keeps the longer time, stack also adds stacks, and replace starts it over. tick(seconds) advances the seconds clock and nextTurn() the turns clock, and every periodic tick is found exactly, including one on the moment a status expires, however you split the time. Tags on a status let the host ask questions such as "is it burning?" through hasTag.

Everything that happens becomes an event with a sequence number, the time, the turn, the source and target, a kind and an amount: damage, absorbed, defeated, heal, shield, status, cleansed, expired or rejected. Your game drains them to show numbers, flashes and log lines. The pack also ships a small seeded turn duel, with a solver that proves each generated challenge can be won.

Features

  • An exact damage order — critical hit, flat armour, elemental resistance, shield absorption, then hit points, with options to ignore armour and to set the critical chance and multiplier per hit.
  • Elemental resistance — a fraction per element from -1 to 1, where negative values are a weakness and 1 is immunity.
  • Shields — shield points accumulate and absorb what armour and resistance let through.
  • Timed statuses — poison, regeneration and marks with a seconds or turns clock, intervals, stacks up to a cap, and refresh, stack or replace on reapplication.
  • Ordered events — every result with a sequence number, for damage numbers, combat logs and sound.
  • Checked before it changes — a bad effect list is refused whole, so a fight is never left half-applied.
  • Snapshot and restore — the combat as plain JSON, including where the critical-hit stream stands, so a restored fight draws the same criticals the original would have.
  • A turn duel with a proof — createCombatDuel(seed, difficulty) makes a challenge at easy, medium, hard or very-hard with a winning solution found by solveCombatDuel.

Good for

  • Action RPGs and brawlers that need armour, resistances and damage over time without writing the arithmetic again.
  • Turn-based skirmishes and card duels, with statuses on a turns clock that tick as each turn ends.
  • Tower defence and survival games with burning, poisoned or shielded enemies.
  • Server-side or headless simulation, where a fight must resolve the same way without a renderer.
  • Balancing and testing, since a seed and a list of effects always give the same events.

Make it yours

The model is configured by the data you pass in. Each combatant sets its hit points, armour, starting shield, resistances and statuses; each effect sets its amount, element, critical chance and multiplier and whether it ignores armour; each status sets its clock, duration, interval, damage and healing per stack, element, stack cap, policy and tags. Element names are yours, so fire and ice or holy and shadow are just strings.

Two different uses: a real-time arena where enemies take poison that ticks every second through tick, with fire-weak and armoured foes; or a turn-based duel where every status runs on turns, bleeds stack to a cap of three, and the game calls nextTurn as each round ends.

A quick look

A hero and a slime, a fire hit, and the events it produced:

import { combatant, createCombat } from '@phaser-ae/combat-rules';
const combat = createCombat([combatant('hero', 'party'), combatant('slime', 'foes', 40)], 29);
combat.apply('hero', 'slime', [{ kind: 'damage', amount: 20, element: 'fire' }]);
const feedback = combat.drain();

Works with

  • abilities — costs, cast timing, charges and cooldowns on top of these rules.
  • turn-battle — a turn-taking battle model with its own attack-against-defence formula, for a classic JRPG.
  • melee-combat — swing phases, combo windows, blocking and parries for the moment a hit lands.

Good to know

  • Statuses tick data-defined damage, healing and tags; they do not run arbitrary scripts.
  • Resurrection, elemental reaction chains and networking are left to your game.
  • Use one clock per status: tick advances seconds-clock effects and nextTurn turns-clock effects.
  • Events queue until drained, so drain them regularly.
  • The duel's solver searches a bounded set of states: a null answer means no win was found within its limits, not that none exists.

More Arcade packs

Show all →