@pkmn/engine 0.1.0-dev.cf6f5d2c → 0.1.0-dev.d97c6e03
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/README.md +18 -18
- package/build/pkg/addon.d.ts +1 -1
- package/build/pkg/addon.js +4 -4
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/gen1.d.ts +3 -3
- package/build/pkg/gen1.js +12 -10
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +10 -11
- package/build/pkg/index.js.map +1 -1
- package/build/test/display.js +9 -22
- package/build/test/display.js.map +1 -1
- package/build.zig +7 -7
- package/package.json +9 -10
- package/src/bin/install-pkmn-engine +3 -3
- package/src/lib/binding/node.zig +1 -1
- package/src/lib/binding/wasm.zig +1 -1
- package/src/lib/common/debug.zig +0 -1
- package/src/lib/common/options.zig +3 -3
- package/src/lib/common/protocol.zig +38 -38
- package/src/lib/gen1/README.md +22 -35
- package/src/lib/gen1/data.zig +1 -1
- package/src/lib/gen1/helpers.zig +7 -10
- package/src/lib/gen1/mechanics.zig +55 -109
- package/src/lib/pkmn.zig +4 -4
|
@@ -62,28 +62,26 @@ pub fn update(battle: anytype, c1: Choice, c2: Choice, log: anytype) !Result {
|
|
|
62
62
|
assert(c1.type != .Pass or c2.type != .Pass or battle.turn == 0);
|
|
63
63
|
if (battle.turn == 0) return start(battle, log);
|
|
64
64
|
|
|
65
|
-
var s1 = false;
|
|
66
|
-
var s2 = false;
|
|
67
65
|
var f1: ?Move = null;
|
|
68
66
|
var f2: ?Move = null;
|
|
69
67
|
|
|
70
|
-
if (selectMove(battle, .P1, c1, c2, &
|
|
71
|
-
if (selectMove(battle, .P2, c2, c1, &
|
|
72
|
-
|
|
73
|
-
var p1 = battle.side(.P1);
|
|
74
|
-
var p2 = battle.side(.P2);
|
|
75
|
-
|
|
76
|
-
var r1 = showdown and p1.active.volatiles.Binding and c2.type == .Switch;
|
|
77
|
-
var r2 = showdown and p2.active.volatiles.Binding and c1.type == .Switch;
|
|
68
|
+
if (selectMove(battle, .P1, c1, c2, &f1)) |r| return r;
|
|
69
|
+
if (selectMove(battle, .P2, c2, c1, &f2)) |r| return r;
|
|
78
70
|
|
|
79
71
|
if (turnOrder(battle, c1, c2) == .P1) {
|
|
80
|
-
if (try doTurn(battle, .P1, c1,
|
|
72
|
+
if (try doTurn(battle, .P1, c1, f1, .P2, c2, f2, log)) |r| return r;
|
|
81
73
|
} else {
|
|
82
|
-
if (try doTurn(battle, .P2, c2,
|
|
74
|
+
if (try doTurn(battle, .P2, c2, f2, .P1, c1, f1, log)) |r| return r;
|
|
83
75
|
}
|
|
84
76
|
|
|
85
|
-
|
|
86
|
-
if (
|
|
77
|
+
var p1 = battle.side(.P1);
|
|
78
|
+
if (p1.active.volatiles.attacks == 0) {
|
|
79
|
+
p1.active.volatiles.Binding = false;
|
|
80
|
+
}
|
|
81
|
+
var p2 = battle.side(.P2);
|
|
82
|
+
if (p2.active.volatiles.attacks == 0) {
|
|
83
|
+
p2.active.volatiles.Binding = false;
|
|
84
|
+
}
|
|
87
85
|
|
|
88
86
|
return endTurn(battle, log);
|
|
89
87
|
}
|
|
@@ -116,7 +114,6 @@ fn selectMove(
|
|
|
116
114
|
player: Player,
|
|
117
115
|
choice: Choice,
|
|
118
116
|
foe_choice: Choice,
|
|
119
|
-
skip_turn: *bool,
|
|
120
117
|
from: *?Move,
|
|
121
118
|
) ?Result {
|
|
122
119
|
if (choice.type == .Pass) return null;
|
|
@@ -129,26 +126,17 @@ fn selectMove(
|
|
|
129
126
|
(choice.type == .Move and choice.data == @boolToInt(showdown)));
|
|
130
127
|
|
|
131
128
|
// pre-battle menu
|
|
132
|
-
if (volatiles.Recharging)
|
|
133
|
-
if (showdown and battle.foe(player).active.volatiles.Binding) skip_turn.* = true;
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
129
|
+
if (volatiles.Recharging) return null;
|
|
136
130
|
if (volatiles.Rage) {
|
|
137
131
|
from.* = side.last_used_move;
|
|
138
|
-
if (showdown)
|
|
139
|
-
if (battle.foe(player).active.volatiles.Binding) skip_turn.* = true;
|
|
140
|
-
saveMove(battle, player, null);
|
|
141
|
-
}
|
|
132
|
+
if (showdown) saveMove(battle, player, null);
|
|
142
133
|
return null;
|
|
143
134
|
}
|
|
144
135
|
// Pokémon Showdown removes Flinch at the end-of-turn in its residual handler
|
|
145
136
|
if (!showdown) volatiles.Flinch = false;
|
|
146
137
|
if (volatiles.Thrashing or volatiles.Charging) {
|
|
147
138
|
from.* = side.last_selected_move;
|
|
148
|
-
if (showdown)
|
|
149
|
-
if (battle.foe(player).active.volatiles.Binding) skip_turn.* = true;
|
|
150
|
-
saveMove(battle, player, null);
|
|
151
|
-
}
|
|
139
|
+
if (showdown) saveMove(battle, player, null);
|
|
152
140
|
return null;
|
|
153
141
|
}
|
|
154
142
|
|
|
@@ -158,10 +146,7 @@ fn selectMove(
|
|
|
158
146
|
// pre-move select
|
|
159
147
|
if (Status.is(stored.status, .FRZ) or Status.is(stored.status, .SLP) or volatiles.Bide) {
|
|
160
148
|
assert(showdown or choice.data == 0);
|
|
161
|
-
if (showdown)
|
|
162
|
-
if (volatiles.Bide and battle.foe(player).active.volatiles.Binding) skip_turn.* = true;
|
|
163
|
-
saveMove(battle, player, choice);
|
|
164
|
-
}
|
|
149
|
+
if (showdown) saveMove(battle, player, choice);
|
|
165
150
|
return null;
|
|
166
151
|
}
|
|
167
152
|
if (volatiles.Binding) {
|
|
@@ -192,7 +177,6 @@ fn selectMove(
|
|
|
192
177
|
}
|
|
193
178
|
|
|
194
179
|
if (battle.foe(player).active.volatiles.Binding) {
|
|
195
|
-
skip_turn.* = true;
|
|
196
180
|
if (showdown) {
|
|
197
181
|
saveMove(battle, player, choice);
|
|
198
182
|
} else {
|
|
@@ -343,13 +327,9 @@ fn doTurn(
|
|
|
343
327
|
battle: anytype,
|
|
344
328
|
player: Player,
|
|
345
329
|
player_choice: Choice,
|
|
346
|
-
player_rewrap: bool,
|
|
347
|
-
player_skip: bool,
|
|
348
330
|
player_from: ?Move,
|
|
349
331
|
foe_player: Player,
|
|
350
332
|
foe_choice: Choice,
|
|
351
|
-
foe_rewrap: bool,
|
|
352
|
-
foe_skip: bool,
|
|
353
333
|
foe_from: ?Move,
|
|
354
334
|
log: anytype,
|
|
355
335
|
) !?Result {
|
|
@@ -357,16 +337,7 @@ fn doTurn(
|
|
|
357
337
|
|
|
358
338
|
var residual = true;
|
|
359
339
|
var replace = battle.side(player).stored().hp == 0;
|
|
360
|
-
if (try executeMove(
|
|
361
|
-
battle,
|
|
362
|
-
player,
|
|
363
|
-
player_choice,
|
|
364
|
-
player_rewrap,
|
|
365
|
-
player_skip,
|
|
366
|
-
player_from,
|
|
367
|
-
&residual,
|
|
368
|
-
log,
|
|
369
|
-
)) |r| return r;
|
|
340
|
+
if (try executeMove(battle, player, player_choice, player_from, &residual, log)) |r| return r;
|
|
370
341
|
if (!replace) {
|
|
371
342
|
if (player_choice.type != .Switch) {
|
|
372
343
|
if (try checkFaint(battle, foe_player, log)) |r| return r;
|
|
@@ -377,16 +348,7 @@ fn doTurn(
|
|
|
377
348
|
|
|
378
349
|
residual = true;
|
|
379
350
|
replace = battle.side(foe_player).stored().hp == 0;
|
|
380
|
-
if (try executeMove(
|
|
381
|
-
battle,
|
|
382
|
-
foe_player,
|
|
383
|
-
foe_choice,
|
|
384
|
-
foe_rewrap,
|
|
385
|
-
foe_skip,
|
|
386
|
-
foe_from,
|
|
387
|
-
&residual,
|
|
388
|
-
log,
|
|
389
|
-
)) |r| return r;
|
|
351
|
+
if (try executeMove(battle, foe_player, foe_choice, foe_from, &residual, log)) |r| return r;
|
|
390
352
|
if (!replace) {
|
|
391
353
|
if (foe_choice.type != .Switch) {
|
|
392
354
|
if (try checkFaint(battle, player, log)) |r| return r;
|
|
@@ -409,8 +371,6 @@ fn executeMove(
|
|
|
409
371
|
battle: anytype,
|
|
410
372
|
player: Player,
|
|
411
373
|
choice: Choice,
|
|
412
|
-
rewrap: bool,
|
|
413
|
-
skip: bool,
|
|
414
374
|
from: ?Move,
|
|
415
375
|
residual: *bool,
|
|
416
376
|
log: anytype,
|
|
@@ -422,10 +382,9 @@ fn executeMove(
|
|
|
422
382
|
return null;
|
|
423
383
|
}
|
|
424
384
|
|
|
425
|
-
// This is the correct place to check for SKIP_TURN and abort early, however since Pokémon
|
|
426
|
-
// Showdown overwrites the SKIP_TURN sentinel with its botched move select we need to add an
|
|
427
|
-
// additional skip boolean to accomplish the same thing in the Binding check of BeforeMove
|
|
428
385
|
if (side.last_selected_move == .SKIP_TURN) {
|
|
386
|
+
// Pokémon Showdown overwrites the SKIP_TURN sentinel with its botched move select,
|
|
387
|
+
// Binding instead gets handled in beforeMove after sleep and freeze
|
|
429
388
|
assert(!showdown);
|
|
430
389
|
if (battle.foe(player).active.volatiles.Binding) {
|
|
431
390
|
try log.cant(battle.active(player), .Bound);
|
|
@@ -475,7 +434,7 @@ fn executeMove(
|
|
|
475
434
|
|
|
476
435
|
var skip_can = false;
|
|
477
436
|
var skip_pp = false;
|
|
478
|
-
switch (try beforeMove(battle, player,
|
|
437
|
+
switch (try beforeMove(battle, player, from, residual, log)) {
|
|
479
438
|
.done => return null,
|
|
480
439
|
.skip_can => skip_can = true,
|
|
481
440
|
.skip_pp => skip_pp = true,
|
|
@@ -487,7 +446,7 @@ fn executeMove(
|
|
|
487
446
|
return null;
|
|
488
447
|
}
|
|
489
448
|
|
|
490
|
-
return doMove(battle, player, mslot,
|
|
449
|
+
return doMove(battle, player, mslot, auto, residual, log);
|
|
491
450
|
}
|
|
492
451
|
|
|
493
452
|
const BeforeMove = enum { done, skip_can, skip_pp, ok, err };
|
|
@@ -495,7 +454,6 @@ const BeforeMove = enum { done, skip_can, skip_pp, ok, err };
|
|
|
495
454
|
fn beforeMove(
|
|
496
455
|
battle: anytype,
|
|
497
456
|
player: Player,
|
|
498
|
-
skip: bool,
|
|
499
457
|
from: ?Move,
|
|
500
458
|
residual: *bool,
|
|
501
459
|
log: anytype,
|
|
@@ -527,15 +485,13 @@ fn beforeMove(
|
|
|
527
485
|
return .done;
|
|
528
486
|
}
|
|
529
487
|
|
|
530
|
-
if (
|
|
488
|
+
if (foe.active.volatiles.Binding) {
|
|
531
489
|
try log.cant(ident, .Bound);
|
|
532
490
|
return .done;
|
|
533
491
|
}
|
|
534
492
|
|
|
535
|
-
if (volatiles.Flinch) {
|
|
536
|
-
|
|
537
|
-
// Pokémon can sometimes flinch multiple times from the same original hit
|
|
538
|
-
if (!showdown) volatiles.Flinch = false;
|
|
493
|
+
if (!showdown and volatiles.Flinch) {
|
|
494
|
+
volatiles.Flinch = false;
|
|
539
495
|
try log.cant(ident, .Flinch);
|
|
540
496
|
return .done;
|
|
541
497
|
}
|
|
@@ -556,6 +512,14 @@ fn beforeMove(
|
|
|
556
512
|
// Pokémon Showdown's disable condition has a single onBeforeMove handler
|
|
557
513
|
if (showdown and try disabled(side, ident, log)) return .done;
|
|
558
514
|
|
|
515
|
+
// Pokémon Showdown checks for Flinch *after* instead of before... and doesn't clear Flinch
|
|
516
|
+
// until its imaginary "residual" phase, meaning Pokémon can sometimes flinch multiple times
|
|
517
|
+
// from the same original hit
|
|
518
|
+
if (showdown and volatiles.Flinch) {
|
|
519
|
+
try log.cant(ident, .Flinch);
|
|
520
|
+
return .done;
|
|
521
|
+
}
|
|
522
|
+
|
|
559
523
|
// This can only happen if a Pokémon started the battle frozen/sleeping and was thawed/woken
|
|
560
524
|
// before the side had a selected a move - we simply need to assume this leads to a desync
|
|
561
525
|
if (side.last_selected_move == .None) {
|
|
@@ -671,13 +635,8 @@ fn beforeMove(
|
|
|
671
635
|
try log.start(battle.active(player), .ConfusionSilent);
|
|
672
636
|
}
|
|
673
637
|
try log.move(ident, side.last_selected_move, battle.active(player.foe()), from);
|
|
674
|
-
if (showdown) {
|
|
675
|
-
|
|
676
|
-
// used in useMove and doesn't have the notion of skipping canMove semantics
|
|
677
|
-
side.last_used_move = side.last_selected_move;
|
|
678
|
-
if (handleThrashing(battle, active)) {
|
|
679
|
-
try log.start(battle.active(player), .ConfusionSilent);
|
|
680
|
-
}
|
|
638
|
+
if (showdown and handleThrashing(battle, active)) {
|
|
639
|
+
try log.start(battle.active(player), .ConfusionSilent);
|
|
681
640
|
}
|
|
682
641
|
return .skip_can;
|
|
683
642
|
}
|
|
@@ -692,11 +651,7 @@ fn beforeMove(
|
|
|
692
651
|
side.last_selected_move,
|
|
693
652
|
);
|
|
694
653
|
if (showdown or battle.last_damage != 0) {
|
|
695
|
-
const sub = showdown and foe.active.volatiles.Substitute;
|
|
696
654
|
_ = try applyDamage(battle, player.foe(), player.foe(), .None, log);
|
|
697
|
-
if (battle.foe(player).stored().hp > 0 and !sub) {
|
|
698
|
-
try buildRage(battle, player.foe(), log);
|
|
699
|
-
}
|
|
700
655
|
}
|
|
701
656
|
return .done;
|
|
702
657
|
}
|
|
@@ -754,7 +709,7 @@ fn canMove(
|
|
|
754
709
|
Effects.thrashing(battle, player);
|
|
755
710
|
} else if (!showdown and move.effect == .Binding) {
|
|
756
711
|
// Pokémon Showdown handles this after hit/miss checks and damage calculation
|
|
757
|
-
Effects.binding(battle, player
|
|
712
|
+
Effects.binding(battle, player);
|
|
758
713
|
}
|
|
759
714
|
|
|
760
715
|
return true;
|
|
@@ -795,7 +750,6 @@ fn doMove(
|
|
|
795
750
|
battle: anytype,
|
|
796
751
|
player: Player,
|
|
797
752
|
mslot: u4,
|
|
798
|
-
rewrap: bool,
|
|
799
753
|
auto: bool,
|
|
800
754
|
residual: *bool,
|
|
801
755
|
log: anytype,
|
|
@@ -887,9 +841,9 @@ fn doMove(
|
|
|
887
841
|
|
|
888
842
|
if (!showdown or !miss) {
|
|
889
843
|
if (move.effect == .MirrorMove) {
|
|
890
|
-
return mirrorMove(battle, player, mslot,
|
|
844
|
+
return mirrorMove(battle, player, mslot, auto, residual, log);
|
|
891
845
|
} else if (move.effect == .Metronome) {
|
|
892
|
-
return metronome(battle, player, mslot,
|
|
846
|
+
return metronome(battle, player, mslot, auto, residual, log);
|
|
893
847
|
} else if (move.effect.onEnd()) {
|
|
894
848
|
try onEnd(battle, player, move, log);
|
|
895
849
|
return null;
|
|
@@ -904,7 +858,7 @@ fn doMove(
|
|
|
904
858
|
move.effect == .OHKO and side.active.stats.spe < foe.active.stats.spe;
|
|
905
859
|
if (ohko) {
|
|
906
860
|
try log.immune(foe_ident, .OHKO);
|
|
907
|
-
} else if (immune and !invulnerable
|
|
861
|
+
} else if (immune and !invulnerable) {
|
|
908
862
|
try log.immune(foe_ident, .None);
|
|
909
863
|
} else if (mist) {
|
|
910
864
|
if (!foe.active.volatiles.Substitute) try log.activate(foe_ident, .Mist);
|
|
@@ -970,17 +924,11 @@ fn doMove(
|
|
|
970
924
|
} else if (showdown) {
|
|
971
925
|
// This should be handled much earlier but Pokémon Showdown does it here... ¯\_(ツ)_/¯
|
|
972
926
|
if (move.effect == .Binding) {
|
|
973
|
-
Effects.binding(battle, player
|
|
927
|
+
Effects.binding(battle, player);
|
|
974
928
|
if (immune) {
|
|
975
929
|
battle.last_damage = 0;
|
|
976
|
-
assert(foe.stored().hp > 0);
|
|
977
930
|
// Pokémon Showdown logs |-damage| here instead of |-immune| because logic...
|
|
978
|
-
|
|
979
|
-
try log.activate(battle.active(player.foe()), .Substitute);
|
|
980
|
-
} else {
|
|
981
|
-
try log.damage(battle.active(player.foe()), foe.stored(), .None);
|
|
982
|
-
try buildRage(battle, player.foe(), log);
|
|
983
|
-
}
|
|
931
|
+
try log.damage(battle.active(player.foe()), foe.stored(), .None);
|
|
984
932
|
return null;
|
|
985
933
|
}
|
|
986
934
|
}
|
|
@@ -1213,9 +1161,7 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, log: anytype)
|
|
|
1213
1161
|
// Pokémon Showdown calls checkHit before Counter
|
|
1214
1162
|
if (!showdown and !try checkHit(battle, player, move, log)) return null;
|
|
1215
1163
|
|
|
1216
|
-
const sub = showdown and foe.active.volatiles.Substitute;
|
|
1217
1164
|
_ = try applyDamage(battle, player.foe(), player.foe(), .None, log);
|
|
1218
|
-
if (battle.foe(player).stored().hp > 0 and !sub) try buildRage(battle, player.foe(), log);
|
|
1219
1165
|
return null;
|
|
1220
1166
|
}
|
|
1221
1167
|
|
|
@@ -1259,7 +1205,6 @@ fn mirrorMove(
|
|
|
1259
1205
|
battle: anytype,
|
|
1260
1206
|
player: Player,
|
|
1261
1207
|
mslot: u4,
|
|
1262
|
-
rewrap: bool,
|
|
1263
1208
|
auto: bool,
|
|
1264
1209
|
residual: *bool,
|
|
1265
1210
|
log: anytype,
|
|
@@ -1272,19 +1217,18 @@ fn mirrorMove(
|
|
|
1272
1217
|
if (foe.last_used_move == .None or foe.last_used_move == .MirrorMove) {
|
|
1273
1218
|
try log.fail(battle.active(player), .None);
|
|
1274
1219
|
return null;
|
|
1275
|
-
} else if (!showdown or foe.last_used_move != .Struggle) {
|
|
1276
|
-
incrementPP(side, mslot);
|
|
1277
1220
|
}
|
|
1278
1221
|
|
|
1222
|
+
incrementPP(side, mslot);
|
|
1223
|
+
|
|
1279
1224
|
if (!try canMove(battle, player, mslot, auto, false, .MirrorMove, residual, log)) return null;
|
|
1280
|
-
return doMove(battle, player, mslot,
|
|
1225
|
+
return doMove(battle, player, mslot, auto, residual, log);
|
|
1281
1226
|
}
|
|
1282
1227
|
|
|
1283
1228
|
fn metronome(
|
|
1284
1229
|
battle: anytype,
|
|
1285
1230
|
player: Player,
|
|
1286
1231
|
mslot: u4,
|
|
1287
|
-
rewrap: bool,
|
|
1288
1232
|
auto: bool,
|
|
1289
1233
|
residual: *bool,
|
|
1290
1234
|
log: anytype,
|
|
@@ -1295,7 +1239,7 @@ fn metronome(
|
|
|
1295
1239
|
incrementPP(side, mslot);
|
|
1296
1240
|
|
|
1297
1241
|
if (!try canMove(battle, player, mslot, auto, false, .Metronome, residual, log)) return null;
|
|
1298
|
-
return doMove(battle, player, mslot,
|
|
1242
|
+
return doMove(battle, player, mslot, auto, residual, log);
|
|
1299
1243
|
}
|
|
1300
1244
|
|
|
1301
1245
|
fn checkHit(battle: anytype, player: Player, move: Move.Data, log: anytype) !bool {
|
|
@@ -1502,7 +1446,11 @@ fn handleResidual(battle: anytype, player: Player, log: anytype) !void {
|
|
|
1502
1446
|
stored.hp -= amount;
|
|
1503
1447
|
|
|
1504
1448
|
// As above, Pokémon Showdown uses damageOf but its not relevant
|
|
1505
|
-
if (amount > 0)
|
|
1449
|
+
if (amount > 0) {
|
|
1450
|
+
try log.damage(ident, stored, .LeechSeed);
|
|
1451
|
+
// Pokémon Showdown erroneously updates last damage with uncapped Leech Seed damage
|
|
1452
|
+
if (showdown) battle.last_damage = damage;
|
|
1453
|
+
}
|
|
1506
1454
|
|
|
1507
1455
|
const before = foe_stored.hp;
|
|
1508
1456
|
// Uncapped damage is added back to the foe
|
|
@@ -2035,9 +1983,9 @@ pub const Effects = struct {
|
|
|
2035
1983
|
}
|
|
2036
1984
|
break :has_mimic false;
|
|
2037
1985
|
};
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
1986
|
+
// If the foe is Invulnerable we still want to fall through to checkHit to be able to
|
|
1987
|
+
// trigger |-miss| instead of |-fail|
|
|
1988
|
+
if (!has_mimic and !foe.active.volatiles.Invulnerable) {
|
|
2041
1989
|
return try log.fail(battle.active(player.foe()), .None);
|
|
2042
1990
|
}
|
|
2043
1991
|
}
|
|
@@ -2305,7 +2253,7 @@ pub const Effects = struct {
|
|
|
2305
2253
|
try log.transform(battle.active(player), foe_ident);
|
|
2306
2254
|
}
|
|
2307
2255
|
|
|
2308
|
-
fn binding(battle: anytype, player: Player
|
|
2256
|
+
fn binding(battle: anytype, player: Player) void {
|
|
2309
2257
|
var side = battle.side(player);
|
|
2310
2258
|
var foe = battle.foe(player);
|
|
2311
2259
|
|
|
@@ -2313,9 +2261,7 @@ pub const Effects = struct {
|
|
|
2313
2261
|
side.active.volatiles.Binding = true;
|
|
2314
2262
|
// GLITCH: Hyper Beam automatic selection glitch if Recharging gets cleared on miss
|
|
2315
2263
|
// (Pokémon Showdown unitentionally patches this glitch, preventing automatic selection)
|
|
2316
|
-
if (!showdown)
|
|
2317
|
-
foe.active.volatiles.Recharging = false;
|
|
2318
|
-
} else if (foe.stored().hp == 0) return if (!rewrap) battle.rng.advance(1);
|
|
2264
|
+
if (!showdown) foe.active.volatiles.Recharging = false;
|
|
2319
2265
|
|
|
2320
2266
|
side.active.volatiles.attacks = Rolls.distribution(battle) - 1;
|
|
2321
2267
|
}
|
package/src/lib/pkmn.zig
CHANGED
|
@@ -25,12 +25,12 @@ pub const Result = @import("common/data.zig").Result;
|
|
|
25
25
|
/// Pokémon Showdown's RNG (backed by a Generation V & VI RNG).
|
|
26
26
|
pub const PSRNG = @import("common/rng.zig").PSRNG;
|
|
27
27
|
|
|
28
|
-
/// Namespace for helpers related to protocol
|
|
28
|
+
/// Namespace for helpers related to protocol trace logging.
|
|
29
29
|
pub const protocol = if (options.internal) struct {
|
|
30
30
|
pub usingnamespace @import("common/protocol.zig");
|
|
31
31
|
} else struct {
|
|
32
32
|
/// Logs protocol information to its `Writer` during a battle update when
|
|
33
|
-
/// `options.
|
|
33
|
+
/// `options.trace` is enabled.
|
|
34
34
|
pub const Log = @import("common/protocol.zig").Log;
|
|
35
35
|
/// Stripped down version of `std.io.FixedBufferStream` optimized for
|
|
36
36
|
/// efficiently writing the individual protocol bytes. Note that the
|
|
@@ -41,8 +41,8 @@ pub const protocol = if (options.internal) struct {
|
|
|
41
41
|
/// intialized with a `LOGS_SIZE`-sized buffer.
|
|
42
42
|
pub const FixedLog = @import("common/protocol.zig").FixedLog;
|
|
43
43
|
/// Null object pattern implementation of `Log` backed by a
|
|
44
|
-
/// `std.io.null_writer`. Ignores anything sent to it, though
|
|
45
|
-
///
|
|
44
|
+
/// `std.io.null_writer`. Ignores anything sent to it, though trace logging
|
|
45
|
+
/// should addtionally be turned off entirely by using `options.trace`.
|
|
46
46
|
pub const NULL = @import("common/protocol.zig").NULL;
|
|
47
47
|
};
|
|
48
48
|
|