Phaser AE

Room-clearing shooter runs

Pro packv3.0.0-12DArcade

A room-clearing roguelite shooter: walk to dodge, stand still to shoot, clear the room to open the door, and shape the build one skill draft at a time. Enemies, skills, rosters and bosses are data, with a first chapter built in, and the rooms are generated for you.

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

Released October 19, 2026

Room-clearing shooter runs: 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

roomRun

function roomRun(): Pack<{ roomRun: RoomRunApi }>

The room-run pack: game.roomRun, Archero's rules. No options.

Helpers

checkRoomRunSave

function checkRoomRunSave(save: unknown, options: RoomRunOptions = {}): string | null

Why a save cannot be restored into a run made with these options, or null when it can: the room must be one of the chapter's, the skills and choices the set's, every enemy one of the set's with the numbers it needs, every arrow and bullet whole, the field null or a place and time, and the random stream a real one. restore throws with this reason.

createRoomAutopilot

function createRoomAutopilot(game: RoomRunGame, options: RoomAutopilotOptions = {}): RoomAutopilot

A room run's autopilot. It only reads game.

createRoomRun

function createRoomRun(options: RoomRunOptions = {}): RoomRunGame

Start a run. Throws, with every fault, on options validateRoomRun refuses.

roomBosses

const roomBosses: RoomBoss[]

The built-in bosses.

roomEnemies

const roomEnemies: RoomEnemyDef[]

The built-in enemies.

roomRosters

const roomRosters: RoomRoster[]

Which enemies come where in the built-in chapter.

roomSet

const roomSet: RoomSet

The built-in set: Archero's first chapter.

roomSkills

const roomSkills: SkillDef[]

The built-in skills.

validateRoomRun

function validateRoomRun(options: RoomRunOptions = {}): string[]

Check a run's options and set. Empty when it can be played.

Types

EnemyShot

interface EnemyShot

How an enemy shoots.

  • every: number — Seconds between volleys.
  • pattern: 'aimed' | 'fan' | 'ring' | 'spiral' — The volley: one at the hero, a fan at the hero, a ring all round, or a ring that turns each volley.
  • count?: number — Bullets a volley. Default 1.
  • spread?: number — A fan's spread in radians. Default 0.6.
  • speed: number — Bullet speed, units a second.
  • damage: number — Damage a bullet.
  • warn?: number — Seconds it shows it is about to fire (and holds still). Default 0.5.

RoomArrow

interface RoomArrow

One of the hero's arrows in flight. Positions are in the layout's units, speeds in units a second.

  • id: number — Its id, unique in the run.
  • x: number — Where it is across the room.
  • y: number — Where it is down the room.
  • vx: number — Its speed across.
  • vy: number — Its speed down.
  • damage: number — Damage it does.
  • pierce: number — Enemies it may still pass through.
  • ricochet: number — Times it may still jump to another enemy.
  • bounce: number — Times it may still bounce off a wall.
  • hits: number[] — Ids of the enemies it has hit, which it does not hit again.
  • life: number — Seconds it has left.

RoomAutopilot

interface RoomAutopilot

A player that picks the way to walk and the choices.

  • next(): { x: number; y: number; choose: number | null } — The way to walk now (zero to stand and shoot), and a pick when a choice waits.

RoomAutopilotOptions

interface RoomAutopilotOptions

How the autopilot plays.

  • prefer?: string[] — Skill ids in the order it takes them. Default the arrow skills first.
  • wary?: number — How close it lets an enemy come before it backs off. Default 70: further off, it stands and shoots.

RoomBoss

interface RoomBoss

A boss and the room it holds.

  • room: number — The room, from 1, that the boss holds.
  • enemy: string — Id of the boss's RoomEnemyDef.

RoomBullet

interface RoomBullet

An enemy bullet in flight. Positions are in the layout's units, speeds in units a second.

  • id: number — Its id, unique in the run.
  • x: number — Where it is across the room.
  • y: number — Where it is down the room.
  • vx: number — Its speed across.
  • vy: number — Its speed down.
  • damage: number — Damage it does to the hero.
  • life: number — Seconds it has left.

RoomChoice

type RoomChoice = { kind: 'skill'; id: string } | { kind: 'heal'; amount: number }

A choice on offer.

RoomEnemy

interface RoomEnemy

An enemy in the room, as a save holds it. Positions are in the layout's units, times in seconds.

  • id: number — Its id, unique in the run.
  • def: number — Index of its definition in the set's enemies.
  • x: number — Its centre across the room.
  • y: number — Its centre down the room.
  • hp: number — Hit points left.
  • maxHp: number — Hit points at full health.
  • timer: number — Seconds until its next shot or move.
  • warn: boolean — It is showing that it is about to fire or dash.
  • dash: { vx: number; vy: number; t: number } | null — A dash under way: its velocity in units a second and the seconds left, or null.
  • goal: { x: number; y: number } | null — Where a wanderer is heading, or null.
  • goalT: number — Seconds until a wanderer picks a new goal.
  • spin: number — A spiral shooter's turn so far, radians.
  • flash: number — Seconds left of the flash after a hit.

RoomEnemyDef

interface RoomEnemyDef

An enemy.

  • id: string — Unique id; rosters, bosses and views refer to the enemy by it.
  • hp: number — Hit points; above 0.
  • speed: number — Units a second.
  • radius: number — Body radius in arena units; above 0. It sets how the enemy meets walls, arrows and the hero.
  • damage: number — Damage on contact.
  • move: 'chase' | 'wander' | 'still' | 'dash' | 'fly' — How it moves: after the hero round the walls, about at random, not at all, in dashes at the hero, or flying over walls.
  • shoot?: EnemyShot — How it shoots, if it does.
  • xp: number — Experience for the kill.

RoomEvent

type RoomEvent = | { kind: 'room'; room: number; boss: string | null } | { kind: 'clear'; room: number } | { kind: 'door' } | { kind: 'shoot' } | { kind: 'hit'; enemy: string; damage: number; crit: boolean } | { kind: 'kill'; enemy: string; x: number; y: number } | { kind: 'warn'; enemy: string } | { kind: 'fire'; enemy: string } | { kind: 'hurt'; damage: number; hp: number } | { kind: 'level'; level: number } | { kind: 'angel' } | { kind: 'choose'; choice: RoomChoice } | { kind: 'over' } | { kind: 'won' }

Something that happened.

RoomHero

interface RoomHero

The hero's numbers, before skills.

  • hp: number — Hit points. Default 600.
  • speed: number — Units a second. Default 150.
  • radius: number — Its size. Default 14.
  • attack: number — Damage an arrow. Default 50.
  • rate: number — Volleys a second. Default 1.3.
  • arrowSpeed: number — Arrow speed. Default 620.
  • grace: number — Seconds after a hit with no other. Default 0.6.

RoomRoster

interface RoomRoster

Which enemies come to rooms, from a room on.

  • from: number — From this room (1 is the first).
  • enemies: { id: string; weight?: number }[] — The enemies, by weight.
  • count: [number, number] — How many to a room.

RoomRunApi

interface RoomRunApi

What the room-run pack adds to a game: game.roomRun.

  • create: typeof createRoomRun — Start a run.
  • validate: typeof validateRoomRun — Check a run's options.
  • checkSave: typeof checkRoomRunSave — Why a save cannot be restored into a run of these options, or null.
  • set: typeof roomSet — The built-in set.
  • autopilot: typeof createRoomAutopilot — A player for an attract mode or a test.

RoomRunGame

interface RoomRunGame

A run.

  • readonly state: RoomRunState — The live run state. Read it; do not write it. view().state is a copy.
  • readonly layout: ArenaLayout — The room now.
  • readonly options: Readonly<{ hero: RoomHero; rooms: number; choices: number; set: RoomSet }> — The settings in force once defaults are applied: the hero, the chapter length, how many skills a level-up offers and the data set.
  • move(x: number, y: number): void — Which way to walk; zero stands still (and shoots).
  • choose(index: number): boolean — Take a choice by its place in state.choosing.
  • step(dt: number): void — Advance dt seconds, in fixed steps of 1/60 s. Paused while a choice waits.
  • view(): RoomView — What is where right now, as plain data a renderer can draw. The state in it is a copy.
  • drain(): RoomEvent[] — Remove and return the events since the last call, oldest first. Only the latest 512 are kept.
  • snapshot(): RoomRunSave — A plain-JSON copy of the whole run, for saving. The room's layout follows from the seed, so it is not stored.
  • restore(save: RoomRunSave): void — Replace the run with a snapshot taken from a run created with the same options. Throws room-run.restore: …, changing nothing, on a save checkRoomRunSave refuses for these options.

RoomRunOptions

interface RoomRunOptions

A run's options.

  • seed?: number — The seed. Default 1.
  • rooms?: number — Rooms in the chapter. Default 10.
  • set?: RoomSet — Enemies, skills, rosters and bosses. Default roomSet.
  • hero?: Partial<RoomHero> — The hero.
  • choices?: number — Skills offered on a level-up. Default 3.
  • xpFor?: (level: number) => number — Experience for each level, from level 1. Default 12, then 6 more a level.
  • arena?: Omit<ArenaOptions, 'seed' | 'start' | 'exit'> — How the rooms are generated (arena-layout's options, the seed and start and exit aside). Default 11 by 15 cells of 40.
  • angels?: boolean — An angel after each boss room but the last. Default true.

RoomRunSave

interface RoomRunSave

A run saved as plain JSON (the room's layout follows from the seed).

  • state: RoomRunState — The run state, as RoomRunGame.state has it.
  • enemies: Enemy[] — The enemies alive in the room.
  • arrows: Arrow[] — The hero's arrows in flight.
  • bullets: Bullet[] — Enemy bullets in flight.
  • queue: number[] — Run times, in seconds of state.time, at which the multishot skill's repeat volleys are still due.
  • serial: number — The last id handed out to an enemy, arrow or bullet; the next one is this plus 1.
  • grace: number — Seconds of immunity left after the hero's last hit.
  • fire: number — Seconds until the hero's next volley.
  • after: 'fight' | 'clear' — The phase to return to when the pending choice is taken: 'fight' or 'clear'.
  • field: { x: number; y: number; t: number } | null — Where the chasers' flow field was made (the hero's position then) and the seconds t until it is remade; null if there is none.
  • rng: RngState — The state of the run's random generator.

RoomRunState

interface RoomRunState

Where the run stands.

  • time: number — Seconds played; frozen while a choice waits and once the run is over or won.
  • room: number — The room, from 1.
  • rooms: number — Rooms in the chapter, from RoomRunOptions.rooms.
  • phase: 'fight' | 'clear' | 'choose' | 'over' | 'won' — Fighting; the room clear and its door open; a choice waiting (paused); the run lost or won.
  • x: number — Hero x in the room, in arena-layout units (the layout's cell is the cell size, 40 by default); 0 is the left wall.
  • y: number — Hero y in the room, in the same units as x; 0 is the top.
  • hp: number — Hit points left; the run is lost when it reaches 0.
  • maxHp: number — Hit points at full health; skills can raise it.
  • aim: number — The way it aims, radians.
  • level: number — Hero level, from 1.
  • xp: number — Experience gathered towards the next level; the surplus carries over when a level is gained.
  • nextXp: number — Experience needed for the next level, from RoomRunOptions.xpFor.
  • kills: number — Enemies killed this run.
  • skills: Record<string, number> — Skills taken, by id, with how many times.
  • choosing: RoomChoice[] | null — The choices on offer while phase is 'choose', otherwise null. Take one with choose(index).
  • pending: number — Level-ups still to choose.

RoomSet

interface RoomSet

A whole run's data.

  • enemies: RoomEnemyDef[] — Every enemy the set uses; ids must be unique.
  • skills: SkillDef[] — The skills a level-up can offer; at least one.
  • rosters: RoomRoster[] — Which enemies come to which rooms; at least one roster must start at room 1.
  • bosses: RoomBoss[] — Boss rooms; each holds one boss instead of a roster.

RoomView

interface RoomView

What is where.

  • state: RoomRunState — A copy of the run state.
  • layout: ArenaLayout — The current room's arena layout: walls, open cells, start and exit.
  • radius: number — The hero's radius, in the layout's units.
  • door: { x: number; y: number; open: boolean } — The exit door: its centre and whether it is open (the room is clear or the chapter won).
  • enemies: { id: number; kind: string; x: number; y: number; r: number; hp: number; maxHp: number; warn: boolean; boss: boolean; flash: boolean; seen: boolean }[] — seen: one of the hero's front arrows has a clear line to it, so the hero aims at the nearest such.
  • arrows: { id: number; x: number; y: number; angle: number }[] — The hero's arrows in flight; angle is the heading in radians.
  • bullets: { id: number; x: number; y: number; vx: number; vy: number; r: number }[] — Enemy bullets in flight; vx and vy are units per second and r is the bullet radius in layout units.

SkillDef

interface SkillDef

A skill.

  • id: string — Unique id; RoomRunState.skills is keyed by it.
  • name: string — Name shown when the skill is offered.
  • effect: SkillEffect — What the skill changes; see SkillEffect.
  • value: number — Per pick: arrows for front, multishot, diagonal, side, rear; hits for ricochet, pierce, bounce; a fraction for attack, speed, crit, maxHp, heal.
  • max?: number — How many times it can be picked. Default 1.

SkillEffect

type SkillEffect = | 'front' | 'multishot' | 'diagonal' | 'side' | 'rear' | 'ricochet' | 'pierce' | 'bounce' | 'attack' | 'speed' | 'crit' | 'maxHp' | 'heal'

What a skill does.