Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/smogon/pokemon-showdown-client/llms.txt

Use this file to discover all available pages before exploring further.

The Battle class is the central state machine of the Pokémon Showdown client engine. It owns all four possible Side objects, manages turn progression, weather, and game-type metadata, and drives the animation pipeline by dispatching protocol messages through runMajor. The Side class represents one player’s slot in the battle and holds the active roster, side conditions, and faint counters. The Pokemon class tracks every observable property of an individual Pokémon — HP, status, volatile effects, move memory, and the sprite handle used for animations.
Licensing: The Pokémon Showdown client as a whole is licensed under AGPLv3. The battle engine files (battle*.ts) alone carry the more permissive MIT license, making them safe to embed in standalone replay viewers without the copyleft requirement.

Type Exports

These types are re-exported from battle.ts for use across the codebase.
EffectState
any[] & { 0: ID }
A tuple-like array whose first element is always an ID string. Used to represent volatile, turn-status, and move-status effects attached to a Pokemon. Additional elements are effect-specific data.
export type EffectState = any[] & { 0: ID };
WeatherState
[name: string, minTimeLeft: number, maxTimeLeft: number]
Tracks a pseudo-weather effect on the field — its name and remaining duration bounds.
export type WeatherState = [name: string, minTimeLeft: number, maxTimeLeft: number];
HPColor
'r' | 'y' | 'g'
Colour of a Pokémon’s HP bar: 'g' (green, above 50%), 'y' (yellow, 21–50%), or 'r' (red, 20% and below).
PPState
number | [number, number]
PP used by a remembered move. A plain number means an exact value is known; a [min, max] tuple encodes a range when the exact value cannot be determined from the log.

Pokemon Class

Pokemon implements both PokemonDetails and PokemonHealth, encoding every client-visible attribute of a battle participant.
export class Pokemon implements PokemonDetails, PokemonHealth {
  name: string;
  speciesForme: string;
  ident: string;
  details: string;
  searchid: string;

  side: Side;
  slot: number;

  fainted: boolean;
  hp: number;
  maxhp: number;
  level: number;
  gender: Dex.GenderName;
  shiny: boolean;

  hpcolor: HPColor;
  moves: string[];
  ability: string;
  baseAbility: string;
  item: string;
  terastallized: string;
  teraType: string;
  moddedType: Dex.TypeName[];

  boosts: { [stat: string]: number };
  status: Dex.StatusName | 'tox' | '' | '???';
  statusStage: number;
  volatiles: { [effectid: string]: EffectState };
  turnstatuses: { [effectid: string]: EffectState };
  movestatuses: { [effectid: string]: EffectState };
  lastMove: string;

  moveTrack: [string, PPState][];
  statusData: { sleepTurns: number; toxicTurns: number };
  timesAttacked: number;
}

Key Properties

ident
string
Uniquely identifies the Pokémon within a message stream in the form "p1: Nickname" or "p2: Sparky". Empty between Team Preview and the first switch-in.
details
string
Human-readable details not included in ident: species, level (omitted at 100), gender (omitted if genderless), and shininess. Examples: "Mimikyu, L50, F", "Steelix, M, shiny".
hp / maxhp
number
Current and maximum HP. The client tracks these for calculating the HP bar width and damage ranges.
hpcolor
HPColor
Current HP bar colour as reported by the server: 'g' (healthy), 'y' (moderate), 'r' (critical).
status
Dex.StatusName | 'tox' | '' | '???'
Non-volatile status condition ID: 'par', 'psn', 'frz', 'slp', 'brn', 'tox' (badly poisoned), or '' (none). '???' is used for unknown status in old replays.
volatiles
{ [effectid: string]: EffectState }
Map of active volatile effects (e.g. confusion, Substitute). Each key is the effect’s ID; the value is an EffectState array.
turnstatuses
{ [effectid: string]: EffectState }
Effects that reset every turn, such as protection moves and Endure.
movestatuses
{ [effectid: string]: EffectState }
Effects that last until the Pokémon’s next move, such as Focus Energy and Charge.
moveTrack
[string, PPState][]
Ordered list of [moveName, ppUsed] pairs inferred from the battle log. Used to display the PP tracker in the battle panel without server-side data.

Pokemon Methods

Returns true if this Pokémon is currently in the active slot on its side.
isActive(): boolean
Returns the slot-qualified ident string, e.g. "p1a: Pikachu" for the first active slot. Slots are encoded as letters af.
getIdent(): string
// Returns e.g. "p1a: Pikachu"
Manage the volatiles map and synchronise with the scene layer for sprite effects.
addVolatile(volatile: ID, ...args: any[]): void
removeVolatile(volatile: ID): void
hasVolatile(volatile: ID): boolean
Same pattern as volatile management but for turnstatuses.
addTurnstatus(volatile: ID): void
removeTurnstatus(volatile: ID): void
clearTurnstatuses(): void
Same pattern as volatile management but for movestatuses.
addMovestatus(volatile: ID): void
removeMovestatus(volatile: ID): void
clearMovestatuses(): void
Records or updates a move entry in moveTrack. Handles Transform recursion guards. Automatically skips Struggle and starred (*-prefixed) transform copies.
rememberMove(moveName: string, pp?: PPState, recursionSource?: string): void
Parses an HP string from the protocol (e.g. "185/250" or "(42%)") and mutates this.hp, this.maxhp, and this.hpcolor. Returns a [delta, denominator, percentDelta] or [delta, denominator, percentDelta, oldnum, oldcolor] tuple, or null on failure.
healthParse(
  hpstring: string,
  parsedamage?: boolean,
  heal?: boolean
): [number, number, number] | [number, number, number, number, HPColor] | null
Converts a pixel-width HP bar reading to a [min, max] HP ratio range. Used for damage calculation in the log overlay.
static getPixelRange(pixels: number, color: HPColor | ''): [number, number]
Formats a [min, max] ratio pair (from getPixelRange) into a human-readable percentage string such as "36–42%".
static getFormattedRange(
  range: [number, number],
  precision: number,
  separator: string
): string
Resets all volatile state: restores base ability, clears boosts, volatiles, turn-statuses, move-statuses, and resets toxic/sleep counters appropriately for the current generation.
clearVolatile(): void

Side Class

Side represents one player slot in the battle. In doubles/triples/multi battles there can be up to four sides (p1p4).
export class Side {
  battle: Battle;
  name: string;
  id: string;
  sideid: SideID;           // 'p1' | 'p2' | 'p3' | 'p4'
  n: number;                // 0-indexed position
  isFar: boolean;           // true for the opponent from the viewer's perspective
  foe: Side;
  ally: Side | null;
  avatar: string;
  rating: string;
  totalPokemon: number;     // defaults to 6
  active: (Pokemon | null)[];
  lastPokemon: Pokemon | null;
  pokemon: Pokemon[];
  sideConditions: {
    [id: string]: [effectName: string, levels: number, minDuration: number, maxDuration: number];
  };
  faintCounter: number;
}

Side Properties

sideid
SideID
'p1' | 'p2' | 'p3' | 'p4' — derived from n via ['p1','p2','p3','p4'][n].
totalPokemon
number
Number of Pokémon on this side, defaults to 6. Updated when the server sends team size information.
pokemon
Pokemon[]
All Pokémon seen on this side so far. Populated as Pokémon are switched in or revealed during Team Preview.
active
(Pokemon | null)[]
Currently active Pokémon. Length matches the number of active slots (1 for singles, 2 for doubles, 3 for triples).
sideConditions
{ [id: string]: [string, number, number, number] }
Active side conditions such as Stealth Rock, Spikes, and Reflect. Each entry is [effectName, levels, minDuration, maxDuration].

Battle Class

Battle is the top-level orchestrator. It owns the step queue (the raw protocol message log), animation timing, and all state that spans both sides.
export class Battle {
  scene: BattleSceneStub;

  p1: Side;
  p2: Side;
  p3?: Side;
  p4?: Side;
  sides: Side[];
  mySide: Side;
  nearSide: Side;
  farSide: Side;

  turn: number;           // -1 before start, 0 after |start|, then 1, 2, ...
  ended: boolean;
  started: boolean;
  atQueueEnd: boolean;

  weather: ID;
  weatherTimeLeft: number;
  weatherMinTimeLeft: number;
  pseudoWeather: WeatherState[];

  gen: number;            // generation number (default 8, updated from |gen|)
  dex: ModdedDex;         // Dex instance, may be a modded dex for special formats
  tier: string;
  gameType: 'singles' | 'doubles' | 'triples' | 'multi' | 'freeforall' | 'rotation';

  stepQueue: string[];
  preemptStepQueue: string[];
  currentStep: number;
  seeking: number | null;

  myPokemon: ServerPokemon[] | null;
  myAllyPokemon: ServerPokemon[] | null;
  lastMove: string;

  id: string;
  roomid: string;
  isReplay: boolean;
  paused: boolean;
  mute: boolean;
  debug: boolean;

  rated: string | boolean;
  rules: { [ruleName: string]: 1 | undefined };
  speciesClause: boolean;
  reportExactHP: boolean;
  kickingInactive: number | boolean;
  totalTimeLeft: number;
  graceTimeLeft: number;
}

Constructor

new Battle(options?: {
  $frame?: JQuery;
  $logFrame?: JQuery;
  id?: ID;
  log?: string[] | string | null;
  paused?: boolean;
  isReplay?: boolean;
  debug?: boolean;
  subscription?: (state:
    'playing' | 'paused' | 'turn' | 'atqueueend' | 'callback' | 'ended' | 'error'
  ) => void;
  autoresize?: boolean;
})
$frame
JQuery
jQuery element for the battle animation canvas. Must be provided together with $logFrame. Omit both to create a headless (stub-scene) battle suitable for log parsing only.
$logFrame
JQuery
jQuery element for the battle text log. Required when $frame is provided.
log
string[] | string | null
Pre-loaded protocol lines. A newline-delimited string is split automatically. These populate stepQueue and begin replaying immediately unless paused is true.
paused
boolean
Start in a paused state. Useful for loading a replay without auto-playing.
subscription
function
Callback invoked on major state transitions: 'playing', 'paused', 'turn', 'atqueueend', 'callback', 'ended', 'error'.

Battle Methods

Appends a protocol line to stepQueue and, if the queue had previously reached its end, resumes stepping.
add(command?: string): void
Runs a command immediately (preemptively) for chat and timer messages, then also appends it to the queue so it plays in sequence. This is the mechanism behind real-time chat in a live battle.
instantAdd(command: string): void
Replaces the current subscription callback. The listener receives one of the state strings on each significant transition.
subscribe(listener: Battle['subscription']): void
reset() pauses playback and calls resetStep(). resetStep() rolls all battle state back to initial values, clears both sides, and restarts the step queue from position 0.
reset(): void
resetStep(): void
Fast-forwards or rewinds the replay to a specific turn number. seeking is set to the target turn while processing, then cleared.
seekTurn(turn: number, forceReset?: boolean): void
Rotates the near/far perspective to view the battle from a different side. Used in multi-battles to switch between allied players.
setViewpoint(sideid: SideID): void
Tears down the battle: removes resize listeners, destroys the scene, and nulls out all side references to prevent memory leaks.
destroy(): void

Supporting Interfaces

export interface PokemonDetails {
  details: string;
  name: string;
  speciesForme: string;
  level: number;
  shiny: boolean;
  gender: Dex.GenderName | '';
  ident: string;
  terastallized: string;
  searchid: string;
}
export interface PokemonHealth {
  hp: number;
  maxhp: number;
  hpcolor: HPColor | '';
  status: Dex.StatusName | 'tox' | '' | '???';
  fainted?: boolean;
}
Extends both PokemonDetails and PokemonHealth with full server-side data sent in |request| messages: unboosted stats, move IDs, base ability, held item, pokéball, and Tera Type.
export interface ServerPokemon extends PokemonDetails, PokemonHealth {
  condition: string;
  active: boolean;
  reviving: boolean;
  commanding: boolean;
  stats: { atk: number; def: number; spa: number; spd: number; spe: number };
  moves: string[];
  baseAbility: string;
  ability?: string;
  item: string;
  pokeball: string;
  teraType: string;
  terastallized: string;
}

Code Examples

import { Battle } from './battle';

// Minimal headless battle (no DOM required)
const battle = new Battle({
  paused: true,
  subscription: (state) => {
    if (state === 'turn') console.log('Now on turn', battle.turn);
    if (state === 'ended') console.log('Battle over');
  },
});

// Feed protocol lines one at a time
battle.add('|player|p1|Alice|dawn|1200');
battle.add('|player|p2|Bob|ethan|1100');
battle.add('|gen|9');
battle.add('|tier|[Gen 9] OU');
battle.add('|gametype|singles');
battle.add('|start');
battle.add('|switch|p1a: Gardevoir|Gardevoir, L50, F|250/250');
battle.add('|switch|p2a: Tyranitar|Tyranitar, L50, M|341/341');
battle.add('|turn|1');
battle.add('|move|p1a: Gardevoir|Moonblast|p2a: Tyranitar');
battle.add('|-damage|p2a: Tyranitar|241/341');
For a read-only replay viewer that does not need animation, pass no $frame/$logFrame to the constructor. The BattleSceneStub shim will absorb all rendering calls, letting you parse battle state from the log without any DOM dependency.

Build docs developers (and LLMs) love