@pkmn/engine 0.1.0-dev.d716e940 → 0.1.0-dev.d952b7c9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -3
- package/README.md +97 -59
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +43 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +96 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon.d.ts +2 -0
- package/build/pkg/addon.js +35 -57
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +26 -4
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +16 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +24 -10
- package/build/pkg/index.js +15 -10
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +123 -102
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +36 -68
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +128 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +132 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +286 -131
- package/package.json +73 -31
- package/src/bin/install-pkmn-engine +39 -24
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +19 -31
- package/src/lib/common/debug.zig +30 -16
- package/src/lib/common/js.zig +120 -0
- package/src/lib/common/optional.zig +71 -0
- package/src/lib/common/options.zig +13 -0
- package/src/lib/common/protocol.zig +911 -367
- package/src/lib/common/rational.zig +447 -0
- package/src/lib/common/rng.zig +31 -22
- package/src/lib/common/util.zig +61 -0
- package/src/lib/gen1/calc.zig +950 -0
- package/src/lib/gen1/chance.zig +1169 -0
- package/src/lib/gen1/data/moves.zig +202 -190
- package/src/lib/gen1/data/species.zig +5 -5
- package/src/lib/gen1/data/types.zig +10 -10
- package/src/lib/gen1/data.zig +92 -79
- package/src/lib/gen1/helpers.zig +141 -28
- package/src/lib/gen1/mechanics.zig +1118 -773
- package/src/lib/gen2/calc.zig +161 -0
- package/src/lib/gen2/chance.zig +902 -0
- package/src/lib/gen2/data/items.zig +14 -20
- package/src/lib/gen2/data/moves.zig +664 -570
- package/src/lib/gen2/data/species.zig +2 -2
- package/src/lib/gen2/data/types.zig +137 -34
- package/src/lib/gen2/data.zig +125 -86
- package/src/lib/gen2/generated.zig +806 -0
- package/src/lib/gen2/helpers.zig +54 -32
- package/src/lib/gen2/mechanics.zig +3564 -15
- package/src/lib/node.zig +131 -0
- package/src/lib/pkmn.zig +81 -5
- package/src/lib/wasm.zig +13 -0
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +351 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/binding/node.zig +0 -229
- package/src/lib/binding/wasm.zig +0 -10
- package/src/lib/gen1/README.md +0 -812
- package/src/lib/gen2/README.md +0 -309
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen1/README.md
DELETED
|
@@ -1,812 +0,0 @@
|
|
|
1
|
-
# Pokémon Generation I: RBY
|
|
2
|
-
|
|
3
|
-
This document exists to describe the design of pkmn's Generation I ("RBY") engine. A high level
|
|
4
|
-
overview of the project's design can be found in the [top level design
|
|
5
|
-
document](../../../docs/DESIGN.md). The Generation I engine is implemented and tested across the
|
|
6
|
-
following files:
|
|
7
|
-
|
|
8
|
-
- [`data.zig`](data.zig) ([`data`](data)): contains definitions of all of the data structures used
|
|
9
|
-
by the engine, described in detail [below](#data-structures)
|
|
10
|
-
- [`helpers.zig`](helpers.zig): helpers used to construct complex data types with
|
|
11
|
-
sensible defaults (internally used by tests and tools)
|
|
12
|
-
- [`mechanics.zig`](mechanics.zig): code which manipulates the data structures in order to implement
|
|
13
|
-
the mechanics of the game
|
|
14
|
-
- [`test.zig`](test.zig): unit tests for `mechanics.zig` (the code is also tested by [integration
|
|
15
|
-
tests](../../../docs/TESTING.md) at a higher level)
|
|
16
|
-
|
|
17
|
-
The engine also relies on the [data types](../common/data.zig), [protocol](../common/protocol.zig),
|
|
18
|
-
and [RNG](../common/rng.zig) logic which is shared across generations and lives in
|
|
19
|
-
[`lib/common`](../common).
|
|
20
|
-
|
|
21
|
-
Covered below is a description of the [**data structures**](#data-structures) used including the
|
|
22
|
-
[**information**](#information) they contain and their [**layout**](#layout), a list of
|
|
23
|
-
[**bugs**](#bugs) that are introduced by `-Dshowdown` for Pokémon Showdown compatibility, an
|
|
24
|
-
[**RNG**](#rng) table, high level [**details**](#details) about the control flow of the engine and a
|
|
25
|
-
list of additional reference [**resources**](#resources).
|
|
26
|
-
|
|
27
|
-
## Data Structures
|
|
28
|
-
|
|
29
|
-
The following information is required to simulate a Generation I Pokémon battle:
|
|
30
|
-
|
|
31
|
-
| pkmn | Pokémon Red (pret) | Pokémon Showdown |
|
|
32
|
-
| --------------------------------------- | ---------------------------------- | -------------------------------------- |
|
|
33
|
-
| `battle.seed` | `Random{Add,Sub}` | `battle.seed` |
|
|
34
|
-
| `battle.turn` | - | `battle.turn` |
|
|
35
|
-
| `battle.last_damage` | `Damage` | `battle.lastDamage` |
|
|
36
|
-
| `side.{active,pokemon}` | `PlayerMonNumber`/`BattleMon` | `side.active` |
|
|
37
|
-
| `side.team` | `PartyMons` | `side.pokemon` |
|
|
38
|
-
| `side.last_used_move` | `PlayerUsedMove` | `pokemon.lastMove` |
|
|
39
|
-
| `side.last_selected_move` | `PlayerSelectedMove` | `side.lastSelectedMove` |
|
|
40
|
-
| `battle.last_moves.{p1,p2}_index` | `PlayerMoveListIndex` | - |
|
|
41
|
-
| `battle.last_moves.{p1,p2}_counterable` | - | `side.lastMove` |
|
|
42
|
-
| `side.order` | - | `pokemon.position` |
|
|
43
|
-
| `{pokemon,active}.moves` | `{party,battle}_struct.{Moves,PP}` | `pokemon.{baseMoveSlots,moveSlots}` |
|
|
44
|
-
| `pokemon.hp` | `{party,battle}_struct.HP` | `pokemon.hp` |
|
|
45
|
-
| `{pokemon,active}.status` | `{party,battle}_struct.Status` | `pokemon.status` |
|
|
46
|
-
| `{pokemon,active}.level` | `PlayerMonUnmodifiedLevel` | `pokemon.level` |
|
|
47
|
-
| `pokemon.species` | `party_struct.Species` | `pokemon.baseSpecies` |
|
|
48
|
-
| `pokemon.stats` | `box_struct.Stats` | `pokemon.baseStoredStats` |
|
|
49
|
-
| `active.stats` | `battle_struct.Stats` | `pokemon.modifiedStats` |
|
|
50
|
-
| `volatiles.transform` | `PlayerMonUnmodified*` | `pokemon.storedStats` |
|
|
51
|
-
| `active.species` | `battle_struct.Species` | `pokemon.species` |
|
|
52
|
-
| `{pokemon,active}.types` | `{party,battle}_struct.Type` | `pokemon.types` |
|
|
53
|
-
| `active.boosts` | `PlayerMon*Mod` | `pokemon.boosts` |
|
|
54
|
-
| `active.volatiles` | `PlayerBattleStatus{1,2,3}` | `pokemon.volatiles` |
|
|
55
|
-
| `volatiles.state` | `PlayerBideAccumulatedDamage` | `volatiles.bide.totalDamage` |
|
|
56
|
-
| `volatiles.attacks` | `PlayerNumAttacksLeft` | `volatiles.{bide,lockedmove}.duration` |
|
|
57
|
-
| `volatiles.confusion` | `PlayerConfusedCounter` | `volatiles.confusion.duration` |
|
|
58
|
-
| `volatiles.toxic` | `PlayerToxicCounter` | `volatiles.residualdmg.counter` |
|
|
59
|
-
| `volatiles.substitute` | `PlayerSubstituteHP` | `volatiles.substitute.hp` |
|
|
60
|
-
| `volatiles.disabled_{move,duration}` | `PlayerDisabledMove{,Number}` | `volatiles.disable.{move,time}` |
|
|
61
|
-
|
|
62
|
-
- Pokémon Showdown [does not implement the correct Generation I RNG](#rng) and as such its `seed` is
|
|
63
|
-
different
|
|
64
|
-
- `battle.turn` only needs to be tracked in order to be compatible with the Pokémon Showdown
|
|
65
|
-
protocol
|
|
66
|
-
- Pokémon Showdown tracks several last used move variables (`Battle.lastMove`, `Side.lastMove`, and
|
|
67
|
-
`Pokemon.lastMove`), none of which accurately match the `PlayerUsedMove` variable from the
|
|
68
|
-
cartridge (`Side.lastMove` should be what is used, but is used by Pokémon Showdown only for
|
|
69
|
-
Counter and is not set and cleared in the correct locations. `Pokemon.lastMove` matches
|
|
70
|
-
`PlayerUsedMove` more often so is what the engine attempts to model, despite the implications for
|
|
71
|
-
Counter)
|
|
72
|
-
- `battle.last_moves` doesn't map precisely to cartridge concepts because it is used to track desyncs
|
|
73
|
-
which instead are represented on the cartridge as discrepancies between the two separate battle states
|
|
74
|
-
that exist in the link battle.
|
|
75
|
-
- Pokémon Showdown does not implement the [partial-trapping move Mirror Move
|
|
76
|
-
glitch](https://glitchcity.wiki/Partial_trapping_move_Mirror_Move_link_battle_glitch) and as such
|
|
77
|
-
does not need to keep track of a player's last selected move index (`PlayerMoveListIndex`)
|
|
78
|
-
- Battle results (win, lose, draw) and request states are communicated via the return value of
|
|
79
|
-
`Battle.update`
|
|
80
|
-
- Nicknames (`BattleMonNick`/`pokemon.name`) are not handled by the pkmn engine as they are expected
|
|
81
|
-
to be handled by driver code if required
|
|
82
|
-
- Pokémon Red's `LastSwitchInEnemyMonHP`, `InHandlePlayerMonFainted`, `PlayerNumHits`,
|
|
83
|
-
`PlayerMonMinimized`, and `Move{DidntMiss,Missed}` are relevant for messaging/UI only
|
|
84
|
-
- Pokémon Red's `TransformedEnemyMonOriginalDVs` is only relevant for the [Transform DV manipulation
|
|
85
|
-
glitch](https://pkmn.cc/bulba/Transform_glitches#Transform_DV_manipulation_glitch)
|
|
86
|
-
- Pokémon Red's `PlayerMove*` tracks information that is stored in the `Move` class
|
|
87
|
-
- Pokémon Red's `PlayerStatsToDouble`/`PlayerStatsToHalve` are constants which are always 0
|
|
88
|
-
- pkmn does not store the DVs/stat experience of Pokémon as they are expected to already be
|
|
89
|
-
accounted for in the `Pokemon` `stats` and never need to be referenced in-battle (though a `DVs`
|
|
90
|
-
struct exists to simplify generating legal test data)
|
|
91
|
-
- Instead of storing unmodified stats like Pokémon Red or Pokémon Showdown, pkmn simply tracks the
|
|
92
|
-
identity of the Pokémon that has been transformed into in the `active.volatiles.transform` field
|
|
93
|
-
- pkmn uses `volatiles.state` for total accumulated damage for Bide but also for implementing
|
|
94
|
-
accuracy overwrite mechanics for certain moves (this glitch is present on the device but is not
|
|
95
|
-
correctly implemented by Pokémon Showdown currently)
|
|
96
|
-
|
|
97
|
-
### `Battle` / `Side`
|
|
98
|
-
|
|
99
|
-
`Battle` and `Side` are analogous to the classes of the
|
|
100
|
-
[same](https://github.com/smogon/pokemon-showdown/blob/master/sim/battle.ts)
|
|
101
|
-
[name](https://github.com/smogon/pokemon-showdown/blob/master/sim/side.ts) in Pokémon Showdown and
|
|
102
|
-
store general information about the battle. Unlike in Pokémon Showdown there is a distinction
|
|
103
|
-
between the data structure for the "active" Pokémon and its party members (see below).
|
|
104
|
-
|
|
105
|
-
Due to layout constraints, details about the last moves for a side are stored in what would
|
|
106
|
-
otherwise be the padding bytes of`Battle` (the `last_moves` field) instead of in `Side`.
|
|
107
|
-
|
|
108
|
-
### `Pokemon` / `ActivePokemon`
|
|
109
|
-
|
|
110
|
-
Similar to the cartridge, in order to save space different information is stored depending on
|
|
111
|
-
whether a [Pokémon](https://pkmn.cc/bulba/Pok%c3%a9mon_data_structure_%28Generation_I%29) is
|
|
112
|
-
actively participating in battle vs. is switched out (pret's [`battle_struct` vs.
|
|
113
|
-
`party_struct`](https://pkmn.cc/pokered/macros/ram.asm)). In Pokémon Showdown, all the Pokémon in a
|
|
114
|
-
battle are represented by the same
|
|
115
|
-
[`Pokemon`](https://github.com/smogon/pokemon-showdown/blob/master/sim/pokemon.ts) class, and static
|
|
116
|
-
party information is saved in fields beginning with "`stored`" or "`base`".
|
|
117
|
-
|
|
118
|
-
#### `MoveSlot`
|
|
119
|
-
|
|
120
|
-
A `MoveSlot` is a data-type for a `(move, current pp)` pair. A pared down version of Pokémon
|
|
121
|
-
Showdown's `Pokemon.moveSlot`, it also stores data from the cartridge's `battle_struct::Move` macro
|
|
122
|
-
and can be used to replace the `PlayerMove*` data. Move PP is stored as a full byte instead of how
|
|
123
|
-
the cartridge (`battle_struct::PP`) stores it (6 bits for current PP and the remaining 2 bits used
|
|
124
|
-
to store the number of applied PP Ups). PP Up bits do not actually need to be stored on move slot
|
|
125
|
-
as max PP is never relevant in Generation I.
|
|
126
|
-
|
|
127
|
-
#### `Status`
|
|
128
|
-
|
|
129
|
-
Bitfield representation of a Pokémon's major [status
|
|
130
|
-
condition](https://pkmn.cc/bulba/Status_condition), mirroring how it is stored on the cartridge. A
|
|
131
|
-
value of `0x00` means that the Pokémon is not affected by any major status, otherwise the lower 3
|
|
132
|
-
bits represent the remaining duration for Sleep. Other status are denoted by the presence of
|
|
133
|
-
individual bits - at most one status should be set at any given time.
|
|
134
|
-
|
|
135
|
-
In Generation I & II, the "badly poisoned" status (Toxic) is instead treated as a volatile (see
|
|
136
|
-
below), so the upper most bit of `Status` is instead used to track state required to implement Pokémon
|
|
137
|
-
quirks:
|
|
138
|
-
|
|
139
|
-
- when combined with a valid sleep duration (or 0), it indicates that the `SLP` status was
|
|
140
|
-
self-inflicted (required in order to implement Pokémon Showdown's "Sleep Clause Mod")
|
|
141
|
-
- when combined with `PSN` it indicates that the Pokémon is actually badly poisoned (required in
|
|
142
|
-
order to send the same incorrect protocol messages as Pokémon Showdown - the Toxic volatile
|
|
143
|
-
alone is not sufficient in compatibility mode because it gets lost on switch)
|
|
144
|
-
|
|
145
|
-
#### `Volatiles`
|
|
146
|
-
|
|
147
|
-
Active Pokémon can have ["volatile" status
|
|
148
|
-
conditions](https://pkmn.cc/bulba/Status_condition#Volatile_status) (called ["battle
|
|
149
|
-
status"](https://pkmn.cc/pokered/constants/battle_constants.asm#L73) bits in pret), all of which are
|
|
150
|
-
boolean flags that are cleared when the Pokémon faints or switches out:
|
|
151
|
-
|
|
152
|
-
| pkmn | Pokémon Red (pret) | Pokémon Showdown |
|
|
153
|
-
| -------------- | -------------------------- | ------------------ |
|
|
154
|
-
| `Bide` | `STORING_ENERGY` | `bide` |
|
|
155
|
-
| `Thrashing` | `TRASHING_ABOUT` | `lockedmove` |
|
|
156
|
-
| `MultiHit` | `ATTACKING_MULTIPLE_TIMES` | `Move#multihit` |
|
|
157
|
-
| `Flinch` | `FLINCHED` | `flinch` |
|
|
158
|
-
| `Charging` | `CHARGING_UP` | `twoturnmove` |
|
|
159
|
-
| `Binding` | `USING_TRAPPING_MOVE` | `partiallytrapped` |
|
|
160
|
-
| `Invulnerable` | `INVULNERABLE` | `Move#onLockMove` |
|
|
161
|
-
| `Confusion` | `CONFUSED` | `confusion` |
|
|
162
|
-
| `Mist` | `PROTECTED_BY_MIST` | `mist` |
|
|
163
|
-
| `FocusEnergy` | `GETTING_PUMPED` | `focusenergy` |
|
|
164
|
-
| `Substitute` | `HAS_SUBSTITUTE_UP` | `substitute` |
|
|
165
|
-
| `Recharging` | `NEEDS_TO_RECHARGE` | `mustrecharge` |
|
|
166
|
-
| `Rage` | `USING_RAGE` | `rage` |
|
|
167
|
-
| `LeechSeed` | `SEEDED` | `leechseed` |
|
|
168
|
-
| `Toxic` | `BADLY_POISONED` | `toxic` |
|
|
169
|
-
| `LightScreen` | `HAS_LIGHT_SCREEN_UP` | `lightscreen` |
|
|
170
|
-
| `Reflect` | `HAS_REFLECT_UP` | `reflect` |
|
|
171
|
-
| `Transform` | `TRANSFORMED` | `transform` |
|
|
172
|
-
|
|
173
|
-
[Bide](https://pkmn.cc/bulba/Bide_(move)) (damage),
|
|
174
|
-
[Substitute](https://pkmn.cc/bulba/Substitute_(move)) (substitute HP),
|
|
175
|
-
[Confusion](https://pkmn.cc/bulba/Confusion_(status_condition)) (duration),
|
|
176
|
-
[Toxic](https://pkmn.cc/bulba/Toxic_(move)) (counter),
|
|
177
|
-
[Transform](https://pkmn.cc/bulba/Transform_(move)) (identity), and
|
|
178
|
-
[Disable](https://pkmn.cc/bulba/Disable_(move)) (move and duration), and multi-hit attacks all
|
|
179
|
-
require additional information that is also stored in the `Volatiles` structure. `MultiHit` is not
|
|
180
|
-
strictly required to be stored as it should always be 0 after updates barring errors, however it is
|
|
181
|
-
convenient to maintain as it maps neatly to the cartridge and is effectively "free" space-wise due
|
|
182
|
-
to padding.
|
|
183
|
-
|
|
184
|
-
The `state` field of `Volatiles` is effectively treated as a union:
|
|
185
|
-
|
|
186
|
-
- if `volatiles.Bide` is set, `volatiles.data.state` reflects the total accumulated Bide damage
|
|
187
|
-
- otherwise, `volatiles.data.state` reflects the last computed move accuracy (required in order to
|
|
188
|
-
implement the [Rage and Thrash / Petal Dance accuracy
|
|
189
|
-
bug](https://www.youtube.com/watch?v=NC5gbJeExbs))
|
|
190
|
-
|
|
191
|
-
#### `Stats` / `Boosts`
|
|
192
|
-
|
|
193
|
-
[Stats](https://pkmn.cc/bulba/Stat) and [boosts (stat
|
|
194
|
-
modifiers)](https://pkmn.cc/bulba/Stat#Stat_modifiers) are stored logically, with the exception that
|
|
195
|
-
boosts should always range from `-6`...`6` instead of `1`...`13` as on the cartridge.
|
|
196
|
-
|
|
197
|
-
### `Move` / `Move.Data`
|
|
198
|
-
|
|
199
|
-
`Move` serves as an identifier for a unique [Pokémon move](https://pkmn.cc/bulba/Move) that can be
|
|
200
|
-
used to retrieve `Move.Data` with information regarding base power, accuracy, and type. As covered
|
|
201
|
-
above, PP information isn't strictly necessary in Generation I so is dropped. `Move.None` exists as
|
|
202
|
-
a special sentinel value to indicate `null`. Move PP data is only included for testing and is not
|
|
203
|
-
necessary for the actual engine implementation.
|
|
204
|
-
|
|
205
|
-
In order to workaround various [Pokémon Showdown bugs](#bugs) and to support its protocol in logs,
|
|
206
|
-
additional information is stored in `Move.Data` (`targets`) about what Pokémon Showdown believes the
|
|
207
|
-
`Move.Target` to be (despite the concept of targeting not existing until Generation III when Double
|
|
208
|
-
battles were introduced). More specifically, a move's "targeting" status is required in various
|
|
209
|
-
places to determine which protocol messages to print.
|
|
210
|
-
|
|
211
|
-
| pkmn | Pokémon Showdown |
|
|
212
|
-
| ----------- | ---------------- |
|
|
213
|
-
| `AllOthers` | `allAdjacent` |
|
|
214
|
-
| (`Self`) | `allyTeam` |
|
|
215
|
-
| `Any` | `any` |
|
|
216
|
-
| `Other` | `normal` |
|
|
217
|
-
| `RandomFoe` | `randomNormal` |
|
|
218
|
-
| `Depends` | `scripted` |
|
|
219
|
-
| `Self` | `self` |
|
|
220
|
-
|
|
221
|
-
### `Species` / `Species.Data`
|
|
222
|
-
|
|
223
|
-
`Species` just serves as an identifier for a unique [Pokémon
|
|
224
|
-
species](https://pkmn.cc/bulba/Pokémon_species_data_structure_(Generation_I)) as the base stats
|
|
225
|
-
of a species are already accounted for in the computed stats in the `Pokemon` structure and nothing
|
|
226
|
-
in battle requires these to be recomputed. Similarly, Type is unnecessary to include as it is also
|
|
227
|
-
already present in the `Pokemon` struct. `Species.None` exists as a special sentinel value to
|
|
228
|
-
indicate `null`. `Species.Data` is only included for testing and is not necessary for the actual
|
|
229
|
-
engine implementation, outside of `Species.CHANCES` which is required as the base speed / 2 of the
|
|
230
|
-
species is necessary for computing critical hit probability.
|
|
231
|
-
|
|
232
|
-
### `Type` / `Types` / `Effectiveness`
|
|
233
|
-
|
|
234
|
-
The [Pokémon types](https://pkmn.cc/bulba/Type) are enumerated by `Type`. `Types` represents a tuple
|
|
235
|
-
of 2 types, but due to limitations in Zig this can't be represented as a `[2]Type` array and thus
|
|
236
|
-
instead takes the form of a packed struct. `Effectiveness` serves as an enum for tracking a moves
|
|
237
|
-
effectiveness - like the cartridge, effectiveness is stored as `0`, `5`, `10`, and `20`
|
|
238
|
-
(technically only a 2-bit value is required, but as with `Types` Zig only allows a minimum of a byte
|
|
239
|
-
to be stored at each address of an array).
|
|
240
|
-
|
|
241
|
-
The 'precedence' of the various type matchups matters beyond just the [dual-type damage
|
|
242
|
-
misinformation
|
|
243
|
-
glitch](https://pkmn.cc/bulba/List_of_glitches_(Generation_I)#Dual-type_damage_misinformation) -
|
|
244
|
-
type effectiveness modifiers are applied based on the (haphazard) ordering of the effectiveness
|
|
245
|
-
table as opposed to first applying modifiers for a species's first type and then second type. The
|
|
246
|
-
engine maintains a `Type.PRECEDENCE` table with just the matchups that are relevant in game (certain
|
|
247
|
-
type combinations cannot crop up with the limited Generation I species pool and as such are pruned
|
|
248
|
-
for efficiency) and only looks up precedence when necessary to minimize expensive searches. Pokémon
|
|
249
|
-
Showdown does not implement type precedence.
|
|
250
|
-
|
|
251
|
-
## Information
|
|
252
|
-
|
|
253
|
-
The information of each field (in terms of [bits of
|
|
254
|
-
entropy](https://en.wikipedia.org/wiki/Entropy_(information_theory))) is as follows:
|
|
255
|
-
|
|
256
|
-
| Data | Range | Bits | | Data | Range | Bits |
|
|
257
|
-
| --------------- | --------- | ---- | --- | ----------------- | -------- | ---- |
|
|
258
|
-
| **seed** | 0...255 | 8 | | **turn** | 1...1000 | 10 |
|
|
259
|
-
| **team index** | 1...6 | 3 | | **move index** | 1...4 | 2 |
|
|
260
|
-
| **species** | 1...151 | 8 | | **move** | 1...165 | 8 |
|
|
261
|
-
| **stat** | 1...999 | 10 | | **boost** | 0...13 | 4 |
|
|
262
|
-
| **level** | 1...100 | 7 | | **volatiles** | *17* | 17 |
|
|
263
|
-
| **bide** | 0...65635 | 16 | | **substitute** | 0...179 | 8 |
|
|
264
|
-
| **confusion** | 0...5 | 3 | | **toxic** | 0...31 | 5 |
|
|
265
|
-
| **multi hits** | 0...5 | 3 | | **base power** | 0...40 | 6 |
|
|
266
|
-
| **base PP** | 1...8 | 3 | | **PP Ups** | 0...3 | 2 |
|
|
267
|
-
| **PP** | 0...64 | 7 | | **HP / damage** | 0...704 | 10 |
|
|
268
|
-
| **status** | 0...13 | 4 | | **effectiveness** | 0...3 | 2 |
|
|
269
|
-
| **type** | 0...15 | 4 | | **accuracy** | 6...20 | 4 |
|
|
270
|
-
| **disabled** | 0...8 | 4 | | **DVs** | 0...15 | 4 |
|
|
271
|
-
| **move effect** | 0..66 | 7 | | **attacks** | 0..4 | 3 |
|
|
272
|
-
| **crit chance** | 7..65 | 6 | | **transform** | 0..15 | 4 |
|
|
273
|
-
| **target** | 0..4 | 3 | | | | |
|
|
274
|
-
|
|
275
|
-
From this we can determine the minimum bits[^1] required to store each data structure to determine
|
|
276
|
-
how much overhead the representations above have after taking into consideration [alignment &
|
|
277
|
-
padding](https://en.wikipedia.org/wiki/Data_structure_alignment) and
|
|
278
|
-
[denormalization](https://en.wikipedia.org/wiki/Denormalization):
|
|
279
|
-
|
|
280
|
-
- **`Pokemon`**: 5× stats (`50`) + 4× move slot (`60`) + HP (`10`) + status (`4`) + species (`8`) +
|
|
281
|
-
types (`8`) + level (`7`)
|
|
282
|
-
- **`ActivePokemon`**: 4× stats (`40`) + 4× move slot (`60`) + 6× boosts (`24`) + volatile data
|
|
283
|
-
(`35`) + volatiles (`17`) + species (`8`) + types (`8`) + disabled (`6`) + transform (`4`)
|
|
284
|
-
- the active Pokémon's stats/species/move slots/types may change in the case of Transform
|
|
285
|
-
- the active Pokémon's types may change due to Conversion
|
|
286
|
-
- the active Pokémon's level and current and max HP can always be referred to the `Pokemon`
|
|
287
|
-
struct
|
|
288
|
-
- **`Side`**: `ActivePokemon` + 6× `Pokemon` + active (`3`) + last used (`8`) + last selected
|
|
289
|
-
(`8`)
|
|
290
|
-
- `order` does not need to be stored as the party can always be rearranged as switches occur
|
|
291
|
-
- **`Battle`**: 6× `Side` + seed (10× `8` + `4`) + turn (`10`) + last damage (`10`)
|
|
292
|
-
- **`Type.CHART`**: attacking types (`15`) × defending types (`15`) × effectiveness (`2`)[^2]
|
|
293
|
-
- **`Type.PRECEDENCE`**: 29× attacking type (`4`) + defending type (`4`) pairs
|
|
294
|
-
- **`Moves.DATA`**: 165× base power (`6`) + effect (`7`) + accuracy (`4`) + type (`4`) + target
|
|
295
|
-
(`3`)
|
|
296
|
-
- **`Species.CHANCES`**: 151× crit chance (`6`)
|
|
297
|
-
|
|
298
|
-
| Data | Actual bits | Minimum bits | Overhead |
|
|
299
|
-
| ----------------- | ----------- | ------------ | -------- |
|
|
300
|
-
| `Pokemon` | 192 | 147 | 30.6% |
|
|
301
|
-
| `ActivePokemon` | 256 | 202 | 26.7% |
|
|
302
|
-
| `Side` | 1472 | 1056 | 33.5% |
|
|
303
|
-
| `Battle` | 3088 | 2216 | 33.7% |
|
|
304
|
-
| `Type.CHART` | 1800 | 450 | 300.0% |
|
|
305
|
-
| `Type.PRECEDENCE` | 232 | 232 | 0.0% |
|
|
306
|
-
| `Moves.DATA` | 5280 | 3960 | 33.3% |
|
|
307
|
-
| `Species.CHANCES` | 1208 | 906 | 33.3% |
|
|
308
|
-
|
|
309
|
-
In the case of `Type.CHART`/`Type.PRECEDENCE`/`Moves.DATA`/`Species.CHANCES`, technically only the
|
|
310
|
-
values which are used by any given simulation are required, which could be as low as 1 in both
|
|
311
|
-
circumstances (e.g. all Normal Pokémon each only using the single move Tackle), though taking into
|
|
312
|
-
consideration the worst case all Pokémon types are required and 48 moves. The `Moves.DATA` array
|
|
313
|
-
could be eliminated and instead the `Move` data actually required by each `Pokemon` could be placed
|
|
314
|
-
beside the `MoveSlot`, though this is both less general and adds unnecessary complexity.
|
|
315
|
-
|
|
316
|
-
[^1]: For data with lower cardinality it is possible to save memory by ordinalizing the values and
|
|
317
|
-
turning them into indices into a lookup table (e.g. encode all possible move base powers in a lookup
|
|
318
|
-
table and store the index into that table instead of the actual base power). While this approach may
|
|
319
|
-
minimize the absolute memory used, performance is more likely to suffer as the goal is to minimize
|
|
320
|
-
uncached memory lookups, not total memory usage.
|
|
321
|
-
|
|
322
|
-
[^2]: Instead of storing as a sparse multi-dimensional array the type chart could instead only
|
|
323
|
-
[store values which are not normal
|
|
324
|
-
effectiveness](https://github.com/pret/pokered/blob/master/data/types/type_matchups.asm) in 820 bits
|
|
325
|
-
as 82× attacking type (`4`) + defending type (`4`) + non-Normal effectiveness (`2`). This would
|
|
326
|
-
avoid having to do two memory lookups at the cost of a longer time scanning, but the second lookup
|
|
327
|
-
should already be fast due to locality of reference meaning it will likely already be in the cache.
|
|
328
|
-
This approach would also render `Type.PRECEDENCE` unnecessary.
|
|
329
|
-
|
|
330
|
-
## Layout
|
|
331
|
-
|
|
332
|
-
The precise layout of the engine's data structures is important to those implementing driver code,
|
|
333
|
-
as clients must directly probe the engine's state through these structures (i.e. the pkmn engine
|
|
334
|
-
does not produce an equivalent to Pokémon Showdown's `|request|` protocol message, this information
|
|
335
|
-
must be gleaned through the `Battle` state). Useful size and offset information can be found in
|
|
336
|
-
the [`layout.json`](../../data/layout.json) which exists to simplify writing driver code.
|
|
337
|
-
|
|
338
|
-
Documentation of the wire protocol used for protocol message logging when `-Dlog` is enabled can be
|
|
339
|
-
found in [PROTOCOL.md](../../../docs/PROTOCOL.md). Note that the
|
|
340
|
-
[`pkmn-debug`](../../../README.md#pkmn-debug) tool exists to display the binary protocol and battle
|
|
341
|
-
data in the browser for ease of debugging.
|
|
342
|
-
|
|
343
|
-
### `Battle`
|
|
344
|
-
|
|
345
|
-
| Start | End | Data | Description |
|
|
346
|
-
| ------- | ------- | ------------------- | ------------------------------------------------------- |
|
|
347
|
-
| 0 | 184 | [`sides[0]`](#side) | Player 1's side |
|
|
348
|
-
| 184 | 368 | [`sides[1]`](#side) | Player 2's side |
|
|
349
|
-
| 368 | 370 | `turn` | The current turn number |
|
|
350
|
-
| 370 | 372 | `last_damage` | The last damage dealt by either side |
|
|
351
|
-
| 372 | 373-376 | `last_moves` | The slot index of the last selected moves for each side |
|
|
352
|
-
| 373-376 | 384 | `rng` | The RNG state |
|
|
353
|
-
|
|
354
|
-
- the current `turn` is 2 bytes, written in native-endianness
|
|
355
|
-
- `last_moves` layout depends on whether or not Pokémon Showdown compatibility mode is enabled
|
|
356
|
-
(`-Dshowdown`). In either case it stores the last selected move index and last executed move for
|
|
357
|
-
each player (required to detect desyncs). In the case of the move index, 0 is used as an empty
|
|
358
|
-
value and 1-4 represent actual moves:
|
|
359
|
-
- if `showdown` is enabled byte 372 is used to store the last selected move index of `side[0]` and
|
|
360
|
-
byte 373 is used to store whether or not the side's last executed move would have been considered
|
|
361
|
-
"counterable"; and bytes 374 and 375 store the same information for `side[1]`
|
|
362
|
-
- otherwise the first 3 bits of byte 372 stores the last selected move index of `side[0]` and the
|
|
363
|
-
next bit stores whether that side's last executed move would have been considered "counterable",
|
|
364
|
-
with the same information for `side[1]`'s being stored in the second 4 bits
|
|
365
|
-
- the `rng` depends on whether or not Pokémon Showdown compatibility mode is enabled (`-Dshowdown`):
|
|
366
|
-
- if `showdown` is enabled, the RNG state begins on byte 376 and consists of a 64-bit seed,
|
|
367
|
-
written in native-endianness
|
|
368
|
-
- otherwise the RNG state begins on byte 373 and consists of the 10 bytes of the seed followed by
|
|
369
|
-
the index pointing to which byte of the seed is currently being used
|
|
370
|
-
|
|
371
|
-
### `Side`
|
|
372
|
-
|
|
373
|
-
| Start | End | Data | Description |
|
|
374
|
-
| ----- | --- | -------------------------- | --------------------------------------- |
|
|
375
|
-
| 0 | 24 | [`pokemon[0]`](#pokemon) | The player's first Pokémon |
|
|
376
|
-
| 24 | 48 | [`pokemon[1]`](#pokemon) | The player's second Pokémon |
|
|
377
|
-
| 48 | 72 | [`pokemon[2]`](#pokemon) | The player's third Pokémon |
|
|
378
|
-
| 72 | 96 | [`pokemon[3]`](#pokemon) | The player's fourth Pokémon |
|
|
379
|
-
| 96 | 120 | [`pokemon[4]`](#pokemon) | The player's fifth Pokémon |
|
|
380
|
-
| 120 | 144 | [`pokemon[5]`](#pokemon) | The player's sixth Pokémon |
|
|
381
|
-
| 144 | 176 | [`active`](#activepokemon) | The player's active Pokémon |
|
|
382
|
-
| 176 | 182 | `order` | The current order of the player's party |
|
|
383
|
-
| 182 | 183 | `last_selected_move` | The last move the player selected |
|
|
384
|
-
| 183 | 184 | `last_used_move` | The last move the player used |
|
|
385
|
-
|
|
386
|
-
- `order` is a 6 byte array where `order[i]` represents the "slot" of `pokemon[i]`, where the slot
|
|
387
|
-
is usually a unique value from 1-6 but can be 0 in situations where a player brings less than six
|
|
388
|
-
Pokémon to battle (note however that if `order[i]` is 0 than for all j > 0 `order[i+j]` **must**
|
|
389
|
-
equal 0).
|
|
390
|
-
|
|
391
|
-
### `ActivePokemon`
|
|
392
|
-
|
|
393
|
-
| Start | End | Data | Description |
|
|
394
|
-
| ----- | --- | ---------------------------------- | ---------------------------------------------------------- |
|
|
395
|
-
| 0 | 2 | `stats.hp` | The active Pokémon's computed max HP stat |
|
|
396
|
-
| 2 | 4 | `stats.atk` | The active Pokémon's modified Attack stat |
|
|
397
|
-
| 4 | 6 | `stats.def` | The active Pokémon's modified Defense stat |
|
|
398
|
-
| 6 | 8 | `stats.spe` | The active Pokémon's modified Speed stat |
|
|
399
|
-
| 8 | 10 | `stats.spc` | The active Pokémon's modified Special stat |
|
|
400
|
-
| 10 | 11 | `species` | The active Pokémon's species |
|
|
401
|
-
| 11 | 12 | `type1`/`type2` | The active Pokémon's types |
|
|
402
|
-
| 12 | 13 | `boosts.atk`/`boosts.def` | The active Pokémon's Attack and Defense boosts |
|
|
403
|
-
| 13 | 14 | `boosts.spe`/`boosts.spd` | The active Pokémon's Speed and Special boosts |
|
|
404
|
-
| 14 | 15 | `boosts.accuracy`/`boosts.evasion` | The active Pokémon's Accuracy and Evasion boosts |
|
|
405
|
-
| 15 | 16 | - | *Zero padding* |
|
|
406
|
-
| 16 | 24 | [`volatiles`](#volatiles-1) | The active Pokémon's volatile statuses and associated data |
|
|
407
|
-
| 24 | 25 | `moves[0].id` | The active Pokémon's second move |
|
|
408
|
-
| 25 | 26 | `moves[0].pp` | The PP of the active Pokémon's first move |
|
|
409
|
-
| 26 | 27 | `moves[1].id` | The active Pokémon's second move |
|
|
410
|
-
| 27 | 28 | `moves[1].pp` | The PP of the active Pokémon's second move |
|
|
411
|
-
| 28 | 29 | `moves[2].id` | The active Pokémon's third move |
|
|
412
|
-
| 29 | 30 | `moves[2].pp` | The PP of the active Pokémon's third move |
|
|
413
|
-
| 30 | 31 | `moves[3].id` | The active Pokémon's fourth move |
|
|
414
|
-
| 31 | 32 | `moves[3].pp` | The PP of the active Pokémon's fourth moe |
|
|
415
|
-
|
|
416
|
-
- the active Pokémon's `stats.hp` is always identical to the corresponding stored Pokémon's `stats.hp`
|
|
417
|
-
- `boosts` and `types` includes bytes which store two 4-bit fields each
|
|
418
|
-
|
|
419
|
-
#### `Volatiles`
|
|
420
|
-
|
|
421
|
-
> **NOTE:** The offsets in the following table represent *bits* and **not** bytes.
|
|
422
|
-
|
|
423
|
-
| Start | End | Data | Description |
|
|
424
|
-
| ----- | --- | ------------------- | ----------------------------------------------------------- |
|
|
425
|
-
| 0 | 1 | `Bide` | Whether the "Bide" volatile status is present |
|
|
426
|
-
| 1 | 2 | `Thrashing` | Whether the "Thrashing" volatile status is present |
|
|
427
|
-
| 2 | 3 | `MultiHit` | Whether the "MultiHit" volatile status is present |
|
|
428
|
-
| 3 | 4 | `Flinch` | Whether the "Flinch" volatile status is present |
|
|
429
|
-
| 4 | 5 | `Charging` | Whether the "Charging" volatile status is present |
|
|
430
|
-
| 5 | 6 | `Binding` | Whether the "Binding" volatile status is present |
|
|
431
|
-
| 6 | 7 | `Invulnerable` | Whether the "Invulnerable" volatile status is present |
|
|
432
|
-
| 7 | 8 | `Confusion` | Whether the "Confusion" volatile status is present |
|
|
433
|
-
| 8 | 9 | `Mist` | Whether the "Mist" volatile status is present |
|
|
434
|
-
| 9 | 10 | `FocusEnergy` | Whether the "FocusEnergy" volatile status is present |
|
|
435
|
-
| 10 | 11 | `Substitute` | Whether the "Substitute" volatile status is present |
|
|
436
|
-
| 11 | 12 | `Recharging` | Whether the "Recharging" volatile status is present |
|
|
437
|
-
| 12 | 13 | `Rage` | Whether the "Rage" volatile status is present |
|
|
438
|
-
| 13 | 14 | `LeechSeed` | Whether the "LeechSeed" volatile status is present |
|
|
439
|
-
| 14 | 15 | `Toxic` | Whether the "Toxic" volatile status is present |
|
|
440
|
-
| 15 | 16 | `LightScreen` | Whether the "LightScreen" volatile status is present |
|
|
441
|
-
| 16 | 17 | `Reflect` | Whether the "Reflect" volatile status is present |
|
|
442
|
-
| 17 | 18 | `Transform` | Whether the "Transform" volatile status is present |
|
|
443
|
-
| 18 | 21 | `confusion` | The remaining turns of confusion |
|
|
444
|
-
| 21 | 24 | `attacks` | The number of attacks remaining |
|
|
445
|
-
| 24 | 40 | `state` | A union of either: <ul><li>the total accumulated damage from Bide</li><li>the overwritten accuracy of certain moves</li></ul> |
|
|
446
|
-
| 40 | 48 | `substitute` | The remaining HP of the Substitute |
|
|
447
|
-
| 48 | 52 | `transform` | The identity of whom the active Pokémon is transformed into |
|
|
448
|
-
| 52 | 56 | `disabled_duration` | The remaining turns the move is disabled |
|
|
449
|
-
| 56 | 59 | `disabled_move` | The move slot (1-4) that is disabled |
|
|
450
|
-
| 59 | 64 | `toxic` | The number of turns toxic damage has been accumulating |
|
|
451
|
-
|
|
452
|
-
### `Pokemon`
|
|
453
|
-
|
|
454
|
-
| Start | End | Data | Description |
|
|
455
|
-
| ----- | --- | --------------- | ------------------------------------------ |
|
|
456
|
-
| 0 | 2 | `stats.hp` | The Pokémon's computed max HP stat |
|
|
457
|
-
| 2 | 4 | `stats.atk` | The Pokémon's unmodified Attack stat |
|
|
458
|
-
| 4 | 6 | `stats.def` | The Pokémon's unmodified Defense stat |
|
|
459
|
-
| 6 | 8 | `stats.spe` | The Pokémon's unmodified Speed stat |
|
|
460
|
-
| 8 | 10 | `stats.spc` | The Pokémon's unmodified Special stat |
|
|
461
|
-
| 10 | 11 | `moves[0].id` | The Pokémon's first stored move |
|
|
462
|
-
| 11 | 12 | `moves[0].pp` | The PP of the Pokémon's first stored move |
|
|
463
|
-
| 12 | 13 | `moves[1].id` | The Pokémon's second stored move |
|
|
464
|
-
| 13 | 14 | `moves[1].pp` | The PP of the Pokémon's second stored move |
|
|
465
|
-
| 14 | 15 | `moves[2].id` | The Pokémon's third stored move |
|
|
466
|
-
| 15 | 16 | `moves[2].pp` | The PP of the Pokémon's third stored move |
|
|
467
|
-
| 16 | 17 | `moves[3].id` | The Pokémon's fourth stored move |
|
|
468
|
-
| 17 | 18 | `moves[3].pp` | The PP of the Pokémon's fourth stored move |
|
|
469
|
-
| 18 | 20 | `hp` | The Pokémon's current HP |
|
|
470
|
-
| 20 | 21 | `status` | The Pokémon's current status |
|
|
471
|
-
| 21 | 22 | `species` | The Pokémon's stored species |
|
|
472
|
-
| 22 | 23 | `type1`/`type2` | The Pokémon's stored types |
|
|
473
|
-
| 23 | 24 | `level` | The Pokémon's level |
|
|
474
|
-
|
|
475
|
-
## Bugs
|
|
476
|
-
|
|
477
|
-
In addition to its alternative [RNG semantics](#rng), Pokémon Showdown's implementation of the first
|
|
478
|
-
generation of Pokémon contains a number bugs, [many of which are known and have been documented on
|
|
479
|
-
Smogon](https://www.smogon.com/forums/posts/5933177/show):
|
|
480
|
-
|
|
481
|
-
- moves on Pokémon Showdown can do 0 damage instead of failing or causing a [division-by-zero
|
|
482
|
-
freeze](https://pkmn.cc/bulba-glitch-1#Division_by_0).
|
|
483
|
-
- Pokémon Showdown does not implement type effectiveness precedence correctly.
|
|
484
|
-
- Pokémon Showdown checks for type and OHKO immunity before accuracy.
|
|
485
|
-
- Confusion self-hits use the wrong damage formula resulting in off-by-one errors (and also fail to
|
|
486
|
-
account for an opponent's Reflect). Furthermore, the confusion self-hit damage will erroneously be
|
|
487
|
-
inflicted to the confused user's substitute if the confused user had been attempting to use a
|
|
488
|
-
self-targeting move. Finally, Pokémon Showdown erroneously considers the *uncapped* self-hit
|
|
489
|
-
damage for the purposes of tracking the battle's last damage.
|
|
490
|
-
|
|
491
|
-
Beyond the general bugs listed above, several move effects are implemented incorrectly by Pokémon
|
|
492
|
-
Showdown. Some of these moves are [too fundamentally broken to be implemented](#unimplementable) by
|
|
493
|
-
the pkmn engine, but the following moves have their broken behavior preserved in `-Dshowdown` mode:
|
|
494
|
-
|
|
495
|
-
- **Bide**: Bide's damage can overflow if OHKO moves are involved because OHKO moves work by setting
|
|
496
|
-
the damage to 65535 - Pokémon Showdown doesn't implement this overflow and instead lets Bide's
|
|
497
|
-
damage grow unbounded. Additionally, if the opponent faints after Bide inflicts damage on Pokémon
|
|
498
|
-
Showdown residual damage incorrectly still gets applied to Bide's user.
|
|
499
|
-
- **Counter**: On Pokémon Showdown choices made while sleeping (which should not have been
|
|
500
|
-
registered) can erroneously cause Counter to trigger Desync Clause Mod behavior.
|
|
501
|
-
- **Leech Seed**: Leech Seed fails to heal its source side if a seeded target faints due to
|
|
502
|
-
recoil/crash damage on Pokémon Showdown.
|
|
503
|
-
- **Pay Day**: Pay Day should still scatter coins if it hits (but doesn't break) and opponent's
|
|
504
|
-
Substitute but doesn't on Pokémon Showdown.
|
|
505
|
-
- **Flinch**: Flinching does not get cleared during move selection on Pokémon Showdown and is
|
|
506
|
-
instead cleared in Pokémon Showdown's "residual" phase, meaning the flinch status gets erroneously
|
|
507
|
-
preserved across fainting (as fainting triggers Pokémon Showdown's "instaswitch" behavior which
|
|
508
|
-
skips end-of-turn residuals).
|
|
509
|
-
- **Rage**: Rage boosts should still result in burn/paralysis [stat modification
|
|
510
|
-
errors](https://pkmn.cc/bulba-glitch-1#Stat_modification_errors) but don't on Pokémon Showdown.
|
|
511
|
-
Furthermore, Pokémon Showdown only builds Rage for Disable/Explosion (hit/miss) when attacking
|
|
512
|
-
into a Substitute instead of all moves. Pokémon Showdown also implements the [Rage and Thrash /
|
|
513
|
-
Petal Dance accuracy bug]( https://www.youtube.com/watch?v=NC5gbJeExbs) incorrectly, as the
|
|
514
|
-
accuracy only gets written when the volatile is present which can lead to incorrect accuracy
|
|
515
|
-
values on turns where Rage is used on the same turn as a move which modifies accuracy or evasion.
|
|
516
|
-
- **Thrash** / **Petal Dance**: once a Pokémon is locked into a thrashing move they will lose all
|
|
517
|
-
speed ties on Pokémon Showdown (due to the fact that if they win the speed tie their action will
|
|
518
|
-
then get "changed" and inserted back into the queue after their opponent's action). Pokémon
|
|
519
|
-
Showdown also implements accuracy incorrectly, as covered above.
|
|
520
|
-
- **Freeze** / **Sleep**: Pokémon Showdown requires a move to be selected when a Pokémon is frozen
|
|
521
|
-
or sleeping and uses that in the event that the status is removed while on the cartridge no
|
|
522
|
-
selection is possible and no turn exists for the thawed/woken Pokémon to act except in the case of
|
|
523
|
-
a Fire-type move thawing a slower Pokémon (which should result in the [Freeze top move selection
|
|
524
|
-
glitch](https://glitchcity.wiki/Freeze_top_move_selection_glitch), which is not implemented and
|
|
525
|
-
would also likely be incorrect if it were to be implemented due to how Pokémon Showdown
|
|
526
|
-
incorrectly saves arbitrary moves with its choice selection semantics). Furthermore, thrashing
|
|
527
|
-
volatiles should not be cleared if the user misses a turn due to freeze / sleep.
|
|
528
|
-
- **Hyper Beam**: due to improperly implemented selection mechanics, the [Hyper Beam
|
|
529
|
-
automatic-selection glitch](https://glitchcity.wiki/Hyper_Beam_automatic_selection_glitch) does
|
|
530
|
-
not exist on Pokémon Showdown. Additionally, Hyper Beam being able to cause [Freeze permanent
|
|
531
|
-
helplessness](https://pkmn.cc/bulba-glitch-1#Hyper_Beam_.2B_Freeze_permanent_helplessness) is not
|
|
532
|
-
implement by Pokémon Showdown.
|
|
533
|
-
- **Roar** / **Whirlwind**: these moves can miss on Pokémon Showdown (and advance the RNG when
|
|
534
|
-
checking) which is incorrect (these moves should always fail, but do not check accuracy or advance
|
|
535
|
-
the RNG). More importantly, these moves should *not* cause the tracked last battle damage to be
|
|
536
|
-
zeroed, but on Pokémon Showdown they do.
|
|
537
|
-
- **Substitute**: in addition to the [Substitute + Confusion
|
|
538
|
-
glitch](https://pkmn.cc/bulba-glitch-1#Substitute_.2B_Confusion_glitch) not being implemented
|
|
539
|
-
correctly (covered above), the [Substitute 1/4
|
|
540
|
-
glitch](https://glitchcity.wiki/Substitute_%C2%BC_HP_glitch) also fails in many cases due to
|
|
541
|
-
Pokémon Showdown implementing the health check based on floating point division instead of integer
|
|
542
|
-
divison like on the cartridge (meaning the Substitute 1/4 glitch will only occur if the Pokémon's
|
|
543
|
-
maximum HP is evenly divisible by 4). Substitute also incorrectly blocks Dream Eater on
|
|
544
|
-
Pokémon Showdown and will incorrectly still heal 1 HP for any draining moves if the attack does 0
|
|
545
|
-
damage. Finally, Pokémon Showdown uses a `subFainted` field to track whether a Substitute was
|
|
546
|
-
broken in order to know when to nullify a move's effect, only it doesn't get cleared at the end of
|
|
547
|
-
the turn and can result in incorrect behavior on subsequent turns with the moves Mirror Move and
|
|
548
|
-
Metronome that invoke `runMove` (which is where `subFainted` gets cleared) on the user but skips
|
|
549
|
-
calling it for the eventual true target.
|
|
550
|
-
|
|
551
|
-
In addition to numerous cases where Pokémon Showdown uses the wrong type of message (e.g. `|-fail`
|
|
552
|
-
vs. `|-miss|` vs. `|-immune|`, e.g. in the case of Leech Seed) which are not documented here, Pokémon
|
|
553
|
-
Showdown sometimes gets message ordering incorrect (which does not effect the outcome
|
|
554
|
-
of the battle, only pedantic UI correctness):
|
|
555
|
-
|
|
556
|
-
- **Rage**: Rage should report the `|-boost|` before the Disable `|-miss|`, not after.
|
|
557
|
-
- **Haze**: Haze clears the status/volatiles in an incorrect order.
|
|
558
|
-
- **Twineedle**: the `|-hitcount|` ("Hit 2 times") should come before the `|-status|` message, not
|
|
559
|
-
after.
|
|
560
|
-
- **Thrash** / **Petal Dance**: confusion should silently get applied before the `|move|` message,
|
|
561
|
-
not after.
|
|
562
|
-
|
|
563
|
-
Pokémon Showdown also implements a number of modifications to the cartridge (usually but not always
|
|
564
|
-
called out in the `|rule|` section at the beginning of a battle's log):
|
|
565
|
-
|
|
566
|
-
- **Sleep Clause Mod**: players are prevented from putting more than one of their opponent's Pokémon
|
|
567
|
-
to sleep at a time (usage of the move will fail).
|
|
568
|
-
- **Freeze Clause Mod**: players are prevented from freezing more than one of their opponent's
|
|
569
|
-
Pokémon at a time (usage of the move will fail).
|
|
570
|
-
- **Desync Clause Mod**: If the usage of a move would cause a desync it instead causes a
|
|
571
|
-
failure. However, this mod does not trigger for [**division by
|
|
572
|
-
zero**](https://pkmn.cc/bulba/List_of_glitches_(Generation_I)#Division_by_0) - instead of failing,
|
|
573
|
-
Pokémon Showdown silently patches the damage calculation to divide by 1 instead of 0. The
|
|
574
|
-
definition of the Desync Clause Mod should be extended or the code should be changed to fail
|
|
575
|
-
instead of succeeding in these cases.
|
|
576
|
-
- **Endless Battle Clause**: Prematurely ends the battle in a tie after 1000 turns or in certain
|
|
577
|
-
situations where it is trivially detectable that no progress can be made.
|
|
578
|
-
- **Switch Priority Clause Mod**: When both player switch out their Pokémon at the same time the
|
|
579
|
-
faster Pokémon switches first. This mod is **not broadcast at the start of the battle in the
|
|
580
|
-
`|rule|` section** in Generation I (or II) as the actual order of switches here does not have
|
|
581
|
-
competitive implications like it does in Generation III, but it is still contrary to how the games
|
|
582
|
-
work where the [host (Player 1)'s Pokemon would switch
|
|
583
|
-
first](https://www.smogon.com/forums/posts/5989318/show).
|
|
584
|
-
|
|
585
|
-
Pokémon Showdown enforces several clauses *before* the battle: **Cleric Clause** (all Pokémon must
|
|
586
|
-
have full HP and PP, and not have any status conditions prior to the battle), **Stat Tradeback
|
|
587
|
-
Clause** (Pokémon may not have moves obtained from trading back from Pokémon Gold/Silver/Crystal,
|
|
588
|
-
though may have DV spreads which would otherwise be unobtainable), **Species Clause** (players may
|
|
589
|
-
not have more than one of the same Pokémon species on their team) and bans specific moves via
|
|
590
|
-
**Evasion Clause**, **OHKO Clause**, and the **Invulnerability Clause** - none of these are
|
|
591
|
-
implemented by the pkmn engine as they can all be accomplished at a higher level by the client.
|
|
592
|
-
Similarly, Pokémon Showdown's UI mods, the **HP Percentage Mod** which displays the HP percentage of
|
|
593
|
-
a Pokémon instead of pixel information and the **Move Effectiveness Mod** which corrects for the
|
|
594
|
-
[dual-type damage misinformation
|
|
595
|
-
glitch](https://pkmn.cc/bulba/List_of_glitches_(Generation_I)#Dual-type_damage_misinformation) or
|
|
596
|
-
the correction to [Poison/Burn animation with 0
|
|
597
|
-
HP](https://pkmn.cc/bulba/List_of_glitches_(Generation_I)#Poison.2FBurn_animation_with_0_HP) are all
|
|
598
|
-
left up to a client to support.
|
|
599
|
-
|
|
600
|
-
### Unimplementable
|
|
601
|
-
|
|
602
|
-
Numerous moves on Pokémon Showdown are broken not simply due to local issues in the implementation
|
|
603
|
-
of their effects but instead due to **global issues** related to fundamental mechanics such as
|
|
604
|
-
[broken move selection](https://www.smogon.com/forums/posts/8655897/show), using move name instead
|
|
605
|
-
of slot to determine move identity, implementing volatile statuses incorrectly or not understanding
|
|
606
|
-
how moves which call other moves work means that it isn't possible to completely implement Pokémon
|
|
607
|
-
Showdown's behavior for the following moves (the pkmn engine attempts to reproduce as much of the
|
|
608
|
-
behavior that can be reproduced without making data structure changes or dramatically deviating from
|
|
609
|
-
the correct control flow):
|
|
610
|
-
|
|
611
|
-
- **Wrap**: Binding moves like Wrap are implemented on Pokémon Showdown with an artificial
|
|
612
|
-
`partialtrappinglock` volatile as opposed to how it works on the cartridge which simply relies on
|
|
613
|
-
the `Binding` volatile of the opponent. This mistake results in choice locking not being reported
|
|
614
|
-
properly when the binding move was initiated via another move such as Metronome or Mirror Move.
|
|
615
|
-
Binding moves also have some local implementation issues - on Pokémon Showdown a bound Pokémon
|
|
616
|
-
still gets a turn under the [trapping sleep
|
|
617
|
-
glitch](https://glitchcity.wiki/Trapping_move_and_sleep_glitch), Wrap does 0 damage against
|
|
618
|
-
Ghost-type Pokémon instead of properly respecting immunity, and binding effects are handled in the
|
|
619
|
-
wrong order in the code which results in either out of order messaging or, more consequentially,
|
|
620
|
-
Pokémon using Rage/Bide or Thrashing/Charging moves being incorrectly forced to skip a turn when
|
|
621
|
-
the Pokémon using the Binding move switches.
|
|
622
|
-
- **Mimic**: Pokémon Showdown checks that the user of Mimic has Mimic in one of their move slots,
|
|
623
|
-
which means Mimic legally called via Metronome or Mirror Move will only work if the user also has
|
|
624
|
-
Mimic (and the moved mimicked by Mimic called via Metronome / Mirror Move will erroneously
|
|
625
|
-
override the Mimic move slot instead of the Metronome / Mirror Move move slot). Furthermore,
|
|
626
|
-
because Pokémon Showdown deducts PP based on a move's name instead of slot, if Mimic copies a move
|
|
627
|
-
the Pokémon already knows, PP deduction for using either the original move of the mimicked move
|
|
628
|
-
will instead deduct PP for whichever appears at the lower move slot index and the PP will be
|
|
629
|
-
allowed to go negative (effectively allowing for infinite PP use). Pokémon Showdown also doesn't
|
|
630
|
-
deduct PP from using Transform if it was copied by Mimic.
|
|
631
|
-
- **Mirror Move**: As covered above, both Substitute and Binding moves misbehave when used via
|
|
632
|
-
Mirror Move (though Pokémon Showdown has its own weird behavior and does not implement the
|
|
633
|
-
[partial trapping move Mirror Move
|
|
634
|
-
glitch](https://glitchcity.wiki/Partial_trapping_move_Mirror_Move_link_battle_glitch) that exists
|
|
635
|
-
on the cartridge). Additionally, Pokémon Showdown sets the last used move every turn a Pokémon is
|
|
636
|
-
Thrashing instead of just on the turn it is actually selected meaning Mirror Move will sometimes
|
|
637
|
-
successfully mirror a Thrashing move when it should fail. Finally, if Mirror Move copies
|
|
638
|
-
Struggle it should not deduct PP but on Pokémon Showdown it does.
|
|
639
|
-
- **Metronome**: In addition to the issues with binding moves and Substitute, Metronome and Mirror
|
|
640
|
-
Move cannot mutually call each other more than 3 times in a row without causing the Pokémon
|
|
641
|
-
Showdown simulator to crash due to defensive safety checks that do not exist on the cartridge.
|
|
642
|
-
- **Transform**: Transform screws up the effect of Disable, because on Pokémon Showdown, Disable
|
|
643
|
-
prevents moves of a given *name* from being used (e.g. "Water Gun") as opposed to moves in a
|
|
644
|
-
specific *slot* (e.g. the 2nd move slot), and a Pokémon's moves can change after Transform (this
|
|
645
|
-
is not an issue with Disable + Mimic because Mimic happens to replace the same slot). Transform
|
|
646
|
-
also has a bad interaction with Pokémon Showdown's buggy Haze implementation, as Haze on Pokémon
|
|
647
|
-
Showdown does not copy unmodified stats, leaving the Transformed Pokémon with incorrect stats.
|
|
648
|
-
Furthermore, transforming and then using Mirror Move / Metronome can result in [glitchy behavior
|
|
649
|
-
and
|
|
650
|
-
softlocks](https://pkmn.cc/bulba/Transform_glitches#Transform_.2B_Mirror_Move.2FMetronome_PP_error)
|
|
651
|
-
which Pokémon Showdown does not implement.
|
|
652
|
-
|
|
653
|
-
Importantly, Pokémon Showdown's **speed tie RNG mechanics are unimplementable** in the pkmn engine
|
|
654
|
-
as they rely on internal implementation decisions made by Pokémon Showdown that are impossible to
|
|
655
|
-
replicate without also mimicking Pokémon Showdown's (incorrect) event and "action" systems (see
|
|
656
|
-
below).
|
|
657
|
-
|
|
658
|
-
## RNG
|
|
659
|
-
|
|
660
|
-
**The pkmn engine aims to match the cartridge's RNG frame-accurately**, in that provided with the
|
|
661
|
-
same initial seed and inputs it should produce the same battle playout as the Pokémon Red cartridge.
|
|
662
|
-
**Pokémon Showdown does not correctly implement frame-accurate RNG in any generation**, and along
|
|
663
|
-
with the [bugs](#bugs) discussed above this results in large differences in the codebase. Because
|
|
664
|
-
the pkmn engine aims to be as compatible with Pokémon Showdown as possible when in `-Dshowdown`
|
|
665
|
-
compatibility mode, the implications of these differences are outlined below:
|
|
666
|
-
|
|
667
|
-
- **RNG**: Pokémon Showdown uses the RNG from Generation V & VI in every generation, despite
|
|
668
|
-
the seeds and algorithm being different. Pokémon Red uses a simple 8-bit RNG with 10 distinct
|
|
669
|
-
seeds generated when the link connection is established, whereas Pokémon Showdown uses a 64-bit
|
|
670
|
-
RNG with a 32-bit output.
|
|
671
|
-
- **Algorithm**: As detailed in the table below, the algorithm used by Pokémon Showdown in the
|
|
672
|
-
places randomness is required is often different than on the cartridge, so even if Pokémon
|
|
673
|
-
Showdown were using the correct RNG the values would still diverge (including using a completely
|
|
674
|
-
incorrect distribution for multi-hit moves).
|
|
675
|
-
- **Bias**: Pokémon Showdown often needs to reduce its 32-bit output range to a smaller range in
|
|
676
|
-
order to implement various effects, and does so using a [biased integer multiplication
|
|
677
|
-
method](http://www.pcg-random.org/posts/bounded-rands.html) as opposed to debiasing via rejection
|
|
678
|
-
sampling to ensure uniformity as is done on the cartridge. This means that certain values are
|
|
679
|
-
fractionally more likely to be chosen than others, though this bias is usually quite small (e.g.
|
|
680
|
-
in the case of Metronome instead of selecting moves with an equal $1\over163$ chance, Pokémon
|
|
681
|
-
Showdown will select some with a $1\over2^{32}$ greater chance than others).
|
|
682
|
-
- **Order of operations**: RNG calls effectively introduce something similar to a ["memory
|
|
683
|
-
barrier"](https://en.wikipedia.org/wiki/Memory_barrier) in that they must be sequenced correctly
|
|
684
|
-
(though operations which occur between them may happen in any order). Pokémon Showdown violates
|
|
685
|
-
this by introducing additional operations (see below) and changing up the order of existing
|
|
686
|
-
operations (e.g. choosing to check for hit/miss, determine the number of hits for moves with
|
|
687
|
-
multiple hits, determine if a move hit critically and then the damage instead of checking for
|
|
688
|
-
hit/miss and determining number of hits *after* the other two). While it is often desirable to
|
|
689
|
-
rearrange code for performance or to improve readability this can only be done if it does not
|
|
690
|
-
affect accuracy.
|
|
691
|
-
- **Speed-ties**: In addition to [breaking switch-in speed ties with an RNG
|
|
692
|
-
call](https://www.smogon.com/forums/threads/adv-switch-priority.3622189/), speed ties in Pokémon
|
|
693
|
-
Showdown actually result in a large number of spurious frame advancements due to the internal
|
|
694
|
-
implementation details of Pokémon Showdown's event and "action" systems. Ultimately, without
|
|
695
|
-
keeping track of all of the handlers for the internal events Pokémon Showdown invents and
|
|
696
|
-
implementing the same "action" system, matching Pokémon Showdown's RNG in the presence of speed
|
|
697
|
-
ties proves impossible, and thus the reference Pokémon Showdown implementation pkmn engine aims to
|
|
698
|
-
match has been [patched](../../../docs/TESTING.md#patches) to change this behavior.
|
|
699
|
-
- **Effects**: Pokémon Showdown occasionally incorrectly inserts RNG calls in move effect handlers
|
|
700
|
-
when they are not relevant:
|
|
701
|
-
- Roar / Whirlwind roll to hit and can "miss" as opposed to simply failing
|
|
702
|
-
|
|
703
|
-
Finally, the **initial 10-byte seed for link battles on Pokémon Red cannot include bytes larger than
|
|
704
|
-
the `SERIAL_PREAMBLE_BYTE`**, so must be in the range $\left[0, 252\right]$. This has implications
|
|
705
|
-
on the first 10 random numbers generated during the battle and has [**non-trivial competitive
|
|
706
|
-
implications**](https://www.smogon.com/forums/posts/9068411/show) (at the start of the battle move
|
|
707
|
-
effects become more likely, the ["1/256-miss" glitch](https://glitchcity.wiki/1/256_miss_glitch)
|
|
708
|
-
cannot happen, Player 1 is more likely to win speed ties, etc) that Pokémon Showdown cannot
|
|
709
|
-
replicate due to everything described above.
|
|
710
|
-
|
|
711
|
-
All of places in the link battle code where randomness is required are outlined below:
|
|
712
|
-
|
|
713
|
-
| Type | Location | Description |
|
|
714
|
-
| ------------------------ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
715
|
-
| **Speed Tie** | `turnOrder` | Player 1 if $X < 127$, otherwise Player 2 |
|
|
716
|
-
| **Critical Hit** | `checkCriticalHit` | <dl><dt>Pokémon Red</dt><dd>Inflict a critical hit if $X' < chance$ where $X'$ is $X$ with its bits rotated left three times</dd><dt>Pokémon Showdown</dt><dd>Critical hit if $X < chance$</dd></dl> |
|
|
717
|
-
| **Damage** (range) | `randomizeDamage` | <dl><dt>Pokémon Red</dt><dd>Continue generating until $X' \geq 217$ where $X'$ is $X$ with its bits rotated right once</dd><dt>Pokémon Showdown</dt><dd>Generate $X \in \left[217, 256\right)$</dd></dl> |
|
|
718
|
-
| **Hit / Miss** | `checkHit` | Hit if $X < scaledAccuracy$ |
|
|
719
|
-
| **Burn** (chance) | `Effects.burnChance` | Trigger if $X < 26$ ($77$ for Fire Blast) |
|
|
720
|
-
| **Confusion** (chance) | `Effects.confusion` | Trigger if $X< 25$ |
|
|
721
|
-
| **Confusion** (duration) | `Effects.confusion` | <dl><dt>Pokémon Red</dt><dd>Last for $\left(X \land 3\right) + 2$ turns</dd><dt>Pokémon Showdown</dt><dd>Last for $X \in \left[2, 6\right)$ turns</dd></dl> |
|
|
722
|
-
| **Confusion** (self-hit) | `beforeMove` | Trigger if $X \geq 128$ |
|
|
723
|
-
| **Flinch** (chance) | `Effects.flinchChance` | Trigger if $X < 26$ ($77$ for Stomp / Headbutt / Rolling Kick / Low Kick) |
|
|
724
|
-
| **Freeze** (chance) | `Effects.freezeChance` | Trigger if $X < 26$ |
|
|
725
|
-
| **Paralysis** (chance) | `Effects.paralyzeChance` | Trigger if $X< 26$ ($77$ for Body Slam / Lick) |
|
|
726
|
-
| **Paralysis** (full) | `beforeMove` | Trigger if $X < 63$ |
|
|
727
|
-
| **Poison** (chance) | `Effects.poison` | Trigger if $X < 52$ ($103$ for Smog / Sludge) |
|
|
728
|
-
| **Sleep** (duration) | `Effects.sleep` | <dl><dt>Pokémon Red</dt><dd>Continue generating until $X \land 7 \neq 0$</dd><dt>Pokémon Showdown</dt><dd>Generate $X \in \left[1, 8\right)$</dd></dl> |
|
|
729
|
-
| **Bide** (duration) | `Effects.bide` | <dl><dt>Pokémon Red</dt><dd>Last for $\left(X \land 1\right) + 2$ turns</dd><dt>Pokémon Showdown</dt><dd>Last for $X \in \left[2, 4\right)$ turns</dd></dl> |
|
|
730
|
-
| **Disable** (move) | `Effects.disable` | <dl><dt>Pokémon Red</dt><dd>Continue generating until $X \land 3$ is the index of a non-empty move slot</dd><dt>Pokémon Showdown</dt><dd>Generate $X \in \left[0, \|movesSlots\|\right)$</dd></dl> |
|
|
731
|
-
| **Disable** (duration) | `Effects.disable` | <dl><dt>Pokémon Red</dt><dd>Last for $\left(X \land 7\right) + 1$ turns</dd><dt>Pokémon Showdown</dt><dd>Last for $X \in \left[1, 9\right)$ turns</dd></dl> |
|
|
732
|
-
| **Metronome** (move) | `metronome` | <dl><dt>Pokémon Red</dt><dd>Continue generating until $X$ matches the index of a move which is not Struggle or Metronome</dd><dt>Pokémon Showdown</dt><dd>Generate $X \in \left[0, \|validMoves\|\right)$, where $validMoves$ is ordered set of moves with Struggle and Metronome removed <i>(indexes of moves > Metronome will be shifted down)</i></dd></dl> |
|
|
733
|
-
| **Mimic** (move) | `Effects.mimic` | <dl><dt>Pokémon Red</dt><dd>Continue generating until $X \land 3$ is the index of a non-empty move slot</dd><dt>Pokémon Showdown</dt><dd>Generate $X \in \left[0, \|movesSlots\|\right)$</dd></dl> |
|
|
734
|
-
| **Psywave** (power) | `specialDamage` | <dl><dt>Pokémon Red</dt><dd>Continue generating until $X < {3\over2} \cdot level$</dd><dt>Pokémon Showdown</dt><dd>Generate $X \in \left[0, {3\over2} \cdot level\right)$</dd></dl> |
|
|
735
|
-
| **Thrash** (rampage) | `Effects.thrash` | <dl><dt>Pokémon Red</dt><dd>Last for $\left(X \land 1\right) + 2$ turns</dd><dt>Pokémon Showdown</dt><dd>Last for $X \in \left[2, 4\right)$ turns</dd></dl> |
|
|
736
|
-
| **Thrash** (confusion) | `beforeMove` | <dl><dt>Pokémon Red</dt><dd>Last for $\left(X \land 3\right) + 2$ turns</dd><dt>Pokémon Showdown</dt><dd>Last for $X \in \left[2, 6\right)$ turns</dd></dl> |
|
|
737
|
-
| **Binding** (duration) | `Effects.binding` | <dl><dt>Pokémon Red</dt><dd>Last for $\left(X \land 3\right) + 1$ further turns if $\left(X \land 3\right) < 2$<br /> otherwise last for $\left(Y \land 3\right) + 1$ further turns (reroll)</dd><dt>Pokémon Showdown</dt><dd>Last for $X \in \{2, 2, 3, 3, 4, 5\} - 1$ further turns</dd></dl> |
|
|
738
|
-
| **Multi-Hit** (hits) | `Effects.multiHit` | *Ibid.* |
|
|
739
|
-
| **Unboost** (chance) | `Effects.unboost` | Trigger if $X < 85$ |
|
|
740
|
-
|
|
741
|
-
*In the table above,* $X$ *is always in the range* $\left[0, 256\right)$ *for Pokémon Red and for
|
|
742
|
-
Pokémon Showdown is always **scaled** from the 32-bit output range of the RNG to either be in that
|
|
743
|
-
range or to whichever range is specified by the description.*
|
|
744
|
-
|
|
745
|
-
## Details
|
|
746
|
-
|
|
747
|
-
The engine's Generation I mechanics code loosely mirrors the structure and naming of the pret
|
|
748
|
-
decompilation of the Pokémon Red source, though has been simplified (e.g. a single method for both
|
|
749
|
-
sides as opposed to having separate duplicated code for each) and optimized. Furthermore, the
|
|
750
|
-
control flow has been modified to handle both the correct Pokémon Red order of operations and
|
|
751
|
-
Pokémon Showdown's order when in compatibility mode - as such, there are many `if (showdown)` blocks
|
|
752
|
-
which handle Pokémon Showdown's divergent behavior (this is most evident in `doMove`). The following
|
|
753
|
-
table provides a rough mapping between pkmn and Pokémon Red methods:
|
|
754
|
-
|
|
755
|
-
| pkmn | Pokémon Red (pret) |
|
|
756
|
-
| ------------------------- | ------------------------------------------------------- |
|
|
757
|
-
| `start` | `StartBattle` |
|
|
758
|
-
| `update` | `MainInBattleLoop` |
|
|
759
|
-
| `findFirstAlive` | `findFirstAlive*MonLoop` / `AnyPartyAlive` |
|
|
760
|
-
| `selectMove` / `saveMove` | `selectPlayerMove` |
|
|
761
|
-
| `switchIn` | `SwitchPlayerMon` / `SendOutMon` |
|
|
762
|
-
| `turnOrder` | `MainInBattleLoop` |
|
|
763
|
-
| `doTurn` | `MainInBattleLoop` |
|
|
764
|
-
| `executeMove` | `ExecutePlayerMove` |
|
|
765
|
-
| `beforeMove` | `CheckPlayerStatusConditions` |
|
|
766
|
-
| `canMove` | `CheckIfPlayerNeedsToChargeUp` / `PlayerCanExecuteMove` |
|
|
767
|
-
| `decrementPP` | `DecrementPP` |
|
|
768
|
-
| `doMove` | `PlayerCalcMoveDamage` / `CalculateDamage` |
|
|
769
|
-
| `checkCriticalHit` | `CriticalHitTest` |
|
|
770
|
-
| `calcDamage` | `GetDamageVarsForPlayerAttack` / `CalculateDamage` |
|
|
771
|
-
| `adjustDamage` | `AdjustDamageForMoveType` |
|
|
772
|
-
| `randomizeDamage` | `RandomizeDamage` |
|
|
773
|
-
| `specialDamage` | `ApplyAttackToEnemyPokemon` |
|
|
774
|
-
| `counterDamage` | `HandleCounterMove` |
|
|
775
|
-
| `applyDamage` | `ApplyDamageToEnemyPokemon` / `AttackSubstitute` |
|
|
776
|
-
| `mirrorMove` | `MirrorMoveCheck` / `MirrorMoveCopyMove` |
|
|
777
|
-
| `metronome` | `metronomeCheck` / `MetronomePickMove` |
|
|
778
|
-
| `checkHit` / `moveHit` | `MoveHitTest` |
|
|
779
|
-
| `checkFaint` | `HasMonFainted` / `HandlePlayerMonFainted` |
|
|
780
|
-
| `faint` | `FaintEnemyPokemon` |
|
|
781
|
-
| `handleResidual` | `HandlePoisonBurnLeechSeed` |
|
|
782
|
-
| `endTurn` / `checkEBC` | - |
|
|
783
|
-
|
|
784
|
-
Pokémon Red groups move effect handlers together into several groups, these have also been renamed for clarification in the pkmn engine:
|
|
785
|
-
|
|
786
|
-
| pkmn | Pokémon Red (pret) |
|
|
787
|
-
| --------------- | ------------------------- |
|
|
788
|
-
| `onBeforeMove` | `ResidualEffects1` |
|
|
789
|
-
| `onEndMove` | `ResidualEffects2` |
|
|
790
|
-
| `alwaysHappens` | `AlwaysHappensSideEffect` |
|
|
791
|
-
| `isSpecial` | `SpecialEffects` |
|
|
792
|
-
|
|
793
|
-
### Naming
|
|
794
|
-
|
|
795
|
-
The pkmn engine attempts to adhere to certain naming conventions:
|
|
796
|
-
|
|
797
|
-
- **`p1`** & **`p2`** always correspond to the `Side` of `Player.P1` and `Player.P2` respectively
|
|
798
|
-
- **`player`** is the executing turn `Player` (`hWhoseTurn`) and **`player.foe()`** is their
|
|
799
|
-
opponent
|
|
800
|
-
- if `player` (executing turn player) is present then **`side`** will always correspond to their
|
|
801
|
-
`Side` and **`foe`** the opposing `Side`, however `side` can refer to an arbitrary `Side` object
|
|
802
|
-
if there is no `player` in scope (e.g. in helper functions)
|
|
803
|
-
- **`target_player`** is a target `Player` whose corresponding `Side` is **`target`**
|
|
804
|
-
|
|
805
|
-
## Resources
|
|
806
|
-
|
|
807
|
-
- [pret/pokered](https://github.com/pret/pokered/) disassembly
|
|
808
|
-
- [Gen I Main Battle Function](https://www.smogon.com/forums/posts/5878612/show) - Crystal_
|
|
809
|
-
- [Pokemon Showdown!](https://github.com/smogon/pokemon-showdown)
|
|
810
|
-
- [List of glitches (Generation
|
|
811
|
-
I)](https://bulbapedia.bulbagarden.net/wiki/List_of_glitches_(Generation_I))
|
|
812
|
-
- [Pokémon Showdown RBY Bugs](https://www.smogon.com/forums/posts/5933177/show)
|