The most accurate and complete multi-generational Pokémon damage calculator package.
@pkmn/dmg
is the spiritual successor of the @smogon/calc
library, designed from scratch to be
compatible with the @pkmn
ecosystem and based around a scalable
architecture familar to Pokémon Showdown developers. In addition to the improvements made to
architecture and correctness, @pkmn/dmg
features:
- sophisticated text parsing support and the ability to canonicalize and encode calculations
- generalized pre-computation state manipulation through the 'application' of effects
- comprehensive multi-hit support and KO chance calculation, enabling 'chained' calculations
- improved programmatic support for recoil, recovery, and crash results
- non-intrusive support for mods overriding data or effects
- extensive tests build on state-of-the-art multi-generational testing infrastructure
$ npm install @pkmn/dmg
Alternatively, as detailed below, if you are using @pkmn/dmg
in the browser and want a
convenient way to get started, simply depend on a transpiled and minified version via
unpkg (@pkmn/dex
and @pkmn/data
are required dependencies of @pkmn/dmg
):
<script src="https://unpkg.com/@pkmn/dex"></script>
<script src="https://unpkg.com/@pkmn/data"></script>
<script src="https://unpkg.com/@pkmn/dmg"></script>
@pkmn/dmg
's main API is the calculate
function which takes in State
and
returns a Result
.
@pkmn/dmg
is data-layer agnostic - thanks to its dependency on
@pkmn/data
it simply requires a Pokémon Showdown
compatible Dex
-type implementation to be provided to @pkmn/data
's Generations
constructor
(@pkmn/dex
is the recommended choice here, though
note that as it is fully featured it is ~4x the size of @smogon/calc/data
and certain applications
may wish to preprocess the JSON files to trim unnecessary fields).
State
's helper functions, State#createPokemon
and State#createMove
are the recommended ways
to initialize the input data structures required for calculate
- these functions provide a
convenient way to avoid having to specify all of the fields while also performing basic integrity
checking. Objects compatible with the State
interface can be provided instead, though this is
mostly relevant for applications which already have their own battle state representation
(eg. @pkmn/client
).
import {Dex} from '@pkmn/dex'
import {Generations} from '@pkmn/data';
import * as dmg from '@pkmn/dmg';
const gens = new Generations(Dex);
const gen = gens.get(4);
const result = dmg.calculate(
gen,
dmg.State.createPokemon(gen, 'Gengar', {item: 'Choice Specs', nature: 'Modest', evs: {spa: 252}}),
{
pokemon: dmg.State.createPokemon(gen, 'Blissey', {evs: {hp: 252, spd: 252}}),
sideConditions: {spikes: {level: 2}, stealthrock: {}},
}
dmg.State.createMove(gen, 'Focus Blast'),
{weather: 'Sandstorm', pseudoWeather: {}}
);
This can be further simplified by using the scoped inGen
helper:
const result = dmg.inGen(gens.get(4), ({calculate, Pokemon, Move}) =>
calculate(
Pokemon('Gengar', {item: 'Choice Specs', nature: 'Modest', evs: {spa: 252}}),
{
pokemon: Pokemon('Blissey', {evs: {hp: 252, spd: 252}}),
sideConditions: {spikes: {level: 2}, stealthrock: {}},
}
Move('Focus Blast'),
{weather: 'Sandstorm', pseudoWeather: {}}
);
);
Above is a more advanced example demonstrating how Side
or Field
conditions would be
specified, the common case looks more similar to the following:
const result = dmg.inGen(gens.get(4) , ({calculate, Pokemon, Move}) =>
calculate(
Pokemon('Gengar', {item: 'Choice Specs', nature: 'Modest', evs: {spa: 252}}),
Pokemon('Blissey', {evs: {hp: 252, spd: 252}})
Move('Focus Blast')
);
);
The Result
returned by calculate
contains information about damage rolls, recoil or
drain/recovery information, end of turn residual data, and detailed KO chance breakdowns, all
available in machine-friendly formats for programmatic usage (compared to @smogon/calc
, where less
indepth human-friendly text is provided). The familiar human-friendly output can be obtained as
well by encoding the Result
into the desired format.
The dmg
binary can be used to perform damage calculations via the command line.
// FIXME improve these to match actual output and encoding
dmg +1 252 SpA Gengar @ Choice Specs [Focus Blast] vs. 0 HP / 172+ SpD Blissey --gen=4
+1 252 SpA Choice Specs Gengar Focus Blast vs. 0 HP / 172+ SpD Blissey: 362-428 (55.6 - 65.7%) -- guaranteed 2HKO after Leftovers recovery
$ dmg gengar [focus blast] vs. blissey gen:6
252 SpA Life Orb Gengar Focus Blast vs. 252 HP / 4 SpD Blissey: 263-309 (36.8 - 43.2%) -- 98.7% chance to 3HKO after Leftovers recovery
$ dmg gen=3 mence @ CB [EQ] vs. cune @ lefties
252+ Atk Choice Band Salamence Earthquake vs. 252 HP / 252+ Def Suicune: 121-143 (29.9 - 35.3%) -- guaranteed 4HKO after Leftovers recovery
Like calc.pokemonshowdown.com, the CLI relies on predefined sets
and heuristics to minimize the amount of information that needs to be specified in order to perform
a calculation. The parsing documentation covers the syntax in more details. The
optional @pkmn/smogon
dependency must be installed
to run dmg
.
While not required, the first positional argument to dmg
can be the format ID (eg. gen7ou
or
gen8anythinggoes
) which will scope the sets from @pkmn/smogon
to be drawn from that particular
format (which is especially useful for VGC or Little Cup calculations).
The recommended way of using @pkmn/dmg
in a web browser is to configure your bundler
(Webpack, Rollup,
Parcel, etc) to minimize it and package it with the rest of your
application. If you do not use a bundler, a convenience index.umd.js
is included in the
package. You simply need to depend on ./node_modules/@pkmn/dmg/build/index.umd.js
in a
script
tag (which is what the unpkg shortcut above is doing), after which calc
will be
accessible as a global. You must also have a Generations
implementation provided, and it must be
loaded before before loading the calc:
<script src="./node_modules/@pkmn/dex/build/production.min.js"></script>
<script src="./node_modules/@pkmn/data/build/production.min.js"></script>
<script src="./node_modules/@pkmn/dmg/build/index.umd.js"></script>
@pkmn/dmg
's handling of state and the concept of 'appliers' and their apply
functions is
perhaps the largest innovation @pkmn/dmg
provides over previous damage calculators. @pkmn/dmg
's
appliers are a comphrensive generalization for the ad hoc convenience functionality provided by
existing calculators which contain things like toggles to turn 'on' an ability or buttons to boost
stats for moves like 'Geomancy' or 'Extreme Evoboost'.
Abilities/conditions/items/moves which have an effect can have their effect apply
-ed to modify the
State
which is then used to perform a damage calculation. This is useful for a UI, as a set with
a move like 'Swords Dance' can be turned into a convenience button to provide a +2 Attack boost when
clicked, or Knock Off's effect can be apply
-ed to remove a Pokémon's item for future calculations
etc. There are limitations to apply
- @pkmn/dmg
does not intend to embed a full simulator /
battle engine inside to be able to perfectly update its state. apply
is intended for convenience
purposes - @pkmn/dmg
aims to be accurate and comprehensive with respect to the State
it is
provided with, but does not guarantee apply
will always result in the exact State
that happened
in battle.
@pkmn/dmg
handles hits differently than previous calculators - each hit is run through the entire
damage formula (though certain optimizations may be detected) and in between hits, the context of
the calculation updates based on effects which may be apply
-ed after each hit. This design
naturally handles Parental Bond or multihit moves (including interactions like Stamina raising
Defense in between hits or the second hit of Parental Bond benefitting from Power Up Punch's Attack
boost) but also allows for abitrary moves to be chained together to compute the the result of a
series of attacks.
Chaining handles the common case of wanting to see what the results of a repeated Overheat or Draco
Meteor might be, but also covers things like Scizor U-turn into Gengar Focus Blast - any results
with the same target can be linked together and the expected KO chance of the joint Result
is
handled in exactly the same way 2 consecutive hits from eg. a Breloom's Bullet Seed are handled. At
the extreme, this design scales to handle scenarios like Gluttony Sitrus Berry kicking in after a
few hits or Defeatist activating after recoil drops the attacker into the requisite HP range.
Chained moves only deal with guaranteed scenarios - ie. effects are only apply
-ed between
moves if they are guaranteed to occur, either because they have a 100% chance of activating or the
ranges involved guarantee that a certain event would occur.
== TODO ==
- OHKO chance exact not approx
- pre and post EOT separated out
- rich breakdowns of all results - recoils, multi stages, etc
@pkmn
packages do not intend to ever provide first class support for mods (non-canonical data or
mechanics), however, @pkmn/dmg
was carefully designed to make it much more extensible for mods
than @smogon/calc
. Changes to @pkmn/dmg
's data can be accomplished via:
- the
override
method exposed, which allows for modifying or adding fields to existing data (this is effectively the same as theoverrides
parameters some of@smogon/calc
's constructors take) - exposing additional non-canonical data from
@pkmn/data
'sGenerations
class by providing its constructor with a customexists
function implementation (useful for National Dex or CAP) - wrapping
@pkmn/dex
and adding in additional data (cf.@pkmn/mods
)
Depending on the what your modifications entail you may not be able to make use of the convenience
factory methods State
provides as they perform some verification of fundamental Pokémon mechanics.
Note, however, that you can always build up a State
object without using these methods.
@pkmn/dmg
will only use the @pkmn/dex-types
fields it is aware of, so additional data fields
should not cause problems. However, if you wish to make use of any new fields or if you simply
wish to change the behavior of various mechanics, calculate
takes an optional handlers
parameter that allows you to extend or override the existing handler mechanics. You most likely
will wish to leverage the existing exported Handlers
object as a base.
If your use case requires more extensive modding capabilities (eg. being able to change around
the core damage flow), please open an issue to describe your use case. If there is sufficient
justification, core parts of the algorithm may be broken up and made moddable in the same way
Handlers
have been.
- the ultimate POKéMON CENTER - Peter O
- The Complete Damage Formula for Diamond & Pearl - X-Act, Peterko, Kaphotics
- The Complete Damage Formula for Black & White - Xfr, Bond697, Kaphotics, V4Victini
- A Complete Guide to the Damage Formula - DaWoblefet, based on work by OZY
- pret - disassembly of Gens 1-4
- Pokémon Showdown! - Guangcong Luo (Zarel) and contributors
- Long Form Damage Calculator - SadisticMystic
@pkmn/engine
- pkmn contributors- sulcalc - sulcata
- relicalc - Corvimae
@smogon/calc
- Honko, Austin, and contributors
This package is distributed under the terms of the MIT License.