@pkmn/engine 0.1.0-dev.dc33f678 → 0.1.0-dev.deda8593

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.
Files changed (49) hide show
  1. package/LICENSE +1 -3
  2. package/README.md +44 -19
  3. package/build/pkg/addon.js +6 -7
  4. package/build/pkg/addon.js.map +1 -1
  5. package/build/pkg/data.d.ts +4 -4
  6. package/build/pkg/data.js.map +1 -1
  7. package/build/pkg/gen1.js.map +1 -1
  8. package/build/pkg/index.d.ts +2 -1
  9. package/build/pkg/index.js.map +1 -1
  10. package/build/pkg/protocol.js +5 -5
  11. package/build/pkg/protocol.js.map +1 -1
  12. package/build/test/display.js +2 -2
  13. package/build/test/display.js.map +1 -1
  14. package/build/tools/debug.js +5 -4
  15. package/build/tools/debug.js.map +1 -1
  16. package/build.zig +100 -102
  17. package/package.json +33 -22
  18. package/src/bin/install-pkmn-engine +26 -18
  19. package/src/lib/common/data.zig +8 -8
  20. package/src/lib/common/debug.zig +2 -2
  21. package/src/lib/common/optional.zig +1 -1
  22. package/src/lib/common/options.zig +2 -0
  23. package/src/lib/common/protocol.zig +397 -384
  24. package/src/lib/common/rational.zig +11 -8
  25. package/src/lib/common/rng.zig +20 -11
  26. package/src/lib/gen1/calc.zig +217 -47
  27. package/src/lib/gen1/chance.zig +91 -92
  28. package/src/lib/gen1/data/moves.zig +10 -10
  29. package/src/lib/gen1/data/species.zig +3 -3
  30. package/src/lib/gen1/data/types.zig +5 -5
  31. package/src/lib/gen1/data.zig +35 -35
  32. package/src/lib/gen1/helpers.zig +136 -55
  33. package/src/lib/gen1/mechanics.zig +333 -312
  34. package/src/lib/gen2/calc.zig +143 -0
  35. package/src/lib/gen2/chance.zig +1315 -0
  36. package/src/lib/gen2/data/items.zig +3 -3
  37. package/src/lib/gen2/data/moves.zig +5 -5
  38. package/src/lib/gen2/data/species.zig +1 -1
  39. package/src/lib/gen2/data/types.zig +10 -10
  40. package/src/lib/gen2/data.zig +27 -37
  41. package/src/lib/gen2/generated.zig +806 -0
  42. package/src/lib/gen2/helpers.zig +37 -7
  43. package/src/lib/gen2/mechanics.zig +1621 -456
  44. package/src/lib/{binding/node.zig → node.zig} +9 -12
  45. package/src/lib/pkmn.zig +27 -12
  46. package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
  47. package/src/test/display/pkmn.html.tmpl +5 -5
  48. package/src/lib/gen1/README.md +0 -894
  49. package/src/lib/gen2/README.md +0 -330
@@ -1,330 +0,0 @@
1
- # Pokémon Generation II: GSC
2
-
3
- ## Data Structures
4
-
5
- The following information is required to simulate a Generation II Pokémon battle:
6
-
7
- ==**TODO**==
8
-
9
- | pkmn | Pokémon Crystal (pret) | Pokémon Showdown |
10
- | ------------------------------ | --------------------------------- | ------------------------------------- |
11
- | `battle.seed` | `Random{Add,Sub}` | `battle.seed` |
12
- | `battle.turn` | | `battle.turn` |
13
- | `field.weather.id` | `BattleWeather` | `field.weather` |
14
- | `field.weather.duration` | `WeatherCount` | `field.weatherState.duration` |
15
- | `side.{active,pokemon}` | `BattleMon` | `side.active` |
16
- | `side.team` | `PartyMons`/`PartySpecies` | `side.pokemon` |
17
- | `side.conditions` | `PlayerScreens` | `side.sideConditions` |
18
- | `conditions.data.safeguard` | `PlayerSafeguardCount` | `sideConditions.safeguard.duration` |
19
- | `conditions.data.light_screen` | `PlayerLightScreenCount` | `sideConditions.lightscreen.duration` |
20
- | `conditions.data.reflect` | `PlayerReflectCount` | `sideConditions.reflect.duration` |
21
- | `pokemon.stats` | `TODO unmodified` | `pokemon.baseStoredStats` |
22
- | `pokemon.position` | `TODO` | `pokemon.position` |
23
- | `pokemon.moves` | `party_struct.{Moves,PP}` | `pokemon.baseMoveSlots` |
24
- | `{pokemon,active}.hp` | `{party,battle}_struct.HP` | `pokemon.hp` |
25
- | `{pokemon,active}.status` | `{party,battle}_struct.Status` | `pokemon.status` |
26
- | `{pokemon,active}.level` | `TODO unmodified` | `pokemon.level` |
27
- | `{pokemon,active}.happiness` | `{box,battle}_struct.Happiness` | `pokemon.happiness` |
28
- | `{pokemon,active}.ivs.gender` | `{box,battle}_struct.DVs` | `pokemon.gender` |
29
- | `{pokemon,active}.ivs.type` | `{box,battle}_struct.DVs` | `pokemon.hpType` |
30
- | `{pokemon,active}.ivs.power` | `{box,battle}_struct.DVs` | `pokemon.hpPower` |
31
- | `{pokemon,active}.item` | `{box,battle}_struct.Item` | `pokemon.item` |
32
- | `pokemon.species` | `party_struct.Species` | `pokemon.baseSpecies` |
33
- | `active.species` | `battle_struct.Species` | `pokemon.species` |
34
- | `active.stats` | `TODO modified` | `pokemon.storedStats` |
35
- | `active.boosts` | `PlayerStatLevels` | `pokemon.boosts` |
36
- | `active.volatiles` | `PlayerBattleStatus{1,2,3}` | `pokemon.volatiles` |
37
- | `volatiles.bide` | `PlayerDamageTaken` | `volatiles.bide.totalDamage` |
38
- | `volatiles.substitute` | `PlayerSubstituteHP` | `volatiles.substitute.hp` |
39
- | `volatiles.rollout` | `PlayerRolloutCount` | `volatiles.rollout.hitCount` |
40
- | `volatiles.confusion` | `PlayerConfuseCount` | `volatiles.confusion.duration` |
41
- | `volatiles.toxic` | `PlayerToxicCount` | `volatiles.residualdmg.counter` |
42
- | `volatiles.disable` | `PlayerDisableCount` | `moveSlots.disabled` |
43
- | `volatiles.encore` | `PlayerEncoreCount` | `volatiles.encore.duration` |
44
- | `volatiles.perish_song` | `PlayerPerishCount` | `volatiles.perishsong.duration` |
45
- | `volatiles.fury_cutter` | `PlayerFuryCutterCount` | `volatiles.furycutter.multiplier` |
46
- | `volatiles.protect` | `PlayerProtectCount` | `volatiles.stall.counter` |
47
- | `volatiles.future_sight` | `PlayerFutureSight{Damage,Count}` | `slotConditions[i].futuremove` |
48
- | `volatiles.rage` | `PlayerRageCounter` | **BUG** |
49
- | `volatiles.bind` | `PlayerWrapCount` | `volatiles.partiallytrapped.duration` |
50
- | `volatiles.bind_reason` | `PlayerTrappingMove` | `volatiles.effectState.sourceEffect` |
51
- | | `PlayerCharging` | |
52
- | `volatiles.trapped FIXME` | `FIXME` | `pokemon.trapped` |
53
- | `volatiles.switched` | `TODO` | `pokemon.switchFlag` |
54
- | `volatiles.switching` | `PlayerIsSwitching` | `pokemon.switchFlag` |
55
- | `volatiles.frozen` | `PlayerJustGotFrozen` | `volatiles.frozen` |
56
- | | | |
57
- | `active.last_used_move` | `PlayerUsedMove` | `side.lastMove` |
58
-
59
- - Pokémon Showdown doesn't implement the correct Generation II RNG and as such its `seed` is
60
- different
61
- - `battle.turn` only needs to be tracked to be compatible with the Pokémon Showdown protocol
62
- - Battle results (win, lose, draw) or request states are communicated via the return code of
63
- `Battle.update`
64
- - Nicknames (`BattleMonNick`/`pokemon.name`) aren't handled by the pkmn engine as they're expected
65
- to be handled by driver code if required
66
-
67
- ==**TODO**==
68
-
69
- wPlayerMinimized
70
-
71
- ### `Battle` / `Side`
72
-
73
- `Battle` and `Side` are analogous to the classes of the
74
- [same](https://github.com/smogon/pokemon-showdown/blob/master/sim/battle.ts)
75
- [name](https://github.com/smogon/pokemon-showdown/blob/master/sim/side.ts) in Pokémon Showdown and
76
- store general information about the battle. Unlike in Pokémon Showdown there is a distinction
77
- between the data structure for the "active" Pokémon and its party members (see below).
78
-
79
- ### `Pokemon` / `ActivePokemon`
80
-
81
- Similar to the cartridge, to save space different information is stored depending on whether a
82
- [Pokémon](https://pkmn.cc/bulba/Pok%c3%a9mon_data_structure_%28Generation_II%29) is actively
83
- participating in battle vs. is switched out (pret's [`battle_struct` vs.
84
- `party_struct`](https://pkmn.cc/pokecrystal/macros/ram.asm)). In Pokémon Showdown, all the Pokemon
85
- are represented by the same
86
- [`Pokemon`](https://github.com/smogon/pokemon-showdown/blob/master/sim/pokemon.ts) class, and static
87
- party information is saved in fields beginning with "`stored`" or "`base`".
88
-
89
- ==**TODO**==
90
-
91
- #### `MoveSlot`
92
-
93
- A `MoveSlot` is a data-type for a `(move, current pp)` pair. A pared down version of Pokémon
94
- Showdown's `Pokemon#moveSlot`, it also stores data from the cartridge's `battle_struct::Move` macro
95
- and can be used to replace the `PlayerMove*` data. Move PP is stored as a full byte instead of how
96
- the cartridge (`battle_struct::PP`) stores it (6 bits for current PP and the remaining 2 bits used
97
- to store the number of applied PP Ups). PP Up bits don't actually need to be stored on move slot
98
- as max PP is never relevant in Generation II (the Mystery Berry always increases PP from 0 -> 5).
99
-
100
- #### `Status`
101
-
102
- Bitfield representation of a Pokémon's major [status
103
- condition](https://pkmn.cc/bulba/Status_condition), mirroring how it's stored on the cartridge. A
104
- value of `0x00` means that the Pokémon isn't affected by any major status, otherwise the lower 3
105
- bits represent the remaining duration for Sleep. Other status are denoted by the presence of
106
- individual bits - at most one status should be set at any given time.
107
-
108
- In Generation I & II, the "badly poisoned" status (Toxic) is instead treated as a volatile (see
109
- below).
110
-
111
- #### `Volatile` / `VolatileData`
112
-
113
- Active Pokémon can have ["volatile" status
114
- conditions](https://pkmn.cc/bulba/Status_condition#Volatile_status) (called ['sub
115
- status'](https://pkmn.cc/pokecrystal/constants/battle_constants.asm#L166-L212) bits in pret), all of
116
- which are boolean flags that are cleared when the Pokémon faints or switches out:
117
-
118
- | pkmn | Pokémon Crystal (pret) | Pokémon Showdown |
119
- | ------------- | ----------------------- | ---------------- |
120
- | `Bide` | `BIDE` | `bide` |
121
- | `Thrashing` | `RAMPAGE` | `lockedmove` |
122
- | `Flinch` | `FLINCHED` | `flinch` |
123
- | `Charging` | `CHARGED` | `twoturnmove` |
124
- | `Underground` | `UNDERGROUND` | `dig` |
125
- | `Flying` | `FLYING` | `fly` |
126
- | `Confusion` | `CONFUSED` | `confusion` |
127
- | `Mist` | `MIST` | `mist` |
128
- | `FocusEnergy` | `FOCUS_ENERGY` | `focusenergy` |
129
- | `Substitute` | `SUBSTITUTE` | `substitute` |
130
- | `Recharging` | `RECHARGE` | `mustrecharge` |
131
- | `Rage` | `RAGE` | `rage` |
132
- | `LeechSeed` | `LEECH_SEED` | `leechseed` |
133
- | `Toxic` | `TOXIC` | `toxic` |
134
- | `Transform` | `TRANSFORMED` | `transform` |
135
- | | | |
136
- | `Nightmare` | `NIGHTMARE` | `nightmare` |
137
- | `Cure` | `CURSE` | `curse` |
138
- | `Protect` | `PROTECT` | `protect` |
139
- | `Foresight` | `IDENTIFIED` | `foresight` |
140
- | `PerishSong` | `PERISH` | `perishsong` |
141
- | `Endure` | `ENDURE` | `endure` |
142
- | `Rollout` | `ROLLOUT` | `rollout` |
143
- | `Attract` | `IN_LOVE` | `attract` |
144
- | `DefenseCurl` | `CURLED` | `defensecurl` |
145
- | `Encore` | `ENCORED` | `encore` |
146
- | `LockOn` | `LOCK_ON` | `lockon` |
147
- | `DestinyBond` | `DESTINY_BOND` | `destinybond` | |
148
-
149
- ==**TODO**==
150
-
151
- - `IN_LOOP` (beatup)
152
-
153
- #### `Stats` / `Boosts`
154
-
155
- [Stats](https://pkmn.cc/bulba/Stat) and [boosts (stat
156
- modifiers)](https://pkmn.cc/bulba/Stat#Stat_modifiers) are stored logically, with the exception that
157
- boosts should always range from `-6`...`6` instead of `1`...`13` as on the cartridge.
158
-
159
- ==**TODO**==
160
-
161
- ### `Move` / `Moves`
162
-
163
- `Moves` serves as an identifier for a unique [Pokémon move](https://pkmn.cc/bulba/Move) that can be
164
- used to retrieve a `Move` with information regarding base power, accuracy and type. As covered
165
- earlier, PP information isn't strictly necessary in Generation I, but fits neatly into the 4 bits of
166
- padding after noticing that all PP values are multiples of 5. `Moves.None` exists as a special
167
- sentinel value to indicate `null`.
168
-
169
- ### `Species`
170
-
171
- `Species` just serves as an identifier for a unique [Pokémon
172
- species](https://pkmn.cc/bulba/Pokémon_species_data_structure_(Generation_II)) as the base
173
- stats of a species are already accounted for in the computed stats in the `Pokemon` structure and
174
- nothing in battle requires these to be recomputed. Similarly, Type is unnecessary to include as it
175
- is also already present in the `Pokemon` struct. `Species.None` exists as a special sentinel value
176
- to indicate `null`.
177
-
178
- ### `Type` / `Types` / `Effectiveness`
179
-
180
- The [Pokémon types](https://pkmn.cc/bulba/Type) are enumerated by `Type`. `Types` represents a tuple
181
- of 2 types, which takes the form of a packed struct instead of `[2]Type` for symmetry with
182
- Generation I. `Effectiveness` serves as an enum for tracking a moves effectiveness - like the
183
- cartridge, effectiveness is stored as `0`, `5`, `10`, and `20` (technically only a 4-bit value is
184
- required, but Zig only allows a minimum of a byte to be stored at each address of an array).
185
-
186
- Type effectiveness modifiers are applied based off of the ordering of the effectiveness table as
187
- opposed to first applying modifiers for a species's first type and then second type. The engine
188
- maintains a `Type.PRECEDENCE` table to allow for reordering type effectiveness modifier application
189
- where necessary.
190
-
191
- ## Information
192
-
193
- The information of each field (in terms of [bits of
194
- entropy](https://en.wikipedia.org/wiki/Entropy_(information_theory))) is as follows:
195
-
196
- ==**TODO**==
197
-
198
- | Data | Range | Bits | | Data | Range | Bits |
199
- | -------------- | ------- | ---- | --- | ----------------- | -------- | ---- |
200
- | **seed** | 0...255 | 8 | | **turn** | 1...1000 | 7 |
201
- | **team index** | 1...6 | 3 | | **move index** | 1...4 | 2 |
202
- | **species** | 1...251 | 8 | | **move** | 1...251 | 8 |
203
- | **stat** | 1...999 | 10 | | **boost** | 0...13 | 4 |
204
- | **level** | 1...100 | 7 | | **volatiles** | *27* | 27 |
205
- | **bide** | 0...703 | 11 | | **substitute** | 0...179 | 8 |
206
- | **multi hits** | 0...5 | 3 | | **base power** | 0...50 | 6 |
207
- | **base PP** | 1...8 | 3 | | **PP Ups** | 0...3 | 2 |
208
- | **PP** | 0...64 | 7 | | **HP / damage** | 0...704 | 10 |
209
- | **status** | 0...10 | 4 | | **effectiveness** | 0...3 | 2 |
210
- | **type** | 0...18 | 5 | | **accuracy** | 6...20 | 4 |
211
- | **disabled** | 0...7 | 3 | | **DVs** | 0...15 | 4 |
212
- | **rollout** | 0...5 | 3 | | **fury cutter** | 0..4 | 3 |
213
- | **confusion** | 0...5 | 3 | | **toxic** | 0...15 | 4 |
214
- | **encore** | 0...6 | 3 | | **future sight** | 0...2 | 2 |
215
- | **protect** | 0...9 | 4 | | **rage** | 0...255 | 8 |
216
- | **wrap** | 0...5 | 3 | | **perish song** | 0...4 | 3 |
217
- | **item** | TODO | 5? | | **gender** | 0..1 | 1 |
218
-
219
- From this one can determine the minimum bits required to store each data structure to determine how
220
- much overhead the preceding representations have after taking into consideration [alignment &
221
- padding](https://en.wikipedia.org/wiki/Data_structure_alignment) and
222
- [denormalization](https://en.wikipedia.org/wiki/Denormalization):
223
-
224
- ## Details
225
-
226
- | pkmn | Pokémon Crystal (pret) |
227
- | ------------------------- | ------------------------------------------------- |
228
- | `start` | `DoBattle` |
229
- | `update` | `BattleTurn` |
230
- | `selectMove` / `saveMove` | `CheckPlayerLockedIn` / `ParsePlayerAction` |
231
- | `switchIn` | `SwitchPlayerMon` / `SendOutPlayerMon` |
232
- | `turnOrder` | `DetermineMoveOrder` |
233
- | `doTurn` | `DoTurn` |
234
- | `executeMove` | `DoMove` |
235
- | `beforeMove` | `CheckTurn` |
236
- | `canMove` | |
237
- | `decrementPP` | |
238
- | `doMove` | |
239
- | `checkCriticalHit` | `BattleCommand_Critical` |
240
- | `calcDamage` | `BattleCommand_DamageCalc` / `PlayerAttackDamage` |
241
- | `adjustDamage` | `BattleCommand_Stab` |
242
- | `randomizeDamage` | `BattleCommand_DamageVariation` |
243
- | `specialDamage` | `BattleCommand_ConstantDamage` |
244
- | `applyDamage` | `BattleCommand_ApplyDamage` |
245
- | `checkHit` | `BattleCommand_CheckHit` |
246
- | `checkFaint` | `CheckFaint` |
247
- | `handleResidual` | `ResidualDamage` |
248
- | `endTurn` | `EndTurn` |
249
-
250
- DoBattle
251
- BattleTurn
252
- - HandleBerserkGene
253
- - CheckPlayerLockedIn
254
- - BattleMenu
255
- - ParsePlayerAction
256
- - DetermineMoveOrder
257
- - CompareMovePriority
258
- - GetMovePriority
259
- - Battle_*
260
- - Handle*Switch
261
- - SwitchPlayerMon
262
- - BreakAttraction
263
- - SendOutPlayerMon
264
- - SpikesDamage
265
- - *Turn_EndOpponentProtectEndureDestinyBond
266
- - EndUserDestinyBond
267
- - Do*Turn
268
- - EndOpponentProtectEndureDestinyBond
269
- - ResidualDamage
270
- - CheckFaint_*
271
- - HandleBetweenTurnEffects
272
- - HandleFutureSight
273
- - HandleWeather
274
- - HandleWrap
275
- - HandlePerishSong
276
- - HandleLeftovers
277
- - HandleMysteryBerry
278
- - HandleDefrost
279
- - HandleSafeguard
280
- - HandleScreens
281
- - HandleStatBoostingHeldItems
282
- - HandleHealingItems
283
- - HandleEncore
284
-
285
- DoTurn
286
- DoMove = executeMove
287
- CheckTurn = beforeMove
288
- BattleCommand_Critical = checkCriticalHit
289
- BattleCommand_Stab = adjustDamage
290
- BattleCommand_DamageVariation = randomizeDamage
291
- BattleCommand_CheckHit = checkHit
292
- BattleCommand_ApplyDamage = applyDamage
293
- BattleCommand_CheckFaint
294
- BattleCommand_BuildOpponentRage = buildRage
295
- BattleCommand_DamageCalc (PlayerAttackDamage / EnemyAttackDamage) = calcDamage
296
- BattleCommand_ConstantDamage = specialDamage
297
- DoEnemyDamage / DoPlayerDamage / DoSubstituteDamage
298
-
299
- ==**TODO**==
300
-
301
- ## Protocol
302
-
303
- TODO
304
-
305
- ```txt
306
- -activate: _, move, name, number
307
- ```
308
-
309
- ## Bugs
310
-
311
- - TODO: randomChance rate different than gen 1?
312
- - Twineedle shouldn't be able to inflict poison if it breaks a substitute
313
- - Mail should not be stolen by Thief
314
- - RNG for crit is 1/16 and not 17/256?
315
- - Struggle recoil into Substitute only 1 HP
316
- - Substitute "activates" even though doesnt take damage for jump crash damage
317
- - Pokémon specific items after Transform
318
- - OHKO moves that miss should be Counterable
319
- - Defense lowering after breaking Substitute glitch
320
- - Beserk Gene copies previous mons confusion counter
321
- - Type boosting items w/ Confusion self-hit
322
- - Spikes 0 HP glitch
323
- - PP Up + Disable freeze
324
- - Beat Up partyless should fail but should still be able to trigger Kings Rock
325
- - Beat Up desync
326
- - Lock-on accuracy should be 100 not true, should be passed
327
- - Pain Split 100 accuracy not true
328
- - Foresight ignoring accuracy/evasiveness stages
329
- - Disable duration?
330
- - type effectiveness precedence