Phaser AE

Turn battle

Free packv3.0.0-0Logic

Turn-based battles as a state machine over plain data. Two sides of fighters act in speed order, every action resolves through rules you can change, and the battle hands your screen a list of events to play at its own pace.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 19, 2026

Turn battle: 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.

The pack

turnBattle

function turnBattle(): Pack<{ turnBattle: typeof api }>

Install the turn-battle API (game.turnBattle): the battle rules and the Battle state machine.

Helpers

BATTLE_RULES

const BATTLE_RULES: Readonly<BattleRules>

The default rules.

battleRules

const battleRules = (o: Partial<BattleRules> = {}): BattleRules=> …

The default rules with some replaced.

breakTurn

function breakTurn(b: BreakState): boolean

A broken fighter's turn comes round: it skips the turn and recovers a full shield. Returns true when a turn was skipped.

damage

const damage = (atk: number, def: number, factor: number, roll: number, rules: BattleRules = BATTLE_RULES): number=> …

Damage of one hit: max(minDamage, round(base(atk, def) × factor × spread)); roll in [0, 1) picks the spread.

dropFalls

const dropFalls = (m: { boss: boolean; dropItemId: string | null }, roll: number, rules: BattleRules = BATTLE_RULES): string | null=> …

A foe leaves its drop: always for a boss, else on a roll under dropChance.

ELEMENT_WHEEL

const ELEMENT_WHEEL: Readonly<Record<string, string | null>>

The built-in wheel: what each element beats. none is physical and beats nothing.

elementFactor

const elementFactor = (attack: string, target: string, rules: BattleRules = BATTLE_RULES): number=> …

The damage multiplier of an attack element against a target element: weak, resisted (the target beats it) or plain.

encounterFires

const encounterFires = (rate: number, roll: number): boolean=> …

A random encounter fires on a roll under the rate.

fleeSucceeds

const fleeSucceeds = (roll: number, rules: BattleRules = BATTLE_RULES): boolean=> …

A flee succeeds on a roll under fleeChance.

hitBreak

function hitBreak(b: BreakState, weak: boolean): boolean

A hit that exploits a weakness removes one shield point; at 0 the fighter is BROKEN. Returns whether this hit broke it.

isCrit

const isCrit = (lck: number, roll: number, rules: BattleRules = BATTLE_RULES): boolean=> …

A critical hit lands when the roll is under the rules' critChance(lck).

isWeak

const isWeak = (attack: string, target: string, rules: BattleRules = BATTLE_RULES): boolean=> …

True when attack beats target on the wheel.

newBreak

const newBreak = (max: number): BreakState=> …

A fresh gauge. A gauge of 0 can never be broken.

resolveHit

function resolveHit(s: HitSpec, rules: BattleRules = BATTLE_RULES): HitResult

Resolve one hit from the numbers alone.

scaleStats

function scaleStats<S extends { hp: number; atk: number }>(stats: S, difficulty: string, rules: BattleRules = BATTLE_RULES): S

Stats scaled by a difficulty: hp and attack times the rules' multiplier (1 for a difficulty it does not list).

SKILL_EFFECTS

const SKILL_EFFECTS: Readonly<Record<string, SkillEffect>>

The built-in skill effects: damage opponents, heal an ally, guard or haste the user (or the party).

turnOrder

const turnOrder = <E extends TurnEntry>(entries: ReadonlyArray<E>): E[]=> …

Turn order: by speed, highest first; ties go to heroes, then to the order given. Pure and stable.

Classes

Battle

class Battle

A turn-based battle.

  • readonly rules: BattleRules — The rules it resolves by.
  • fighters: Fighter[] — Every fighter, heroes and foes, in the order given.
  • round: number — The round number, 0 before the first.
  • order: Fighter[] — This round's order.
  • turn: number — Index into order of the current actor; -1 before the first turn.
  • result: BattleResult | null — How it ended, or null while it runs.
  • constructor(defs: readonly FighterDef[], o: BattleOptions)
  • get(id: string): Fighter | undefined — A fighter by id.
  • side(side: Side): Fighter[] — The fighters of a side, standing or not.
  • standing(side: Side): Fighter[] — The standing fighters of a side.
  • get actor(): Fighter | null — The fighter whose turn it is, or null.
  • get done(): boolean — True once the current actor has taken its turn: next() moves on.
  • get recovering(): boolean — True when the current actor is broken and will spend its turn recovering.
  • emit(e: BattleEvent): void — Queue an event (for skill effects of a game's own).
  • drain(): BattleEvent[] — Every event since the last call.
  • next(): Fighter | null — Move to the next fighter's turn (a new round when this one is done) and return it; null when the battle is over.
  • targets(user: Fighter, kind: 'foe' | 'ally' | 'downed'): Fighter[] — The choices for a one-target action: standing opponents, standing allies, or downed allies (a revive).
  • reach(user: Fighter, skill: BattleSkill, target?: string): Fighter[] — Who a skill reaches when target (for one-target skills) is chosen: an unknown effect reaches nobody.
  • picks(skill: BattleSkill): 'foe' | 'ally' | null — What a one-target skill asks the player to pick: an opponent, an ally, or nothing (it reaches its targets itself).
  • auto(): BattleAction — What the battle would choose for the current actor.
  • act(action: BattleAction): boolean — The current actor takes its turn. A broken actor recovers instead, whatever is asked. Returns false (and keeps the turn) when the action cannot be done: not enough MP, no such target, an item that did nothing, no fleeing, or the turn already taken.
  • hit(from: Fighter, to: Fighter, element: string, power: number): HitResult — Hit to from from with an element and extra power, through the rules: crits, the wheel, the gauge, defending.
  • heal(from: Fighter, to: Fighter, amount: number): number — Heal to by up to amount; a downed fighter is not healed. Returns what was restored.
  • snapshot(): BattleSnapshot — The whole state, as plain data.
  • restore(s: BattleSnapshot): void — Put a snapshot back (the same battle's options are assumed).

Types

BattleAction

type BattleAction = /** A plain attack on one opponent, with the attacker's element. */ | { kind: 'attack'; target: string } /** A skill; `target` for one-target skills (default the first standing choice). */ | { kind: 'skill'; skill: BattleSkill; target?: string } /** An item: `use` changes the target (pools, flags) and says whether it did anything; nothing done keeps the turn. */ | { kind: 'item'; target: string; name?: string; use(target: Fighter): boolean } /** Defend until the next turn, getting the rules' `defendMp` back. */ | { kind: 'defend' } /** Try to run: the rules' `fleeChance`. */ | { kind: 'flee' } /** Do nothing. */ | { kind: 'wait' }

What a fighter does with its turn.

BattleEvent

type BattleEvent = /** A round began, in this order. */ | { type: 'round'; round: number; order: string[] } /** A fighter's turn began. */ | { type: 'turn'; actor: string } /** A broken fighter spent its turn recovering its shield. */ | { type: 'recover'; actor: string } /** A fighter used a skill. */ | { type: 'skill'; actor: string; skill: string } /** A hit landed. */ | ({ type: 'hit'; from: string; to: string; element: string } & HitResult) /** A hit broke its target's shield. */ | { type: 'break'; to: string } /** A fighter fell. */ | { type: 'down'; id: string; by: string } /** A heal restored health. */ | { type: 'heal'; from: string; to: string; amount: number } /** An item was used. */ | { type: 'item'; actor: string; to: string; name: string; hp: number; mp: number } /** A fighter is guarded (defending) until its next turn. */ | { type: 'guard'; to: string } /** A fighter is hasted. */ | { type: 'haste'; to: string } /** A fighter defends. */ | { type: 'defend'; actor: string; mp: number } /** A flee was tried. */ | { type: 'flee'; actor: string; ok: boolean } /** The battle ended. */ | { type: 'end'; result: BattleResult }

What happened, in order.

BattleOptions

interface BattleOptions

What a battle is made with.

  • rules?: Partial<BattleRules> — The rules, over BATTLE_RULES.
  • rng: () => number — Random numbers in [0, 1): a game's seeded stream (() => game.rng.next()).
  • effects?: Readonly<Record<string, SkillEffect>> — Skill effects, over SKILL_EFFECTS.
  • choose?: (battle: Battle, actor: Fighter) => BattleAction — Choose an action for a fighter the player does not command (foes, or heroes in an auto battle). Default: attack a random standing opponent.
  • canFlee?: boolean — Can the heroes run. Default true.

BattleResult

type BattleResult = 'win' | 'lose' | 'flee'

How a battle ended.

BattleRules

interface BattleRules

How a battle is resolved. Every field has a default (BATTLE_RULES).

  • wheel: Readonly<Record<string, string | null>> — What each element beats (or null): an attack of an element on what it beats is a weakness. Default ELEMENT_WHEEL.
  • weakFactor: number — Damage on a weakness. Default 1.5.
  • resistFactor: number — Damage on a target whose element beats the attack's. Default 0.5.
  • critFactor: number — A critical's multiplier. Default 2.
  • brokenFactor: number — What a broken fighter takes. Default 1.5.
  • defendFactor: number — What a defending fighter takes. Default 0.5.
  • defendMp: number — MP a turn spent defending gives back. Default 5.
  • hasteFactor: number — Speed multiplier while hasted. Default 1.5.
  • hasteTurns: number — Rounds a haste lasts after the turn it is cast (counted down on the hasted fighter's own turns); Infinity is the rest of the battle. Default Infinity.
  • spread: number — The damage spread: a hit is the formula times 1 ± spread/2. Default 0.2 (0.9 to 1.1).
  • minDamage: number — The least a hit does. Default 1.
  • fleeChance: number — Chance a flee succeeds. Default 0.6.
  • dropChance: number — Chance a non-boss foe leaves its drop. Default 0.3.
  • healAtk: number — A heal's amount: the skill's power plus this share of the healer's attack. Default 0.5.
  • difficulty: Readonly<Record<string, number>> — Stat multipliers per difficulty: hp and attack are scaled. Default gentle 0.8, fair 1, cruel 1.25.
  • base(atk: number, def: number): number — The damage formula before the factors: default atk - def / 2.
  • critChance(lck: number): number — The chance of a critical from luck. Default lck / 100.

BattleSkill

interface BattleSkill

A skill as the battle sees it.

  • id: string — Its id, for events.
  • name?: string — Its name, for events. Default the id.
  • effect: string — What it does: a key of the battle's effects (built in: damage, heal, guard, haste).
  • power: number — For damage, added to the user's attack; for a heal, the amount (plus the rules' share of the user's attack).
  • element?: string — Its element. Default none.
  • mp: number — MP it costs.
  • target: 'one' | 'all' | 'ally' | 'party' | 'self' — Who it aims at: one foe, all foes, one ally, the party, or the user.

BattleSnapshot

interface BattleSnapshot

A battle's whole state, for snapshot and restore.

  • fighters: Fighter[] — The fighters.
  • round: number — The round number, 0 before the first.
  • order: string[] — This round's order, by id.
  • turn: number — Index into the order of the current actor, -1 before the first turn.
  • result: BattleResult | null — The result, or null while it runs.
  • acted: boolean — The current actor has taken its turn.

BattleStats

interface BattleStats

A fighter's six stats.

  • hp: number — Most health.
  • mp: number — Most magic points.
  • atk: number — Attack: added to a skill's power and set against the target's defence.
  • def: number — Defence: cuts the damage taken.
  • spd: number — Speed: who acts first.
  • lck: number — Luck: the chance of a critical hit.

BreakState

interface BreakState

The Break gauge of one fighter.

  • shield: number — Shield points left; 0 is broken.
  • max: number — The full gauge.
  • broken: boolean — True while broken: skips its next turn and takes brokenFactor.

Fighter

interface Fighter

A fighter in battle: plain data.

  • id: string — Unique among the fighters.
  • name: string — Display name.
  • side: Side — The side it fights on.
  • stats: BattleStats — Its stats.
  • hp: number — Health now; 0 is down.
  • mp: number — Magic now.
  • element: string — Its element.
  • brk: BreakState — Its Break gauge.
  • defending: boolean — Defending until its next turn: takes defendFactor.
  • haste: number — Rounds of haste left, counted down at the start of each of its own turns (Infinity for the rest of the battle); 0 is not hasted.
  • data: unknown — The game's own data.

FighterDef

interface FighterDef

A fighter going into battle.

  • id: string — Unique among the fighters.
  • name?: string — Display name. Default the id.
  • side: Side — The side it fights on.
  • stats: BattleStats — Its stats, gear and difficulty already applied.
  • hp?: number — Health going in. Default full.
  • mp?: number — Magic going in. Default full.
  • element?: string — The element it attacks with and defends as. Default none.
  • breaks?: number — Shield points of its Break gauge. Default 0 (never broken).
  • data?: unknown — Anything the game wants to keep with it (a monster definition, a hero id). Kept in snapshots when it is plain data.

HitResult

interface HitResult

What one attack does.

  • amount: number — HP removed.
  • crit: boolean — It was a critical.
  • factor: number — The element factor applied.
  • weak: boolean — The element exploited a weakness.

HitSpec

interface HitSpec

What an attack takes to resolve.

  • atk: number — Attacker's attack stat, gear included.
  • lck: number — Attacker's luck stat, for the crit roll.
  • power: number — Extra power from a skill; 0 for a plain attack.
  • element: string — The attack's element.
  • def: number — Target's defence stat, gear included.
  • element2: string — Target's element.
  • broken: boolean — Target is broken.
  • defending: boolean — Target is defending.
  • rolls: [number, number] — Two rolls in [0, 1): the spread and the crit.

Side

type Side = 'hero' | 'foe'

Which side a fighter is on.

SkillEffect

interface SkillEffect

A skill effect: who its one and all aim at, and what it does to the targets it reaches.

  • aim: 'foe' | 'ally' | 'self' — What a one skill of this effect aims at (all is that whole side; self makes it the user's side): opponents, allies or the user.
  • run(battle: Battle, user: Fighter, targets: Fighter[], skill: BattleSkill): void — Act on the targets through the battle's verbs (hit, heal, emit) or on the fighters' data.

TurnEntry

interface TurnEntry

A fighter as the turn order sees it.

  • id: string — Any id.
  • spd: number — Speed.
  • hero: boolean — Heroes act first on a tie.