@pkmn/engine 0.1.0-dev.83de29ee → 0.1.0-dev.89cc1c98

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 (51) hide show
  1. package/README.md +55 -44
  2. package/build/pkg/addon.js +3 -2
  3. package/build/pkg/addon.js.map +1 -1
  4. package/build/pkg/data/ids.json +12 -12
  5. package/build/pkg/data/layout.json +26 -4
  6. package/build/pkg/data/protocol.json +69 -12
  7. package/build/pkg/data.d.ts +2 -2
  8. package/build/pkg/gen1.js +23 -18
  9. package/build/pkg/gen1.js.map +1 -1
  10. package/build/pkg/index.js.map +1 -1
  11. package/build/pkg/protocol.d.ts +4 -2
  12. package/build/pkg/protocol.js +60 -39
  13. package/build/pkg/protocol.js.map +1 -1
  14. package/build/test/display.js +0 -1
  15. package/build/test/display.js.map +1 -1
  16. package/build.zig +89 -65
  17. package/package.json +41 -20
  18. package/src/bin/install-pkmn-engine +10 -5
  19. package/src/lib/binding/node.zig +46 -141
  20. package/src/lib/binding/wasm.zig +4 -2
  21. package/src/lib/common/battle.zig +17 -0
  22. package/src/lib/common/data.zig +19 -31
  23. package/src/lib/common/debug.zig +20 -11
  24. package/src/lib/common/js.zig +118 -0
  25. package/src/lib/common/optional.zig +67 -0
  26. package/src/lib/common/options.zig +9 -0
  27. package/src/lib/common/protocol.zig +750 -208
  28. package/src/lib/common/rational.zig +353 -0
  29. package/src/lib/common/rng.zig +15 -15
  30. package/src/lib/common/util.zig +12 -0
  31. package/src/lib/gen1/README.md +297 -211
  32. package/src/lib/gen1/calc.zig +426 -0
  33. package/src/lib/gen1/chance.zig +1060 -0
  34. package/src/lib/gen1/data/moves.zig +202 -190
  35. package/src/lib/gen1/data/species.zig +4 -4
  36. package/src/lib/gen1/data/types.zig +10 -10
  37. package/src/lib/gen1/data.zig +85 -73
  38. package/src/lib/gen1/helpers.zig +576 -29
  39. package/src/lib/gen1/mechanics.zig +859 -600
  40. package/src/lib/gen2/README.md +106 -85
  41. package/src/lib/gen2/calc.zig +143 -0
  42. package/src/lib/gen2/chance.zig +1314 -0
  43. package/src/lib/gen2/data/items.zig +14 -20
  44. package/src/lib/gen2/data/moves.zig +664 -570
  45. package/src/lib/gen2/data/species.zig +2 -2
  46. package/src/lib/gen2/data/types.zig +137 -34
  47. package/src/lib/gen2/data.zig +121 -85
  48. package/src/lib/gen2/generated.zig +857 -0
  49. package/src/lib/gen2/helpers.zig +16 -22
  50. package/src/lib/gen2/mechanics.zig +2998 -12
  51. package/src/lib/pkmn.zig +61 -2
@@ -0,0 +1,857 @@
1
+ //! Code generated by `tools/generate` - manual edits will be overwritten.
2
+
3
+ const std = @import("std");
4
+
5
+ const common = @import("../common/data.zig");
6
+
7
+ const data = @import("data.zig");
8
+ const mechanics = @import("mechanics.zig");
9
+
10
+ const assert = std.debug.assert;
11
+
12
+ const Player = common.Player;
13
+ const Result = common.Result;
14
+
15
+ const Effectiveness = data.Effectiveness;
16
+ const Move = data.Move;
17
+
18
+ const Effects = mechanics.Effects;
19
+ const State = mechanics.State;
20
+
21
+ const canMove = mechanics.canMove;
22
+ const canCharge = mechanics.canCharge;
23
+ const decrementPP = mechanics.decrementPP;
24
+ const doMove = mechanics.doMove;
25
+ const checkHit = mechanics.checkHit;
26
+ const checkCriticalHit = mechanics.checkCriticalHit;
27
+ const calcDamage = mechanics.calcDamage;
28
+ const adjustDamage = mechanics.adjustDamage;
29
+ const randomizeDamage = mechanics.randomizeDamage;
30
+ const applyDamage = mechanics.applyDamage;
31
+ const reportOutcome = mechanics.reportOutcome;
32
+ const effectChance = mechanics.effectChance;
33
+ const afterMove = mechanics.afterMove;
34
+ const buildRage = mechanics.buildRage;
35
+ const kingsRock = mechanics.kingsRock;
36
+ const destinyBond = mechanics.destinyBond;
37
+
38
+ const SECONDARY = true;
39
+ const KINGS = true;
40
+
41
+ pub fn runMove(battle: anytype, player: Player, state: *State, options: anytype) !void {
42
+ var log = options.log;
43
+
44
+ var side = battle.side(player);
45
+ var volatiles = &side.active.volatiles;
46
+
47
+ const ident = battle.active(player);
48
+ const foe_ident = battle.active(player.foe());
49
+
50
+ const move = Move.get(state.move);
51
+ switch (move.effect) {
52
+ .AlwaysHit, .HighCritical, .Priority, .JumpKick, .None => {
53
+ if (!try canMove(battle, player, state, options)) return;
54
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
55
+ _ = try afterMove(KINGS, battle, player, state, options);
56
+ },
57
+ .DoubleHit, .MultiHit => {
58
+ if (!try canMove(battle, player, state, options)) return;
59
+ // TODO startloop
60
+ try checkHit(battle, player, state, options);
61
+ try checkCriticalHit(battle, player, state, options);
62
+ try calcDamage(battle, player, state, options);
63
+ try adjustDamage(battle, player, state, options);
64
+ try randomizeDamage(battle, player, state, options);
65
+
66
+ if (state.miss) state.damage = 0;
67
+
68
+ try applyDamage(battle, player, state, options);
69
+ // TODO critsupereffectivelooptext
70
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
71
+ // TODO endloop
72
+ try kingsRock(battle, player, state, options);
73
+ },
74
+ .PayDay => {
75
+ if (!try canMove(battle, player, state, options)) return;
76
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
77
+ try Effects.payDay(battle, player, state, options);
78
+ _ = try afterMove(KINGS, battle, player, state, options);
79
+ },
80
+ .BurnChance => {
81
+ if (!try canMove(battle, player, state, options)) return;
82
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
83
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
84
+ try Effects.burnChance(battle, player, state, options);
85
+ },
86
+ .FreezeChance => {
87
+ if (!try canMove(battle, player, state, options)) return;
88
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
89
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
90
+ try Effects.freezeChance(battle, player, state, options);
91
+ },
92
+ .ParalyzeChance => {
93
+ if (!try canMove(battle, player, state, options)) return;
94
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
95
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
96
+ try Effects.paralyzeChance(battle, player, state, options);
97
+ },
98
+ .OHKO => {
99
+ if (!try canMove(battle, player, state, options)) return;
100
+
101
+ // OHKO calls adjustDamage but only actually cares about immunity
102
+ assert(move.type == .Normal or move.type == .Ground);
103
+ const foe = battle.foe(player);
104
+ const immune = if (move.type == .Normal)
105
+ foe.active.types.includes(.Ghost) and !foe.active.volatiles.Foresight
106
+ else
107
+ foe.active.types.includes(.Ghost);
108
+ state.effectiveness = if (immune) 0 else Effectiveness.neutral;
109
+
110
+ try Effects.ohko(battle, player, state, options);
111
+ try applyDamage(battle, player, state, options);
112
+ try reportOutcome(battle, player, state, options);
113
+ _ = try afterMove(!KINGS, battle, player, state, options);
114
+ },
115
+ .Gust => {
116
+ if (!try canMove(battle, player, state, options)) return;
117
+ try checkCriticalHit(battle, player, state, options);
118
+ try calcDamage(battle, player, state, options);
119
+ try adjustDamage(battle, player, state, options);
120
+ try randomizeDamage(battle, player, state, options);
121
+
122
+ if (battle.foe(player).active.volatiles.Flying) state.damage *|= 2;
123
+
124
+ try checkHit(battle, player, state, options);
125
+ try applyDamage(battle, player, state, options);
126
+ try reportOutcome(battle, player, state, options);
127
+ _ = try afterMove(!KINGS, battle, player, state, options);
128
+ },
129
+ .ForceSwitch => {
130
+ if (!try canMove(battle, player, state, options)) return;
131
+ try checkHit(battle, player, state, options);
132
+ try Effects.forceSwitch(battle, player, state, options);
133
+ },
134
+ .RazorWind, .SolarBeam, .FlyDig => {
135
+ if (!try canCharge(battle, player, state, options)) return;
136
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
137
+ _ = try afterMove(KINGS, battle, player, state, options);
138
+ },
139
+ .Binding => {
140
+ if (!try canMove(battle, player, state, options)) return;
141
+ try checkHit(battle, player, state, options);
142
+ try checkCriticalHit(battle, player, state, options);
143
+ try calcDamage(battle, player, state, options);
144
+ try adjustDamage(battle, player, state, options);
145
+ try randomizeDamage(battle, player, state, options);
146
+
147
+ if (state.miss) state.damage = 0;
148
+
149
+ try applyDamage(battle, player, state, options);
150
+ try reportOutcome(battle, player, state, options);
151
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
152
+ try Effects.binding(battle, player, state, options);
153
+ },
154
+ .Stomp => {
155
+ if (!try canMove(battle, player, state, options)) return;
156
+ try checkCriticalHit(battle, player, state, options);
157
+ try calcDamage(battle, player, state, options);
158
+ try adjustDamage(battle, player, state, options);
159
+ try randomizeDamage(battle, player, state, options);
160
+
161
+ if (battle.foe(player).active.volatiles.minimized) state.damage *|= 2;
162
+
163
+ try checkHit(battle, player, state, options);
164
+ try effectChance(battle, player, state, options);
165
+ try applyDamage(battle, player, state, options);
166
+ try reportOutcome(battle, player, state, options);
167
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
168
+ try Effects.flinchChance(battle, player, state, options);
169
+ },
170
+ .FlinchChance => {
171
+ if (!try canMove(battle, player, state, options)) return;
172
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
173
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
174
+ try Effects.flinchChance(battle, player, state, options);
175
+ },
176
+ .Recoil => {
177
+ if (!try canMove(battle, player, state, options)) return;
178
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
179
+ try Effects.recoil(battle, player, state, options);
180
+ _ = try afterMove(KINGS, battle, player, state, options);
181
+ },
182
+ .Thrashing => {
183
+ // TODO checkrampage
184
+ // doturn
185
+ const charging = false; // TODO
186
+ const skip_pp = charging or state.move == .Struggle or
187
+ (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
188
+ if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
189
+ // TODO rampage
190
+ // usedmovetext
191
+ try log.move(ident, state.move, foe_ident); // FIXME self? from?
192
+ try checkHit(battle, player, state, options);
193
+ try checkCriticalHit(battle, player, state, options);
194
+ try calcDamage(battle, player, state, options);
195
+ try adjustDamage(battle, player, state, options);
196
+ try randomizeDamage(battle, player, state, options);
197
+
198
+ if (state.miss) state.damage = 0;
199
+
200
+ try applyDamage(battle, player, state, options);
201
+ try reportOutcome(battle, player, state, options);
202
+ _ = try afterMove(KINGS, battle, player, state, options);
203
+ },
204
+ .PoisonChance => {
205
+ if (!try canMove(battle, player, state, options)) return;
206
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
207
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
208
+ try Effects.poisonChance(battle, player, state, options);
209
+ },
210
+ .Twineedle => {
211
+ if (!try canMove(battle, player, state, options)) return;
212
+ // TODO startloop
213
+ try checkHit(battle, player, state, options);
214
+ try effectChance(battle, player, state, options);
215
+ try checkCriticalHit(battle, player, state, options);
216
+ try calcDamage(battle, player, state, options);
217
+ try adjustDamage(battle, player, state, options);
218
+ try randomizeDamage(battle, player, state, options);
219
+
220
+ if (state.miss) state.damage = 0;
221
+
222
+ try applyDamage(battle, player, state, options);
223
+ // TODO critsupereffectivelooptext
224
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
225
+ // TODO endloop
226
+ try kingsRock(battle, player, state, options);
227
+ try Effects.poisonChance(battle, player, state, options);
228
+ },
229
+ .Sleep => {
230
+ if (!try canMove(battle, player, state, options)) return;
231
+ try checkHit(battle, player, state, options);
232
+ // TODO checksafeguard
233
+ try Effects.sleep(battle, player, state, options);
234
+ },
235
+ .Confusion => {
236
+ if (!try canMove(battle, player, state, options)) return;
237
+ try checkHit(battle, player, state, options);
238
+ // TODO checksafeguard
239
+ try Effects.confusion(battle, player, state, options);
240
+ },
241
+ .SuperFang, .LevelDamage, .Psywave, .FixedDamage => {
242
+ if (!try canMove(battle, player, state, options)) return;
243
+ try Effects.fixedDamage(battle, player, state, options);
244
+ try checkHit(battle, player, state, options);
245
+
246
+ if (state.immune()) {
247
+ state.damage = 0;
248
+ state.miss = true;
249
+ }
250
+ state.effectiveness = Effectiveness.neutral;
251
+
252
+ try applyDamage(battle, player, state, options);
253
+ _ = try afterMove(KINGS, battle, player, state, options);
254
+ },
255
+ .Disable => {
256
+ if (!try canMove(battle, player, state, options)) return;
257
+ try checkHit(battle, player, state, options);
258
+ try Effects.disable(battle, player, state, options);
259
+ },
260
+ .Mist => {
261
+ if (!try canMove(battle, player, state, options)) return;
262
+ try Effects.mist(battle, player, state, options);
263
+ },
264
+ .ConfusionChance => {
265
+ if (!try canMove(battle, player, state, options)) return;
266
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
267
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
268
+ try Effects.confusionChance(battle, player, state, options);
269
+ },
270
+ .HyperBeam => {
271
+ if (!try canMove(battle, player, state, options)) return;
272
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
273
+ try Effects.hyperBeam(battle, player, state, options);
274
+ _ = try afterMove(!KINGS, battle, player, state, options);
275
+ },
276
+ .Counter => {
277
+ if (!try canMove(battle, player, state, options)) return;
278
+ try Effects.counter(battle, player, state, options);
279
+ try applyDamage(battle, player, state, options);
280
+ _ = try afterMove(KINGS, battle, player, state, options);
281
+ },
282
+ .DreamEater, .DrainHP => {
283
+ if (!try canMove(battle, player, state, options)) return;
284
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
285
+ try Effects.drainHP(battle, player, state, options);
286
+ _ = try afterMove(KINGS, battle, player, state, options);
287
+ },
288
+ .LeechSeed => {
289
+ if (!try canMove(battle, player, state, options)) return;
290
+ try checkHit(battle, player, state, options);
291
+ try Effects.leechSeed(battle, player, state, options);
292
+ },
293
+ .Toxic, .Poison => {
294
+ if (!try canMove(battle, player, state, options)) return;
295
+ try checkHit(battle, player, state, options);
296
+ try adjustDamage(battle, player, state, options);
297
+ // TODO checksafeguard
298
+ try Effects.poison(battle, player, state, options);
299
+ },
300
+ .Paralyze => {
301
+ if (!try canMove(battle, player, state, options)) return;
302
+ try adjustDamage(battle, player, state, options);
303
+ try checkHit(battle, player, state, options);
304
+ // TODO checksafeguard
305
+ try Effects.paralyze(battle, player, state, options);
306
+ },
307
+ .Thunder => {
308
+ if (!try canMove(battle, player, state, options)) return;
309
+ try checkCriticalHit(battle, player, state, options);
310
+ try calcDamage(battle, player, state, options);
311
+ // TODO thunderaccuracy
312
+ try checkHit(battle, player, state, options);
313
+ try effectChance(battle, player, state, options);
314
+ try adjustDamage(battle, player, state, options);
315
+ try randomizeDamage(battle, player, state, options);
316
+ try applyDamage(battle, player, state, options);
317
+ try reportOutcome(battle, player, state, options);
318
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
319
+ try Effects.paralyzeChance(battle, player, state, options);
320
+ },
321
+ .Earthquake => {
322
+ if (!try canMove(battle, player, state, options)) return;
323
+ try checkCriticalHit(battle, player, state, options);
324
+ try calcDamage(battle, player, state, options);
325
+ try adjustDamage(battle, player, state, options);
326
+ try randomizeDamage(battle, player, state, options);
327
+
328
+ if (battle.foe(player).active.volatiles.Underground) state.damage *|= 2;
329
+
330
+ try checkHit(battle, player, state, options);
331
+ try effectChance(battle, player, state, options);
332
+ try applyDamage(battle, player, state, options);
333
+ try reportOutcome(battle, player, state, options);
334
+ _ = try afterMove(!KINGS, battle, player, state, options);
335
+ },
336
+ .Rage => {
337
+ if (!try canMove(battle, player, state, options)) return;
338
+ try checkCriticalHit(battle, player, state, options);
339
+ try calcDamage(battle, player, state, options);
340
+ try adjustDamage(battle, player, state, options);
341
+ try checkHit(battle, player, state, options);
342
+
343
+ assert(volatiles.Rage);
344
+ state.damage *|= (volatiles.rage +| 1);
345
+
346
+ try randomizeDamage(battle, player, state, options);
347
+ // TODO failuretext
348
+ try Effects.rage(battle, player, state, options);
349
+ try applyDamage(battle, player, state, options);
350
+ try reportOutcome(battle, player, state, options);
351
+ _ = try afterMove(KINGS, battle, player, state, options);
352
+ },
353
+ .Teleport => {
354
+ if (!try canMove(battle, player, state, options)) return;
355
+ try Effects.teleport(battle, player, state, options);
356
+ },
357
+ .Mimic => {
358
+ if (!try canMove(battle, player, state, options)) return;
359
+ try checkHit(battle, player, state, options);
360
+ try Effects.mimic(battle, player, state, options);
361
+ },
362
+ .Heal => {
363
+ if (!try canMove(battle, player, state, options)) return;
364
+ try Effects.heal(battle, player, state, options);
365
+ },
366
+ .Haze => {
367
+ if (!try canMove(battle, player, state, options)) return;
368
+ try Effects.haze(battle, player, state, options);
369
+ },
370
+ .LightScreen, .Reflect => {
371
+ if (!try canMove(battle, player, state, options)) return;
372
+ try Effects.screens(battle, player, state, options);
373
+ },
374
+ .FocusEnergy => {
375
+ if (!try canMove(battle, player, state, options)) return;
376
+ try Effects.focusEnergy(battle, player, state, options);
377
+ },
378
+ .Bide => {
379
+ if (!volatiles.Bide) return;
380
+
381
+ assert(volatiles.attacks > 0);
382
+ // if (options.calc.modify(player, .attacking)) |extend| {
383
+ // if (!extend) volatiles.attacks = 0;
384
+ // } else {
385
+ volatiles.attacks -= 1;
386
+ // }
387
+ try options.chance.attacking(player, volatiles.attacks);
388
+
389
+ if (volatiles.attacks != 0) return try options.log.activate(ident, .Bide);
390
+
391
+ volatiles.Bide = false;
392
+ try options.log.end(ident, .Bide);
393
+
394
+ state.damage = volatiles.bide *% 2;
395
+ volatiles.bide = 0;
396
+
397
+ if (state.damage == 0) state.miss = true;
398
+
399
+ // doturn
400
+ const charging = false; // TODO
401
+ const skip_pp = charging or state.move == .Struggle or
402
+ (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
403
+ if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
404
+ // usedmovetext
405
+ try log.move(ident, state.move, foe_ident); // FIXME self? from?
406
+ // TODO unleashenergy
407
+
408
+ if (state.immune()) {
409
+ state.damage = 0;
410
+ state.miss = true;
411
+ }
412
+ state.effectiveness = Effectiveness.neutral;
413
+
414
+ try checkHit(battle, player, state, options);
415
+ // TODO bidefailtext
416
+ try applyDamage(battle, player, state, options);
417
+ _ = try afterMove(KINGS, battle, player, state, options);
418
+ },
419
+ .Metronome => {
420
+ if (!try canMove(battle, player, state, options)) return;
421
+ try Effects.metronome(battle, player, state, options);
422
+ },
423
+ .MirrorMove => {
424
+ if (!try canMove(battle, player, state, options)) return;
425
+ try Effects.mirrorMove(battle, player, state, options);
426
+ },
427
+ .Explode => {
428
+ if (!try canMove(battle, player, state, options)) return;
429
+ try checkCriticalHit(battle, player, state, options);
430
+ try calcDamage(battle, player, state, options);
431
+ try adjustDamage(battle, player, state, options);
432
+ try randomizeDamage(battle, player, state, options);
433
+ try checkHit(battle, player, state, options);
434
+ try Effects.explode(battle, player, state, options);
435
+ try applyDamage(battle, player, state, options);
436
+ try reportOutcome(battle, player, state, options);
437
+ _ = try afterMove(KINGS, battle, player, state, options);
438
+ },
439
+ .SkullBash => {
440
+ if (!try canCharge(battle, player, state, options)) return;
441
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
442
+ if (!try afterMove(KINGS, battle, player, state, options)) return;
443
+ // TODO endturn
444
+ try Effects.boost(battle, player, state, options);
445
+ },
446
+ .SkyAttack => {
447
+ if (!try canCharge(battle, player, state, options)) return;
448
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
449
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
450
+ try Effects.flinchChance(battle, player, state, options);
451
+ try kingsRock(battle, player, state, options);
452
+ },
453
+ .Transform => {
454
+ if (!try canMove(battle, player, state, options)) return;
455
+ try Effects.transform(battle, player, state, options);
456
+ },
457
+ .Splash => {
458
+ if (!try canMove(battle, player, state, options)) return;
459
+ try Effects.splash(battle, player, state, options);
460
+ },
461
+ .Conversion => {
462
+ if (!try canMove(battle, player, state, options)) return;
463
+ try Effects.conversion(battle, player, state, options);
464
+ },
465
+ .TriAttack => {
466
+ if (!try canMove(battle, player, state, options)) return;
467
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
468
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
469
+ try Effects.triAttack(battle, player, state, options);
470
+ },
471
+ .Substitute => {
472
+ if (!try canMove(battle, player, state, options)) return;
473
+ try Effects.substitute(battle, player, state, options);
474
+ },
475
+ .Sketch => {
476
+ if (!try canMove(battle, player, state, options)) return;
477
+ try Effects.sketch(battle, player, state, options);
478
+ },
479
+ .TripleKick => {
480
+ if (!try canMove(battle, player, state, options)) return;
481
+ // TODO startloop
482
+ try checkHit(battle, player, state, options);
483
+ try checkCriticalHit(battle, player, state, options);
484
+ try calcDamage(battle, player, state, options);
485
+ try Effects.tripleKick(battle, player, state, options);
486
+ try adjustDamage(battle, player, state, options);
487
+ try randomizeDamage(battle, player, state, options);
488
+
489
+ if (state.miss) state.damage = 0;
490
+
491
+ try applyDamage(battle, player, state, options);
492
+ // TODO critsupereffectivelooptext
493
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
494
+ // TODO kickcounter
495
+ // TODO endloop
496
+ try kingsRock(battle, player, state, options);
497
+ },
498
+ .Thief => {
499
+ if (!try canMove(battle, player, state, options)) return;
500
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
501
+ try Effects.thief(battle, player, state, options);
502
+ _ = try afterMove(KINGS, battle, player, state, options);
503
+ },
504
+ .MeanLook => {
505
+ if (!try canMove(battle, player, state, options)) return;
506
+ try Effects.meanLook(battle, player, state, options);
507
+ },
508
+ .LockOn => {
509
+ if (!try canMove(battle, player, state, options)) return;
510
+ try checkHit(battle, player, state, options);
511
+ try Effects.lockOn(battle, player, state, options);
512
+ },
513
+ .Nightmare => {
514
+ if (!try canMove(battle, player, state, options)) return;
515
+ try Effects.nightmare(battle, player, state, options);
516
+ },
517
+ .Snore => {
518
+ if (!try canMove(battle, player, state, options)) return;
519
+ try checkCriticalHit(battle, player, state, options);
520
+ try calcDamage(battle, player, state, options);
521
+ try adjustDamage(battle, player, state, options);
522
+ try randomizeDamage(battle, player, state, options);
523
+ try checkHit(battle, player, state, options);
524
+ try effectChance(battle, player, state, options);
525
+ try Effects.snore(battle, player, state, options);
526
+ try applyDamage(battle, player, state, options);
527
+ try reportOutcome(battle, player, state, options);
528
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
529
+ try Effects.flinchChance(battle, player, state, options);
530
+ try kingsRock(battle, player, state, options);
531
+ },
532
+ .Curse => {
533
+ if (!try canMove(battle, player, state, options)) return;
534
+ try Effects.curse(battle, player, state, options);
535
+ },
536
+ .Reversal => {
537
+ if (!try canMove(battle, player, state, options)) return;
538
+ try Effects.reversal(battle, player, state, options);
539
+ try adjustDamage(battle, player, state, options);
540
+ try checkHit(battle, player, state, options);
541
+ try applyDamage(battle, player, state, options);
542
+ // TODO reversalsupereffectivetext
543
+ _ = try afterMove(KINGS, battle, player, state, options);
544
+ },
545
+ .Conversion2 => {
546
+ if (!try canMove(battle, player, state, options)) return;
547
+ try checkHit(battle, player, state, options);
548
+ try Effects.conversion2(battle, player, state, options);
549
+ },
550
+ .Spite => {
551
+ if (!try canMove(battle, player, state, options)) return;
552
+ try checkHit(battle, player, state, options);
553
+ try Effects.spite(battle, player, state, options);
554
+ },
555
+ .Endure, .Protect => {
556
+ if (!try canMove(battle, player, state, options)) return;
557
+ try Effects.protect(battle, player, state, options);
558
+ },
559
+ .BellyDrum => {
560
+ if (!try canMove(battle, player, state, options)) return;
561
+ try Effects.bellyDrum(battle, player, state, options);
562
+ },
563
+ .Spikes => {
564
+ if (!try canMove(battle, player, state, options)) return;
565
+ try Effects.spikes(battle, player, state, options);
566
+ },
567
+ .Foresight => {
568
+ if (!try canMove(battle, player, state, options)) return;
569
+ try checkHit(battle, player, state, options);
570
+ try Effects.foresight(battle, player, state, options);
571
+ },
572
+ .DestinyBond => {
573
+ if (!try canMove(battle, player, state, options)) return;
574
+ try Effects.destinyBond(battle, player, state, options);
575
+ },
576
+ .PerishSong => {
577
+ if (!try canMove(battle, player, state, options)) return;
578
+ try Effects.perishSong(battle, player, state, options);
579
+ },
580
+ .Sandstorm => {
581
+ if (!try canMove(battle, player, state, options)) return;
582
+ try Effects.sandstorm(battle, player, state, options);
583
+ },
584
+ .Rollout => {
585
+ // TODO checkcurl
586
+ // doturn
587
+ const charging = false; // TODO
588
+ const skip_pp = charging or state.move == .Struggle or
589
+ (volatiles.BeatUp or volatiles.Thrashing or volatiles.Bide);
590
+ if (!skip_pp) _ = decrementPP(side, state.move, state.mslot); // TODO if no pp return
591
+ // usedmovetext
592
+ try log.move(ident, state.move, foe_ident); // FIXME self? from?
593
+ try checkCriticalHit(battle, player, state, options);
594
+ try calcDamage(battle, player, state, options);
595
+ try adjustDamage(battle, player, state, options);
596
+ try checkHit(battle, player, state, options);
597
+ // TODO rolloutpower
598
+ try randomizeDamage(battle, player, state, options);
599
+ try applyDamage(battle, player, state, options);
600
+ try reportOutcome(battle, player, state, options);
601
+ _ = try afterMove(KINGS, battle, player, state, options);
602
+ },
603
+ .FalseSwipe => {
604
+ if (!try canMove(battle, player, state, options)) return;
605
+ try checkCriticalHit(battle, player, state, options);
606
+ try calcDamage(battle, player, state, options);
607
+ try adjustDamage(battle, player, state, options);
608
+ try randomizeDamage(battle, player, state, options);
609
+ try Effects.falseSwipe(battle, player, state, options);
610
+ try checkHit(battle, player, state, options);
611
+ try applyDamage(battle, player, state, options);
612
+ try reportOutcome(battle, player, state, options);
613
+ _ = try afterMove(KINGS, battle, player, state, options);
614
+ },
615
+ .Swagger => {
616
+ if (!try canMove(battle, player, state, options)) return;
617
+ try checkHit(battle, player, state, options);
618
+ // TODO switchturn
619
+ // TODO attackup2
620
+ // TODO switchturn
621
+ // TODO failuretext
622
+ // TODO switchturn
623
+ // TODO switchturn
624
+ try Effects.confusionChance(battle, player, state, options);
625
+ },
626
+ .FuryCutter => {
627
+ if (!try canMove(battle, player, state, options)) return;
628
+ try checkCriticalHit(battle, player, state, options);
629
+ try calcDamage(battle, player, state, options);
630
+ try adjustDamage(battle, player, state, options);
631
+ try checkHit(battle, player, state, options);
632
+ try Effects.furyCutter(battle, player, state, options);
633
+ try randomizeDamage(battle, player, state, options);
634
+ try applyDamage(battle, player, state, options);
635
+ try reportOutcome(battle, player, state, options);
636
+ _ = try afterMove(KINGS, battle, player, state, options);
637
+ },
638
+ .Attract => {
639
+ if (!try canMove(battle, player, state, options)) return;
640
+ try checkHit(battle, player, state, options);
641
+ try Effects.attract(battle, player, state, options);
642
+ },
643
+ .SleepTalk => {
644
+ if (!try canMove(battle, player, state, options)) return;
645
+ try Effects.sleepTalk(battle, player, state, options);
646
+ },
647
+ .HealBell => {
648
+ if (!try canMove(battle, player, state, options)) return;
649
+ try Effects.healBell(battle, player, state, options);
650
+ },
651
+ .Frustration, .Return => {
652
+ if (!try canMove(battle, player, state, options)) return;
653
+ try checkCriticalHit(battle, player, state, options);
654
+ try Effects.happiness(battle, player, state, options);
655
+ try calcDamage(battle, player, state, options);
656
+ try adjustDamage(battle, player, state, options);
657
+ try randomizeDamage(battle, player, state, options);
658
+ try checkHit(battle, player, state, options);
659
+ try applyDamage(battle, player, state, options);
660
+ try reportOutcome(battle, player, state, options);
661
+ _ = try afterMove(KINGS, battle, player, state, options);
662
+ },
663
+ .Present => {
664
+ if (!try canMove(battle, player, state, options)) return;
665
+ try checkHit(battle, player, state, options);
666
+ try checkCriticalHit(battle, player, state, options);
667
+ try Effects.present(battle, player, state, options);
668
+ try calcDamage(battle, player, state, options);
669
+ try adjustDamage(battle, player, state, options);
670
+ try randomizeDamage(battle, player, state, options);
671
+
672
+ if (state.miss) state.damage = 0;
673
+
674
+ try applyDamage(battle, player, state, options);
675
+ try reportOutcome(battle, player, state, options);
676
+ _ = try afterMove(KINGS, battle, player, state, options);
677
+ },
678
+ .Safeguard => {
679
+ if (!try canMove(battle, player, state, options)) return;
680
+ try Effects.safeguard(battle, player, state, options);
681
+ },
682
+ .PainSplit => {
683
+ if (!try canMove(battle, player, state, options)) return;
684
+ try checkHit(battle, player, state, options);
685
+ try Effects.painSplit(battle, player, state, options);
686
+ },
687
+ .FlameWheel, .SacredFire => {
688
+ if (!try canMove(battle, player, state, options)) return;
689
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
690
+ try Effects.defrost(battle, player, state, options);
691
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
692
+ try Effects.burnChance(battle, player, state, options);
693
+ },
694
+ .Magnitude => {
695
+ if (!try canMove(battle, player, state, options)) return;
696
+ try checkCriticalHit(battle, player, state, options);
697
+ // TODO getmagnitude
698
+ try calcDamage(battle, player, state, options);
699
+ try adjustDamage(battle, player, state, options);
700
+ try randomizeDamage(battle, player, state, options);
701
+ try checkHit(battle, player, state, options);
702
+
703
+ if (battle.foe(player).active.volatiles.Underground) state.damage *|= 2;
704
+
705
+ try applyDamage(battle, player, state, options);
706
+ try reportOutcome(battle, player, state, options);
707
+ _ = try afterMove(KINGS, battle, player, state, options);
708
+ },
709
+ .BatonPass => {
710
+ if (!try canMove(battle, player, state, options)) return;
711
+ try Effects.batonPass(battle, player, state, options);
712
+ },
713
+ .Encore => {
714
+ if (!try canMove(battle, player, state, options)) return;
715
+ try checkHit(battle, player, state, options);
716
+ try Effects.encore(battle, player, state, options);
717
+ },
718
+ .Pursuit => {
719
+ if (!try canMove(battle, player, state, options)) return;
720
+ try checkCriticalHit(battle, player, state, options);
721
+ try calcDamage(battle, player, state, options);
722
+ try adjustDamage(battle, player, state, options);
723
+ try randomizeDamage(battle, player, state, options);
724
+ try Effects.pursuit(battle, player, state, options);
725
+ try checkHit(battle, player, state, options);
726
+ try applyDamage(battle, player, state, options);
727
+ try reportOutcome(battle, player, state, options);
728
+ _ = try afterMove(KINGS, battle, player, state, options);
729
+ },
730
+ .RapidSpin => {
731
+ if (!try canMove(battle, player, state, options)) return;
732
+ if (!try doMove(!SECONDARY, battle, player, state, options)) return;
733
+ try Effects.rapidSpin(battle, player, state, options);
734
+ _ = try afterMove(KINGS, battle, player, state, options);
735
+ },
736
+ .MorningSun, .Synthesis, .Moonlight => {
737
+ if (!try canMove(battle, player, state, options)) return;
738
+ try Effects.weatherHeal(battle, player, state, options);
739
+ },
740
+ .HiddenPower => {
741
+ if (!try canMove(battle, player, state, options)) return;
742
+ try checkCriticalHit(battle, player, state, options);
743
+ try Effects.hiddenPower(battle, player, state, options);
744
+ try calcDamage(battle, player, state, options);
745
+ try adjustDamage(battle, player, state, options);
746
+ try randomizeDamage(battle, player, state, options);
747
+ try checkHit(battle, player, state, options);
748
+ try applyDamage(battle, player, state, options);
749
+ try reportOutcome(battle, player, state, options);
750
+ _ = try afterMove(KINGS, battle, player, state, options);
751
+ },
752
+ .Twister => {
753
+ if (!try canMove(battle, player, state, options)) return;
754
+ try checkCriticalHit(battle, player, state, options);
755
+ try calcDamage(battle, player, state, options);
756
+ try adjustDamage(battle, player, state, options);
757
+ try randomizeDamage(battle, player, state, options);
758
+
759
+ if (battle.foe(player).active.volatiles.Flying) state.damage *|= 2;
760
+
761
+ try checkHit(battle, player, state, options);
762
+ try effectChance(battle, player, state, options);
763
+ try applyDamage(battle, player, state, options);
764
+ try reportOutcome(battle, player, state, options);
765
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
766
+ try Effects.flinchChance(battle, player, state, options);
767
+ },
768
+ .RainDance => {
769
+ if (!try canMove(battle, player, state, options)) return;
770
+ try Effects.rainDance(battle, player, state, options);
771
+ },
772
+ .SunnyDay => {
773
+ if (!try canMove(battle, player, state, options)) return;
774
+ try Effects.sunnyDay(battle, player, state, options);
775
+ },
776
+ .MirrorCoat => {
777
+ if (!try canMove(battle, player, state, options)) return;
778
+ try Effects.mirrorCoat(battle, player, state, options);
779
+ try applyDamage(battle, player, state, options);
780
+ _ = try afterMove(KINGS, battle, player, state, options);
781
+ },
782
+ .PsychUp => {
783
+ if (!try canMove(battle, player, state, options)) return;
784
+ try Effects.psychUp(battle, player, state, options);
785
+ },
786
+ .AllStatUpChance => {
787
+ if (!try canMove(battle, player, state, options)) return;
788
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
789
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
790
+ try Effects.allStatUpChance(battle, player, state, options);
791
+ },
792
+ .FutureSight => {
793
+ // TODO checkfuturesight
794
+ if (!try canMove(battle, player, state, options)) return;
795
+ try calcDamage(battle, player, state, options);
796
+ try Effects.futureSight(battle, player, state, options);
797
+ try randomizeDamage(battle, player, state, options);
798
+ try checkHit(battle, player, state, options);
799
+ try applyDamage(battle, player, state, options);
800
+ _ = try afterMove(!KINGS, battle, player, state, options);
801
+ },
802
+ .BeatUp => {
803
+ if (!try canMove(battle, player, state, options)) return;
804
+ // TODO startloop
805
+ try checkHit(battle, player, state, options);
806
+ try checkCriticalHit(battle, player, state, options);
807
+ try Effects.beatUp(battle, player, state, options);
808
+ try randomizeDamage(battle, player, state, options);
809
+
810
+ if (state.miss) state.damage = 0;
811
+
812
+ try applyDamage(battle, player, state, options);
813
+ try reportOutcome(battle, player, state, options);
814
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
815
+ // TODO endloop
816
+ // TODO beatupfailtext
817
+ try kingsRock(battle, player, state, options);
818
+ },
819
+ // zig fmt: off
820
+ .AttackUp1, .AttackUp2, .DefenseCurl, .DefenseUp1, .DefenseUp2,
821
+ .EvasionUp1, .SpAtkUp1, .SpDefUp2, .SpeedUp2 => {
822
+ // zig fmt: on
823
+ _ = try canMove(battle, player, state, options);
824
+ try Effects.boost(battle, player, state, options);
825
+ if (move.effect == .DefenseCurl) {
826
+ try Effects.defenseCurl(battle, player, state, options);
827
+ }
828
+ },
829
+ .AttackUpChance, .DefenseUpChance => {
830
+ _ = try canMove(battle, player, state, options);
831
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
832
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
833
+ try Effects.boost(battle, player, state, options);
834
+ },
835
+ // zig fmt: off
836
+ .AccuracyDown1, .AttackDown1, .AttackDown2, .DefenseDown1,
837
+ .DefenseDown2, .EvasionDown1, .SpeedDown1, .SpeedDown2 => {
838
+ // zig fmt: on
839
+ _ = try canMove(battle, player, state, options);
840
+ try checkHit(battle, player, state, options);
841
+ try Effects.unboost(battle, player, state, options);
842
+ },
843
+ // zig fmt: off
844
+ .AccuracyDownChance, .AttackDownChance, .DefenseDownChance,
845
+ .SpDefDownChance, .SpeedDownChance => {
846
+ // zig fmt: on
847
+ _ = try canMove(battle, player, state, options);
848
+ if (!try doMove(SECONDARY, battle, player, state, options)) return;
849
+ if (!try afterMove(!KINGS, battle, player, state, options)) return;
850
+ // GLITCH: moves that lower Defense can do so after breaking a Substitute
851
+ if (move.effect == .DefenseDownChance) {
852
+ try effectChance(battle, player, state, options);
853
+ }
854
+ try Effects.unboost(battle, player, state, options);
855
+ },
856
+ }
857
+ }