@pkmn/engine 0.1.0-dev.900756a9 → 0.1.0-dev.9257876b
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 +39 -16
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +3 -2
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +1 -1
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js.map +1 -1
- package/build.zig +72 -64
- package/package.json +31 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/protocol.zig +381 -405
- package/src/lib/common/rational.zig +7 -3
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/gen1/calc.zig +5 -5
- package/src/lib/gen1/chance.zig +27 -26
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +18 -18
- package/src/lib/gen1/helpers.zig +7 -7
- package/src/lib/gen1/mechanics.zig +255 -229
- package/src/lib/gen2/chance.zig +38 -37
- package/src/lib/gen2/data.zig +9 -9
- package/src/lib/gen2/generated.zig +4 -6
- package/src/lib/gen2/mechanics.zig +398 -299
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +26 -3
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
|
@@ -22,6 +22,7 @@ const Result = common.Result;
|
|
|
22
22
|
|
|
23
23
|
const Boost = protocol.Boost;
|
|
24
24
|
const Damage = protocol.Damage;
|
|
25
|
+
const Fail = protocol.Fail;
|
|
25
26
|
const Heal = protocol.Heal;
|
|
26
27
|
|
|
27
28
|
const Gen12 = rng.Gen12;
|
|
@@ -70,8 +71,8 @@ pub fn update(battle: anytype, c1: Choice, c2: Choice, options: anytype) !Result
|
|
|
70
71
|
var p1 = battle.side(.P1);
|
|
71
72
|
var p2 = battle.side(.P2);
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
const r1 = showdown and p1.active.volatiles.Binding and c2.type == .Switch;
|
|
75
|
+
const r2 = showdown and p2.active.volatiles.Binding and c1.type == .Switch;
|
|
75
76
|
|
|
76
77
|
if (try turnOrder(battle, c1, c2, options) == .P1) {
|
|
77
78
|
if (try doTurn(battle, .P1, c1, r1, s1, .P2, c2, r2, s2, options)) |r| return r;
|
|
@@ -89,11 +90,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
89
90
|
const p1 = battle.side(.P1);
|
|
90
91
|
const p2 = battle.side(.P2);
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
const p1_slot = findFirstAlive(p1);
|
|
93
94
|
assert(!showdown or p1_slot == 1);
|
|
94
95
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
const p2_slot = findFirstAlive(p2);
|
|
97
98
|
assert(!showdown or p2_slot == 1);
|
|
98
99
|
if (p2_slot == 0) return Result.Win;
|
|
99
100
|
|
|
@@ -255,14 +256,14 @@ fn switchIn(battle: anytype, player: Player, slot: u8, initial: bool, options: a
|
|
|
255
256
|
|
|
256
257
|
foe.active.volatiles.Binding = false;
|
|
257
258
|
|
|
258
|
-
try options.log.switched(battle.active(player), incoming);
|
|
259
|
+
try options.log.switched(.{ battle.active(player), incoming });
|
|
259
260
|
options.chance.switched(player, side.order[0], out);
|
|
260
261
|
|
|
261
262
|
if (showdown and incoming.status == Status.TOX) {
|
|
262
263
|
incoming.status = Status.init(.PSN);
|
|
263
264
|
// Technically, Pokémon Showdown adds these after *both* Pokémon have switched, but we'd
|
|
264
265
|
// rather not clutter up turnOrder just for this (incorrect) log message
|
|
265
|
-
try options.log.status(battle.active(player), incoming.status, .Silent);
|
|
266
|
+
try options.log.status(.{ battle.active(player), incoming.status, .Silent });
|
|
266
267
|
}
|
|
267
268
|
}
|
|
268
269
|
|
|
@@ -406,7 +407,7 @@ fn executeMove(
|
|
|
406
407
|
if (side.last_selected_move == .SKIP_TURN) {
|
|
407
408
|
assert(!showdown);
|
|
408
409
|
if (battle.foe(player).active.volatiles.Binding) {
|
|
409
|
-
try options.log.cant(battle.active(player), .Bound);
|
|
410
|
+
try options.log.cant(.{ battle.active(player), .Bound });
|
|
410
411
|
}
|
|
411
412
|
return null;
|
|
412
413
|
}
|
|
@@ -496,23 +497,23 @@ fn beforeMove(
|
|
|
496
497
|
}
|
|
497
498
|
|
|
498
499
|
if (duration == 0) {
|
|
499
|
-
try log.curestatus(ident, before, .Message);
|
|
500
|
+
try log.curestatus(.{ ident, before, .Message });
|
|
500
501
|
stored.status = 0; // clears EXT if present
|
|
501
502
|
} else {
|
|
502
|
-
try log.cant(ident, .Sleep);
|
|
503
|
+
try log.cant(.{ ident, .Sleep });
|
|
503
504
|
}
|
|
504
505
|
side.last_used_move = .None;
|
|
505
506
|
return .done;
|
|
506
507
|
}
|
|
507
508
|
|
|
508
509
|
if (Status.is(stored.status, .FRZ)) {
|
|
509
|
-
try log.cant(ident, .Freeze);
|
|
510
|
+
try log.cant(.{ ident, .Freeze });
|
|
510
511
|
side.last_used_move = .None;
|
|
511
512
|
return .done;
|
|
512
513
|
}
|
|
513
514
|
|
|
514
515
|
if (skip or foe.active.volatiles.Binding) {
|
|
515
|
-
try log.cant(ident, .Bound);
|
|
516
|
+
try log.cant(.{ ident, .Bound });
|
|
516
517
|
return .done;
|
|
517
518
|
}
|
|
518
519
|
|
|
@@ -520,13 +521,13 @@ fn beforeMove(
|
|
|
520
521
|
// Pokémon Showdown doesn't clear Flinch until its imaginary "residual" phase, meaning
|
|
521
522
|
// Pokémon can sometimes flinch multiple times from the same original hit
|
|
522
523
|
if (!showdown) volatiles.Flinch = false;
|
|
523
|
-
try log.cant(ident, .Flinch);
|
|
524
|
+
try log.cant(.{ ident, .Flinch });
|
|
524
525
|
return .done;
|
|
525
526
|
}
|
|
526
527
|
|
|
527
528
|
if (volatiles.Recharging) {
|
|
528
529
|
volatiles.Recharging = false;
|
|
529
|
-
try log.cant(ident, .Recharge);
|
|
530
|
+
try log.cant(.{ ident, .Recharge });
|
|
530
531
|
return .done;
|
|
531
532
|
}
|
|
532
533
|
|
|
@@ -540,7 +541,7 @@ fn beforeMove(
|
|
|
540
541
|
|
|
541
542
|
if (volatiles.disable_duration == 0) {
|
|
542
543
|
volatiles.disable_move = 0;
|
|
543
|
-
try log.end(ident, .Disable);
|
|
544
|
+
try log.end(.{ ident, .Disable });
|
|
544
545
|
}
|
|
545
546
|
}
|
|
546
547
|
// Pokémon Showdown's disable condition has a single onBeforeMove handler
|
|
@@ -565,9 +566,9 @@ fn beforeMove(
|
|
|
565
566
|
|
|
566
567
|
if (volatiles.confusion == 0) {
|
|
567
568
|
volatiles.Confusion = false;
|
|
568
|
-
try log.end(ident, .Confusion);
|
|
569
|
+
try log.end(.{ ident, .Confusion });
|
|
569
570
|
} else {
|
|
570
|
-
try log.activate(ident, .Confusion);
|
|
571
|
+
try log.activate(.{ ident, .Confusion });
|
|
571
572
|
|
|
572
573
|
if (try Rolls.confused(battle, player, options)) {
|
|
573
574
|
assert(!volatiles.MultiHit);
|
|
@@ -618,7 +619,7 @@ fn beforeMove(
|
|
|
618
619
|
volatiles.Binding = false;
|
|
619
620
|
options.chance.clearDurations(player, null);
|
|
620
621
|
// GLITCH: Invulnerable is not cleared, resulting in permanent Fly/Dig invulnerability
|
|
621
|
-
try log.cant(ident, .Paralysis);
|
|
622
|
+
try log.cant(.{ ident, .Paralysis });
|
|
622
623
|
return .done;
|
|
623
624
|
}
|
|
624
625
|
|
|
@@ -634,8 +635,6 @@ fn beforeMove(
|
|
|
634
635
|
volatiles.state +%= battle.last_damage;
|
|
635
636
|
}
|
|
636
637
|
|
|
637
|
-
assert(volatiles.attacks > 0);
|
|
638
|
-
|
|
639
638
|
if (options.calc.modify(player, .attacking)) |extend| {
|
|
640
639
|
if (!extend) volatiles.attacks = 0;
|
|
641
640
|
} else {
|
|
@@ -644,18 +643,18 @@ fn beforeMove(
|
|
|
644
643
|
try options.chance.attacking(player, volatiles.attacks);
|
|
645
644
|
|
|
646
645
|
if (volatiles.attacks != 0) {
|
|
647
|
-
try log.activate(ident, .Bide);
|
|
646
|
+
try log.activate(.{ ident, .Bide });
|
|
648
647
|
return .done;
|
|
649
648
|
}
|
|
650
649
|
|
|
651
650
|
volatiles.Bide = false;
|
|
652
|
-
try log.end(ident, .Bide);
|
|
651
|
+
try log.end(.{ ident, .Bide });
|
|
653
652
|
|
|
654
653
|
battle.last_damage = volatiles.state *% 2;
|
|
655
654
|
volatiles.state = 0;
|
|
656
655
|
|
|
657
656
|
if (battle.last_damage == 0) {
|
|
658
|
-
try log.fail(ident, .None);
|
|
657
|
+
try log.fail(.{ ident, .None });
|
|
659
658
|
return .done;
|
|
660
659
|
}
|
|
661
660
|
|
|
@@ -675,8 +674,6 @@ fn beforeMove(
|
|
|
675
674
|
}
|
|
676
675
|
|
|
677
676
|
if (volatiles.Thrashing) {
|
|
678
|
-
assert(volatiles.attacks > 0);
|
|
679
|
-
|
|
680
677
|
if (options.calc.modify(player, .attacking)) |extend| {
|
|
681
678
|
if (!extend) volatiles.attacks = 0;
|
|
682
679
|
} else {
|
|
@@ -685,23 +682,21 @@ fn beforeMove(
|
|
|
685
682
|
try options.chance.attacking(player, volatiles.attacks);
|
|
686
683
|
|
|
687
684
|
if (!showdown and handleThrashing(battle, active, player, options)) {
|
|
688
|
-
try log.start(battle.active(player), .ConfusionSilent);
|
|
685
|
+
try log.start(.{ battle.active(player), .ConfusionSilent });
|
|
689
686
|
}
|
|
690
|
-
try log.move(ident, side.last_selected_move, battle.active(player.foe()));
|
|
687
|
+
try log.move(.{ ident, side.last_selected_move, battle.active(player.foe()) });
|
|
691
688
|
if (showdown) {
|
|
692
689
|
// This shouldn't actually set last_used_move, but Pokémon Showdown sets last
|
|
693
690
|
// used in useMove and doesn't have the notion of skipping canMove semantics
|
|
694
691
|
side.last_used_move = side.last_selected_move;
|
|
695
692
|
if (handleThrashing(battle, active, player, options)) {
|
|
696
|
-
try log.start(battle.active(player), .ConfusionSilent);
|
|
693
|
+
try log.start(.{ battle.active(player), .ConfusionSilent });
|
|
697
694
|
}
|
|
698
695
|
}
|
|
699
696
|
return .skip_can;
|
|
700
697
|
}
|
|
701
698
|
|
|
702
699
|
if (volatiles.Binding) {
|
|
703
|
-
assert(volatiles.attacks > 0);
|
|
704
|
-
|
|
705
700
|
if (options.calc.modify(player, .binding)) |extend| {
|
|
706
701
|
if (!extend) volatiles.attacks = 0;
|
|
707
702
|
} else {
|
|
@@ -709,7 +704,7 @@ fn beforeMove(
|
|
|
709
704
|
}
|
|
710
705
|
try options.chance.binding(player, volatiles.attacks);
|
|
711
706
|
|
|
712
|
-
try log.move(
|
|
707
|
+
try log.move(.{ ident, side.last_selected_move, battle.active(player.foe()) });
|
|
713
708
|
if (showdown or battle.last_damage != 0) {
|
|
714
709
|
const sub = showdown and foe.active.volatiles.Substitute;
|
|
715
710
|
_ = try applyDamage(battle, player.foe(), player.foe(), .None, options);
|
|
@@ -737,22 +732,21 @@ fn canMove(
|
|
|
737
732
|
const player_ident = battle.active(player);
|
|
738
733
|
const move = Move.get(side.last_selected_move);
|
|
739
734
|
|
|
740
|
-
var skip = skip_pp;
|
|
741
735
|
if (side.active.volatiles.Charging) {
|
|
742
736
|
side.active.volatiles.Charging = false;
|
|
743
737
|
side.active.volatiles.Invulnerable = false;
|
|
744
738
|
} else if (move.effect == .Charge) {
|
|
745
|
-
try options.log.
|
|
739
|
+
try options.log.move(.{ player_ident, side.last_selected_move, ID{}, from });
|
|
746
740
|
setCounterable(battle, player, side, move);
|
|
747
741
|
try Effects.charge(battle, player, options);
|
|
748
742
|
return false;
|
|
749
743
|
}
|
|
750
744
|
|
|
751
745
|
side.last_used_move = side.last_selected_move;
|
|
752
|
-
if (!
|
|
746
|
+
if (!skip_pp) decrementPP(side, mslot, auto);
|
|
753
747
|
|
|
754
748
|
const target = if (move.target == .Self) player else player.foe();
|
|
755
|
-
try options.log.
|
|
749
|
+
try options.log.move(.{ player_ident, side.last_selected_move, battle.active(target), from });
|
|
756
750
|
setCounterable(battle, player, side, move);
|
|
757
751
|
|
|
758
752
|
if (move.effect.onBegin()) {
|
|
@@ -901,7 +895,7 @@ fn doMove(
|
|
|
901
895
|
}
|
|
902
896
|
}
|
|
903
897
|
|
|
904
|
-
|
|
898
|
+
const zero = battle.last_damage == 0;
|
|
905
899
|
miss = if (showdown or skip)
|
|
906
900
|
miss
|
|
907
901
|
else
|
|
@@ -929,16 +923,16 @@ fn doMove(
|
|
|
929
923
|
ohko = (!showdown or (!immune and !invulnerable)) and
|
|
930
924
|
move.effect == .OHKO and side.active.stats.spe < foe.active.stats.spe;
|
|
931
925
|
if (ohko) {
|
|
932
|
-
try log.immune(foe_ident, .OHKO);
|
|
926
|
+
try log.immune(.{ foe_ident, .OHKO });
|
|
933
927
|
} else if (immune and !invulnerable and (!showdown or move.effect != .Binding)) {
|
|
934
|
-
try log.immune(foe_ident, .None);
|
|
928
|
+
try log.immune(.{ foe_ident, .None });
|
|
935
929
|
} else if (mist) {
|
|
936
|
-
if (!foe.active.volatiles.Substitute) try log.activate(foe_ident, .Mist);
|
|
937
|
-
try log.fail(foe_ident, .None);
|
|
930
|
+
if (!foe.active.volatiles.Substitute) try log.activate(.{ foe_ident, .Mist });
|
|
931
|
+
try log.fail(.{ foe_ident, .None });
|
|
938
932
|
} else {
|
|
939
933
|
if (showdown or !zero) try options.chance.commit(player, .miss);
|
|
940
|
-
try log.lastmiss();
|
|
941
|
-
try log.miss(battle.active(player));
|
|
934
|
+
try log.lastmiss(.{});
|
|
935
|
+
try log.miss(.{battle.active(player)});
|
|
942
936
|
}
|
|
943
937
|
if (move.effect == .JumpKick) {
|
|
944
938
|
// Recoil is supposed to be damage/8 but damage will always be 0 here
|
|
@@ -974,15 +968,15 @@ fn doMove(
|
|
|
974
968
|
var hit: u4 = 0;
|
|
975
969
|
while (hit < hits) {
|
|
976
970
|
if (hit == 0) {
|
|
977
|
-
if (crit) try log.crit(battle.active(player.foe()));
|
|
971
|
+
if (crit) try log.crit(.{battle.active(player.foe())});
|
|
978
972
|
if (effectiveness > Effectiveness.neutral) {
|
|
979
|
-
try log.supereffective(battle.active(player.foe()));
|
|
973
|
+
try log.supereffective(.{battle.active(player.foe())});
|
|
980
974
|
} else if (effectiveness < Effectiveness.neutral) {
|
|
981
|
-
try log.resisted(battle.active(player.foe()));
|
|
975
|
+
try log.resisted(.{battle.active(player.foe())});
|
|
982
976
|
}
|
|
983
977
|
}
|
|
984
978
|
if (!skip) nullified = try applyDamage(battle, player.foe(), player.foe(), .None, options);
|
|
985
|
-
if (hit == 0 and ohko) try log.ohko();
|
|
979
|
+
if (hit == 0 and ohko) try log.ohko(.{});
|
|
986
980
|
hit += 1;
|
|
987
981
|
if (foe.stored().hp == 0) break;
|
|
988
982
|
if (!late and (!sub or move.effect == .Explode)) {
|
|
@@ -999,7 +993,7 @@ fn doMove(
|
|
|
999
993
|
if (showdown and move.effect == .Twineedle and !nullified and foe.stored().hp > 0) {
|
|
1000
994
|
try Effects.poison(battle, player, Move.get(.PoisonSting), options);
|
|
1001
995
|
}
|
|
1002
|
-
try log.hitcount(battle.active(player.foe()), hit);
|
|
996
|
+
try log.hitcount(.{ battle.active(player.foe()), hit });
|
|
1003
997
|
} else if (showdown) {
|
|
1004
998
|
// This should be handled much earlier but Pokémon Showdown does it here... ¯\_(ツ)_/¯
|
|
1005
999
|
if (move.effect == .Binding) {
|
|
@@ -1009,9 +1003,9 @@ fn doMove(
|
|
|
1009
1003
|
assert(foe.stored().hp > 0);
|
|
1010
1004
|
// Pokémon Showdown logs |-damage| here instead of |-immune| because logic...
|
|
1011
1005
|
if (sub) {
|
|
1012
|
-
try log.activate(battle.active(player.foe()), .Substitute);
|
|
1006
|
+
try log.activate(.{ battle.active(player.foe()), .Substitute });
|
|
1013
1007
|
} else {
|
|
1014
|
-
try log.damage(battle.active(player.foe()), foe.stored(), .None);
|
|
1008
|
+
try log.damage(.{ battle.active(player.foe()), foe.stored(), .None });
|
|
1015
1009
|
try buildRage(battle, player.foe(), options);
|
|
1016
1010
|
}
|
|
1017
1011
|
return null;
|
|
@@ -1059,7 +1053,7 @@ fn checkCriticalHit(battle: anytype, player: Player, move: Move.Data, options: a
|
|
|
1059
1053
|
const side = battle.side(player);
|
|
1060
1054
|
|
|
1061
1055
|
// Base speed is used for the critical hit calculation, even when Transform-ed
|
|
1062
|
-
var rate =
|
|
1056
|
+
var rate: u16 = Species.chance(side.stored().species);
|
|
1063
1057
|
// GLITCH: Focus Energy reduces critical hit rate instead of increasing it
|
|
1064
1058
|
rate = if (side.active.volatiles.FocusEnergy) rate / 2 else @min(rate * 2, 255);
|
|
1065
1059
|
rate = if (move.effect == .HighCritical) @min(rate * 4, 255) else rate / 2;
|
|
@@ -1116,9 +1110,9 @@ fn calcDamage(
|
|
|
1116
1110
|
def = @max((def / 4) & 255, if (showdown) 1 else 0);
|
|
1117
1111
|
}
|
|
1118
1112
|
|
|
1119
|
-
const lvl =
|
|
1113
|
+
const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
|
|
1120
1114
|
|
|
1121
|
-
def =
|
|
1115
|
+
def = if (move.effect == .Explode) @max(def / 2, 1) else def;
|
|
1122
1116
|
|
|
1123
1117
|
if (def == 0) return false;
|
|
1124
1118
|
|
|
@@ -1209,7 +1203,7 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1209
1203
|
const foe = battle.foe(player);
|
|
1210
1204
|
|
|
1211
1205
|
if (battle.last_damage == 0) {
|
|
1212
|
-
try options.log.fail(battle.active(player), .None);
|
|
1206
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
1213
1207
|
return null;
|
|
1214
1208
|
}
|
|
1215
1209
|
|
|
@@ -1227,14 +1221,14 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1227
1221
|
foe_last_selected_move.type == .Fighting);
|
|
1228
1222
|
|
|
1229
1223
|
if (!used and !selected) {
|
|
1230
|
-
try options.log.fail(battle.active(player), .None);
|
|
1224
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
1231
1225
|
return null;
|
|
1232
1226
|
}
|
|
1233
1227
|
|
|
1234
1228
|
if (!used or !selected) {
|
|
1235
1229
|
// GLITCH: Counter desync (covered by Desync Clause Mod on Pokémon Showdown)
|
|
1236
1230
|
if (!showdown) return Result.Error;
|
|
1237
|
-
try options.log.fail(battle.active(player), .None);
|
|
1231
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
1238
1232
|
return null;
|
|
1239
1233
|
}
|
|
1240
1234
|
|
|
@@ -1270,20 +1264,20 @@ fn applyDamage(
|
|
|
1270
1264
|
subbed.active.volatiles.substitute = 0;
|
|
1271
1265
|
subbed.active.volatiles.Substitute = false;
|
|
1272
1266
|
// battle.last_damage is not updated with the amount of HP the Substitute had
|
|
1273
|
-
try options.log.end(battle.active(sub_player), .Substitute);
|
|
1267
|
+
try options.log.end(.{ battle.active(sub_player), .Substitute });
|
|
1274
1268
|
options.calc.capped(target_player);
|
|
1275
1269
|
return true;
|
|
1276
1270
|
} else {
|
|
1277
1271
|
// Safe to truncate since less than subbed.volatiles.substitute which is a u8
|
|
1278
1272
|
subbed.active.volatiles.substitute -= @intCast(battle.last_damage);
|
|
1279
|
-
try options.log.activate(battle.active(sub_player), .Substitute);
|
|
1273
|
+
try options.log.activate(.{ battle.active(sub_player), .Substitute });
|
|
1280
1274
|
return false;
|
|
1281
1275
|
}
|
|
1282
1276
|
}
|
|
1283
1277
|
|
|
1284
1278
|
if (battle.last_damage > target.stored().hp) battle.last_damage = target.stored().hp;
|
|
1285
1279
|
target.stored().hp -= battle.last_damage;
|
|
1286
|
-
try options.log.damage(battle.active(target_player), target.stored(), reason);
|
|
1280
|
+
try options.log.damage(.{ battle.active(target_player), target.stored(), reason });
|
|
1287
1281
|
return false;
|
|
1288
1282
|
}
|
|
1289
1283
|
|
|
@@ -1302,7 +1296,7 @@ fn mirrorMove(
|
|
|
1302
1296
|
side.last_selected_move = foe.last_used_move;
|
|
1303
1297
|
|
|
1304
1298
|
if (foe.last_used_move == .None or foe.last_used_move == .MirrorMove) {
|
|
1305
|
-
try options.log.fail(battle.active(player), .None);
|
|
1299
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
1306
1300
|
return null;
|
|
1307
1301
|
} else if (!showdown or foe.last_used_move != .Struggle) {
|
|
1308
1302
|
incrementPP(side, mslot);
|
|
@@ -1344,12 +1338,12 @@ fn checkHit(battle: anytype, player: Player, move: Move.Data, options: anytype)
|
|
|
1344
1338
|
if (mist) {
|
|
1345
1339
|
assert(!showdown);
|
|
1346
1340
|
const foe_ident = battle.active(player.foe());
|
|
1347
|
-
try options.log.activate(foe_ident, .Mist);
|
|
1348
|
-
try options.log.fail(foe_ident, .None);
|
|
1341
|
+
try options.log.activate(.{ foe_ident, .Mist });
|
|
1342
|
+
try options.log.fail(.{ foe_ident, .None });
|
|
1349
1343
|
} else {
|
|
1350
1344
|
try options.chance.commit(player, .miss);
|
|
1351
|
-
try options.log.lastmiss();
|
|
1352
|
-
try options.log.miss(battle.active(player));
|
|
1345
|
+
try options.log.lastmiss(.{});
|
|
1346
|
+
try options.log.miss(.{battle.active(player)});
|
|
1353
1347
|
}
|
|
1354
1348
|
|
|
1355
1349
|
return false;
|
|
@@ -1401,7 +1395,7 @@ fn moveHit(
|
|
|
1401
1395
|
const overwritten = overwrite and state > 0;
|
|
1402
1396
|
assert(!overwritten or (0 < state and state <= 255 and !side.active.volatiles.Bide));
|
|
1403
1397
|
|
|
1404
|
-
var accuracy = if (overwritten) state else
|
|
1398
|
+
var accuracy: u16 = if (overwritten) state else move.accuracy;
|
|
1405
1399
|
var boost = BOOSTS[@as(u4, @intCast(@as(i8, side.active.boosts.accuracy) + 6))];
|
|
1406
1400
|
accuracy = accuracy * boost[0] / boost[1];
|
|
1407
1401
|
boost = BOOSTS[@as(u4, @intCast(@as(i8, -foe.active.boosts.evasion) + 6))];
|
|
@@ -1450,13 +1444,13 @@ fn checkFaint(
|
|
|
1450
1444
|
assert(!foe.active.volatiles.MultiHit);
|
|
1451
1445
|
|
|
1452
1446
|
if (player_out and foe_out) {
|
|
1453
|
-
try options.log.tie();
|
|
1447
|
+
try options.log.tie(.{});
|
|
1454
1448
|
return Result.Tie;
|
|
1455
1449
|
} else if (player_out) {
|
|
1456
|
-
try options.log.win(player.foe());
|
|
1450
|
+
try options.log.win(.{player.foe()});
|
|
1457
1451
|
return if (player == .P1) Result.Lose else Result.Win;
|
|
1458
1452
|
} else if (foe_out) {
|
|
1459
|
-
try options.log.win(player);
|
|
1453
|
+
try options.log.win(.{player});
|
|
1460
1454
|
return if (player == .P1) Result.Win else Result.Lose;
|
|
1461
1455
|
}
|
|
1462
1456
|
|
|
@@ -1498,7 +1492,7 @@ fn faint(battle: anytype, player: Player, done: bool, options: anytype) !?Result
|
|
|
1498
1492
|
side.stored().stats.spe;
|
|
1499
1493
|
}
|
|
1500
1494
|
|
|
1501
|
-
try options.log.faint(battle.active(player), done);
|
|
1495
|
+
try options.log.faint(.{ battle.active(player), done });
|
|
1502
1496
|
options.calc.capped(player);
|
|
1503
1497
|
return null;
|
|
1504
1498
|
}
|
|
@@ -1520,8 +1514,8 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !void {
|
|
|
1520
1514
|
|
|
1521
1515
|
if (!showdown or damage > 0) {
|
|
1522
1516
|
stored.hp -= damage;
|
|
1523
|
-
// Pokémon Showdown uses
|
|
1524
|
-
try options.log.damage(ident, stored, if (brn) Damage.Burn else Damage.Poison);
|
|
1517
|
+
// Pokémon Showdown uses damage [of] here but its not relevant in Generation I
|
|
1518
|
+
try options.log.damage(.{ ident, stored, if (brn) Damage.Burn else Damage.Poison });
|
|
1525
1519
|
}
|
|
1526
1520
|
}
|
|
1527
1521
|
|
|
@@ -1546,14 +1540,14 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !void {
|
|
|
1546
1540
|
const amount = @min(damage, stored.hp);
|
|
1547
1541
|
stored.hp -= amount;
|
|
1548
1542
|
|
|
1549
|
-
// As above, Pokémon Showdown uses
|
|
1550
|
-
if (amount > 0) try options.log.damage(ident, stored, .LeechSeed);
|
|
1543
|
+
// As above, Pokémon Showdown uses damage [of] but its not relevant
|
|
1544
|
+
if (amount > 0) try options.log.damage(.{ ident, stored, .LeechSeed });
|
|
1551
1545
|
|
|
1552
1546
|
const before = foe_stored.hp;
|
|
1553
1547
|
// Uncapped damage is added back to the foe
|
|
1554
1548
|
foe_stored.hp = @min(foe_stored.hp + damage, foe_stored.stats.hp);
|
|
1555
1549
|
// Pokémon Showdown uses the less specific heal here instead of drain... because reasons?
|
|
1556
|
-
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored,
|
|
1550
|
+
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored, .Silent });
|
|
1557
1551
|
}
|
|
1558
1552
|
}
|
|
1559
1553
|
|
|
@@ -1564,18 +1558,18 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
|
|
|
1564
1558
|
battle.turn += 1;
|
|
1565
1559
|
|
|
1566
1560
|
if (showdown and pkmn.options.ebc and checkEBC(battle)) {
|
|
1567
|
-
try options.log.tie();
|
|
1561
|
+
try options.log.tie(.{});
|
|
1568
1562
|
return Result.Tie;
|
|
1569
1563
|
}
|
|
1570
1564
|
|
|
1571
1565
|
if (showdown and battle.turn >= 1000) {
|
|
1572
|
-
try options.log.tie();
|
|
1566
|
+
try options.log.tie(.{});
|
|
1573
1567
|
return Result.Tie;
|
|
1574
1568
|
} else if (battle.turn >= 65535) {
|
|
1575
1569
|
return Result.Error;
|
|
1576
1570
|
}
|
|
1577
1571
|
|
|
1578
|
-
try options.log.turn(battle.turn);
|
|
1572
|
+
try options.log.turn(.{battle.turn});
|
|
1579
1573
|
|
|
1580
1574
|
return Result.Default;
|
|
1581
1575
|
}
|
|
@@ -1645,7 +1639,7 @@ fn disabled(side: *Side, ident: ID, options: anytype) !bool {
|
|
|
1645
1639
|
side.last_selected_move;
|
|
1646
1640
|
if (m != .None and m == last) {
|
|
1647
1641
|
side.active.volatiles.Charging = false;
|
|
1648
|
-
try options.log.
|
|
1642
|
+
try options.log.cant(.{ ident, .Disable, last });
|
|
1649
1643
|
return true;
|
|
1650
1644
|
}
|
|
1651
1645
|
}
|
|
@@ -1678,7 +1672,6 @@ fn onBegin(
|
|
|
1678
1672
|
residual: *bool,
|
|
1679
1673
|
options: anytype,
|
|
1680
1674
|
) !void {
|
|
1681
|
-
assert(move.effect.onBegin());
|
|
1682
1675
|
return switch (move.effect) {
|
|
1683
1676
|
.Confusion => Effects.confusion(battle, player, move, options),
|
|
1684
1677
|
.Conversion => Effects.conversion(battle, player, options),
|
|
@@ -1701,7 +1694,6 @@ fn onBegin(
|
|
|
1701
1694
|
}
|
|
1702
1695
|
|
|
1703
1696
|
fn onEnd(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1704
|
-
assert(move.effect.onEnd());
|
|
1705
1697
|
return switch (move.effect) {
|
|
1706
1698
|
.Bide => Effects.bide(battle, player, options),
|
|
1707
1699
|
// zig fmt: off
|
|
@@ -1723,7 +1715,6 @@ fn alwaysHappens(
|
|
|
1723
1715
|
residual: *bool,
|
|
1724
1716
|
options: anytype,
|
|
1725
1717
|
) !void {
|
|
1726
|
-
assert(move.effect.alwaysHappens());
|
|
1727
1718
|
return switch (move.effect) {
|
|
1728
1719
|
.DrainHP, .DreamEater => Effects.drainHP(battle, player, options),
|
|
1729
1720
|
.Explode => Effects.explode(battle, player),
|
|
@@ -1762,7 +1753,7 @@ pub const Effects = struct {
|
|
|
1762
1753
|
side.active.volatiles.state = 0;
|
|
1763
1754
|
side.active.volatiles.attacks = Rolls.attackingDuration(battle, player, options);
|
|
1764
1755
|
|
|
1765
|
-
try options.log.start(battle.active(player), .Bide);
|
|
1756
|
+
try options.log.start(.{ battle.active(player), .Bide });
|
|
1766
1757
|
}
|
|
1767
1758
|
|
|
1768
1759
|
fn burnChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -1776,11 +1767,11 @@ pub const Effects = struct {
|
|
|
1776
1767
|
// GLITCH: Freeze top move selection desync can occur if thawed Pokémon is slower
|
|
1777
1768
|
if (Status.is(foe_stored.status, .FRZ)) {
|
|
1778
1769
|
assert(move.type == .Fire);
|
|
1779
|
-
try options.log.curestatus(
|
|
1770
|
+
try options.log.curestatus(.{
|
|
1780
1771
|
battle.active(player.foe()),
|
|
1781
1772
|
foe_stored.status,
|
|
1782
1773
|
.Message,
|
|
1783
|
-
);
|
|
1774
|
+
});
|
|
1784
1775
|
foe_stored.status = 0;
|
|
1785
1776
|
}
|
|
1786
1777
|
return;
|
|
@@ -1794,7 +1785,7 @@ pub const Effects = struct {
|
|
|
1794
1785
|
foe_stored.status = Status.init(.BRN);
|
|
1795
1786
|
foe.active.stats.atk = @max(foe.active.stats.atk / 2, 1);
|
|
1796
1787
|
|
|
1797
|
-
try options.log.status(battle.active(player.foe()), foe_stored.status, .None);
|
|
1788
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
1798
1789
|
}
|
|
1799
1790
|
|
|
1800
1791
|
fn charge(battle: anytype, player: Player, options: anytype) !void {
|
|
@@ -1804,8 +1795,8 @@ pub const Effects = struct {
|
|
|
1804
1795
|
volatiles.Charging = true;
|
|
1805
1796
|
const move = side.last_selected_move;
|
|
1806
1797
|
if (move == .Fly or move == .Dig) volatiles.Invulnerable = true;
|
|
1807
|
-
try options.log.laststill();
|
|
1808
|
-
try options.log.prepare(battle.active(player), move);
|
|
1798
|
+
try options.log.laststill(.{});
|
|
1799
|
+
try options.log.prepare(.{ battle.active(player), move });
|
|
1809
1800
|
}
|
|
1810
1801
|
|
|
1811
1802
|
fn confusion(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -1816,17 +1807,11 @@ pub const Effects = struct {
|
|
|
1816
1807
|
if (!try Rolls.confusionChance(battle, player, options)) return;
|
|
1817
1808
|
} else {
|
|
1818
1809
|
if (showdown) {
|
|
1819
|
-
if (!try checkHit(battle, player, move, options))
|
|
1820
|
-
|
|
1821
|
-
} else if (sub) {
|
|
1822
|
-
return options.log.fail(battle.active(player.foe()), .None);
|
|
1823
|
-
}
|
|
1810
|
+
if (!try checkHit(battle, player, move, options)) return;
|
|
1811
|
+
if (sub) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1824
1812
|
} else {
|
|
1825
|
-
if (sub) {
|
|
1826
|
-
|
|
1827
|
-
} else if (!try checkHit(battle, player, move, options)) {
|
|
1828
|
-
return;
|
|
1829
|
-
}
|
|
1813
|
+
if (sub) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1814
|
+
if (!try checkHit(battle, player, move, options)) return;
|
|
1830
1815
|
}
|
|
1831
1816
|
try options.chance.commit(player, .hit);
|
|
1832
1817
|
}
|
|
@@ -1835,23 +1820,24 @@ pub const Effects = struct {
|
|
|
1835
1820
|
foe.active.volatiles.Confusion = true;
|
|
1836
1821
|
foe.active.volatiles.confusion = Rolls.confusionDuration(battle, player, false, options);
|
|
1837
1822
|
|
|
1838
|
-
try options.log.start(battle.active(player.foe()), .Confusion);
|
|
1823
|
+
try options.log.start(.{ battle.active(player.foe()), .Confusion });
|
|
1839
1824
|
}
|
|
1840
1825
|
|
|
1841
1826
|
fn conversion(battle: anytype, player: Player, options: anytype) !void {
|
|
1842
1827
|
const foe = battle.foe(player);
|
|
1843
1828
|
|
|
1844
1829
|
if (foe.active.volatiles.Invulnerable) {
|
|
1845
|
-
try options.log.lastmiss();
|
|
1846
|
-
return options.log.miss(battle.active(player));
|
|
1830
|
+
try options.log.lastmiss(.{});
|
|
1831
|
+
return options.log.miss(.{battle.active(player)});
|
|
1847
1832
|
}
|
|
1848
1833
|
|
|
1849
1834
|
battle.side(player).active.types = foe.active.types;
|
|
1850
|
-
return options.log.
|
|
1835
|
+
return options.log.start(.{
|
|
1851
1836
|
battle.active(player),
|
|
1837
|
+
.TypeChange,
|
|
1852
1838
|
foe.active.types,
|
|
1853
1839
|
battle.active(player.foe()),
|
|
1854
|
-
);
|
|
1840
|
+
});
|
|
1855
1841
|
}
|
|
1856
1842
|
|
|
1857
1843
|
fn disable(battle: anytype, player: Player, move: Move.Data, options: anytype) !?Result {
|
|
@@ -1866,7 +1852,7 @@ pub const Effects = struct {
|
|
|
1866
1852
|
}
|
|
1867
1853
|
|
|
1868
1854
|
if (volatiles.disable_move != 0) {
|
|
1869
|
-
try options.log.fail(foe_ident, .None);
|
|
1855
|
+
try options.log.fail(.{ foe_ident, .None });
|
|
1870
1856
|
return null;
|
|
1871
1857
|
}
|
|
1872
1858
|
|
|
@@ -1882,7 +1868,7 @@ pub const Effects = struct {
|
|
|
1882
1868
|
// Technically this is still considered simply a "miss" on the cartridge,
|
|
1883
1869
|
// but diverging from Pokémon Showdown here would mostly just be pedantic
|
|
1884
1870
|
if (n == 0) {
|
|
1885
|
-
try options.log.fail(foe_ident, .None);
|
|
1871
|
+
try options.log.fail(.{ foe_ident, .None });
|
|
1886
1872
|
return null;
|
|
1887
1873
|
} else if (err) {
|
|
1888
1874
|
// GLITCH: Transform + Mirror Move / Metronome PP softlock
|
|
@@ -1895,12 +1881,13 @@ pub const Effects = struct {
|
|
|
1895
1881
|
volatiles.disable_duration = Rolls.disableDuration(battle, player, options);
|
|
1896
1882
|
|
|
1897
1883
|
const id = foe.active.move(volatiles.disable_move).id;
|
|
1898
|
-
try options.log.
|
|
1884
|
+
try options.log.start(.{ foe_ident, .Disable, id });
|
|
1899
1885
|
return null;
|
|
1900
1886
|
}
|
|
1901
1887
|
|
|
1902
1888
|
fn drainHP(battle: anytype, player: Player, options: anytype) !void {
|
|
1903
1889
|
var stored = battle.side(player).stored();
|
|
1890
|
+
const ident = battle.active(player);
|
|
1904
1891
|
|
|
1905
1892
|
if (battle.last_damage == 0) {
|
|
1906
1893
|
assert(showdown);
|
|
@@ -1913,7 +1900,7 @@ pub const Effects = struct {
|
|
|
1913
1900
|
if (stored.hp == stored.stats.hp) return;
|
|
1914
1901
|
stored.hp = @min(stored.stats.hp, stored.hp + drain);
|
|
1915
1902
|
|
|
1916
|
-
try options.log.
|
|
1903
|
+
try options.log.heal(.{ ident, stored, .Drain, battle.active(player.foe()) });
|
|
1917
1904
|
}
|
|
1918
1905
|
|
|
1919
1906
|
fn explode(battle: anytype, player: Player) !void {
|
|
@@ -1945,7 +1932,7 @@ pub const Effects = struct {
|
|
|
1945
1932
|
if (side.active.volatiles.FocusEnergy) return;
|
|
1946
1933
|
side.active.volatiles.FocusEnergy = true;
|
|
1947
1934
|
|
|
1948
|
-
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1935
|
+
try options.log.start(.{ battle.active(player), .FocusEnergy });
|
|
1949
1936
|
}
|
|
1950
1937
|
|
|
1951
1938
|
fn freezeChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -1967,7 +1954,7 @@ pub const Effects = struct {
|
|
|
1967
1954
|
foe_stored.status = Status.init(.FRZ);
|
|
1968
1955
|
// GLITCH: Hyper Beam recharging status is not cleared
|
|
1969
1956
|
|
|
1970
|
-
try options.log.status(foe_ident, foe_stored.status, .None);
|
|
1957
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
1971
1958
|
}
|
|
1972
1959
|
|
|
1973
1960
|
fn haze(battle: anytype, player: Player, options: anytype) !void {
|
|
@@ -1975,7 +1962,7 @@ pub const Effects = struct {
|
|
|
1975
1962
|
var side = battle.side(player);
|
|
1976
1963
|
var foe = battle.foe(player);
|
|
1977
1964
|
|
|
1978
|
-
|
|
1965
|
+
const side_stored = side.stored();
|
|
1979
1966
|
var foe_stored = foe.stored();
|
|
1980
1967
|
|
|
1981
1968
|
const player_ident = battle.active(player);
|
|
@@ -1987,8 +1974,8 @@ pub const Effects = struct {
|
|
|
1987
1974
|
side.active.stats = side_stored.stats;
|
|
1988
1975
|
foe.active.stats = foe_stored.stats;
|
|
1989
1976
|
|
|
1990
|
-
try log.activate(player_ident, .Haze);
|
|
1991
|
-
try log.clearallboost();
|
|
1977
|
+
try log.activate(.{ player_ident, .Haze });
|
|
1978
|
+
try log.clearallboost(.{});
|
|
1992
1979
|
|
|
1993
1980
|
// Pokémon Showdown clears P1 then P2 instead of status -> side -> foe
|
|
1994
1981
|
if (showdown) {
|
|
@@ -1996,11 +1983,11 @@ pub const Effects = struct {
|
|
|
1996
1983
|
const p: Player = @enumFromInt(i);
|
|
1997
1984
|
// Pokémon Showdown incorrectly does not prevent sleep/freeze from moving
|
|
1998
1985
|
if (p != player and Status.any(s.stored().status)) {
|
|
1999
|
-
try log.curestatus(foe_ident, foe_stored.status, .Silent);
|
|
1986
|
+
try log.curestatus(.{ foe_ident, foe_stored.status, .Silent });
|
|
2000
1987
|
s.stored().status = 0;
|
|
2001
1988
|
} else if (showdown and s.stored().status == Status.TOX) {
|
|
2002
1989
|
s.stored().status = Status.init(.PSN);
|
|
2003
|
-
try log.status(battle.active(p), s.stored().status, .None);
|
|
1990
|
+
try log.status(.{ battle.active(p), s.stored().status, .None });
|
|
2004
1991
|
}
|
|
2005
1992
|
try clearVolatiles(battle, p, p != player, options);
|
|
2006
1993
|
}
|
|
@@ -2009,7 +1996,7 @@ pub const Effects = struct {
|
|
|
2009
1996
|
if (Status.is(foe_stored.status, .FRZ) or Status.is(foe_stored.status, .SLP)) {
|
|
2010
1997
|
foe.last_selected_move = .SKIP_TURN;
|
|
2011
1998
|
}
|
|
2012
|
-
try log.curestatus(foe_ident, foe_stored.status, .Silent);
|
|
1999
|
+
try log.curestatus(.{ foe_ident, foe_stored.status, .Silent });
|
|
2013
2000
|
foe_stored.status = 0;
|
|
2014
2001
|
}
|
|
2015
2002
|
try clearVolatiles(battle, player, false, options);
|
|
@@ -2025,7 +2012,7 @@ pub const Effects = struct {
|
|
|
2025
2012
|
// GLITCH: HP recovery move failure glitches
|
|
2026
2013
|
const delta = stored.stats.hp - stored.hp;
|
|
2027
2014
|
if (delta == 0 or (delta & 255 == 255 and stored.hp % 256 != 0)) {
|
|
2028
|
-
return options.log.fail(ident, .None);
|
|
2015
|
+
return options.log.fail(.{ ident, .None });
|
|
2029
2016
|
}
|
|
2030
2017
|
|
|
2031
2018
|
const rest = side.last_selected_move == .Rest;
|
|
@@ -2033,7 +2020,7 @@ pub const Effects = struct {
|
|
|
2033
2020
|
// Adding the sleep status runs the sleep condition handler to roll duration
|
|
2034
2021
|
if (showdown) battle.rng.advance(1);
|
|
2035
2022
|
stored.status = Status.slf(2);
|
|
2036
|
-
try options.log.
|
|
2023
|
+
try options.log.status(.{ ident, stored.status, .From, Move.Rest });
|
|
2037
2024
|
stored.hp = stored.stats.hp;
|
|
2038
2025
|
} else {
|
|
2039
2026
|
stored.hp = @min(stored.stats.hp, stored.hp + (stored.stats.hp / 2));
|
|
@@ -2043,7 +2030,7 @@ pub const Effects = struct {
|
|
|
2043
2030
|
|
|
2044
2031
|
fn hyperBeam(battle: anytype, player: Player, options: anytype) !void {
|
|
2045
2032
|
battle.side(player).active.volatiles.Recharging = true;
|
|
2046
|
-
try options.log.mustrecharge(battle.active(player));
|
|
2033
|
+
try options.log.mustrecharge(.{battle.active(player)});
|
|
2047
2034
|
}
|
|
2048
2035
|
|
|
2049
2036
|
fn leechSeed(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -2052,33 +2039,32 @@ pub const Effects = struct {
|
|
|
2052
2039
|
if (showdown) {
|
|
2053
2040
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
2054
2041
|
if (!foe.active.volatiles.Invulnerable and foe.active.types.includes(.Grass)) {
|
|
2055
|
-
return options.log.immune(battle.active(player.foe()), .None);
|
|
2042
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
2056
2043
|
}
|
|
2057
2044
|
if (!try checkHit(battle, player, move, options)) return;
|
|
2058
2045
|
if (foe.active.volatiles.LeechSeed) return;
|
|
2059
2046
|
} else {
|
|
2060
2047
|
if (!try checkHit(battle, player, move, options)) return;
|
|
2061
2048
|
if (foe.active.types.includes(.Grass) or foe.active.volatiles.LeechSeed) {
|
|
2062
|
-
try options.log.lastmiss();
|
|
2063
|
-
return options.log.miss(battle.active(player));
|
|
2049
|
+
try options.log.lastmiss(.{});
|
|
2050
|
+
return options.log.miss(.{battle.active(player)});
|
|
2064
2051
|
}
|
|
2065
2052
|
}
|
|
2066
2053
|
|
|
2067
2054
|
try options.chance.commit(player, .hit);
|
|
2068
2055
|
foe.active.volatiles.LeechSeed = true;
|
|
2069
2056
|
|
|
2070
|
-
try options.log.start(battle.active(player.foe()), .LeechSeed);
|
|
2057
|
+
try options.log.start(.{ battle.active(player.foe()), .LeechSeed });
|
|
2071
2058
|
}
|
|
2072
2059
|
|
|
2073
2060
|
fn lightScreen(battle: anytype, player: Player, options: anytype) !void {
|
|
2074
2061
|
var side = battle.side(player);
|
|
2062
|
+
const ident = battle.active(player);
|
|
2075
2063
|
|
|
2076
|
-
if (side.active.volatiles.LightScreen) {
|
|
2077
|
-
return options.log.fail(battle.active(player), .None);
|
|
2078
|
-
}
|
|
2064
|
+
if (side.active.volatiles.LightScreen) return options.log.fail(.{ ident, .None });
|
|
2079
2065
|
side.active.volatiles.LightScreen = true;
|
|
2080
2066
|
|
|
2081
|
-
try options.log.start(
|
|
2067
|
+
try options.log.start(.{ ident, .LightScreen });
|
|
2082
2068
|
}
|
|
2083
2069
|
|
|
2084
2070
|
fn mimic(battle: anytype, player: Player, move: Move.Data, mslot: u8, options: anytype) !void {
|
|
@@ -2107,7 +2093,7 @@ pub const Effects = struct {
|
|
|
2107
2093
|
// Invulnerable foes or 1/256 miss can trigger |-miss| instead of |-fail|
|
|
2108
2094
|
if (!try checkHit(battle, player, move, options)) return;
|
|
2109
2095
|
try options.chance.commit(player, .hit);
|
|
2110
|
-
return options.log.fail(battle.active(player.foe()), .None);
|
|
2096
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2111
2097
|
}
|
|
2112
2098
|
}
|
|
2113
2099
|
if (!try checkHit(battle, player, move, options)) return;
|
|
@@ -2116,7 +2102,7 @@ pub const Effects = struct {
|
|
|
2116
2102
|
const rslot = try Rolls.moveSlot(battle, player, &foe.active.moves, 0, options);
|
|
2117
2103
|
side.active.move(oslot).id = foe.active.move(rslot).id;
|
|
2118
2104
|
|
|
2119
|
-
try options.log.
|
|
2105
|
+
try options.log.start(.{ battle.active(player), .Mimic, side.active.move(oslot).id });
|
|
2120
2106
|
}
|
|
2121
2107
|
|
|
2122
2108
|
fn mist(battle: anytype, player: Player, options: anytype) !void {
|
|
@@ -2125,7 +2111,7 @@ pub const Effects = struct {
|
|
|
2125
2111
|
if (side.active.volatiles.Mist) return;
|
|
2126
2112
|
side.active.volatiles.Mist = true;
|
|
2127
2113
|
|
|
2128
|
-
try options.log.start(battle.active(player), .Mist);
|
|
2114
|
+
try options.log.start(.{ battle.active(player), .Mist });
|
|
2129
2115
|
}
|
|
2130
2116
|
|
|
2131
2117
|
fn multiHit(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -2151,17 +2137,18 @@ pub const Effects = struct {
|
|
|
2151
2137
|
|
|
2152
2138
|
if (showdown) {
|
|
2153
2139
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
2154
|
-
if (immune and !foe.active.volatiles.Invulnerable)
|
|
2155
|
-
|
|
2140
|
+
if (immune and !foe.active.volatiles.Invulnerable) {
|
|
2141
|
+
return log.immune(.{ foe_ident, .None });
|
|
2142
|
+
} else if (!try checkHit(battle, player, move, options)) return;
|
|
2156
2143
|
}
|
|
2157
2144
|
if (Status.any(foe_stored.status)) {
|
|
2158
|
-
return log.fail(
|
|
2145
|
+
return log.fail(.{
|
|
2159
2146
|
foe_ident,
|
|
2160
|
-
if (Status.is(foe_stored.status, .PAR)) .Paralysis else .None,
|
|
2161
|
-
);
|
|
2147
|
+
if (Status.is(foe_stored.status, .PAR)) Fail.Paralysis else Fail.None,
|
|
2148
|
+
});
|
|
2162
2149
|
}
|
|
2163
2150
|
if (!showdown) {
|
|
2164
|
-
if (immune) return log.immune(foe_ident, .None);
|
|
2151
|
+
if (immune) return log.immune(.{ foe_ident, .None });
|
|
2165
2152
|
if (!try checkHit(battle, player, move, options)) return;
|
|
2166
2153
|
}
|
|
2167
2154
|
try options.chance.commit(player, .hit);
|
|
@@ -2169,7 +2156,7 @@ pub const Effects = struct {
|
|
|
2169
2156
|
foe_stored.status = Status.init(.PAR);
|
|
2170
2157
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2171
2158
|
|
|
2172
|
-
try log.status(foe_ident, foe_stored.status, .None);
|
|
2159
|
+
try log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2173
2160
|
}
|
|
2174
2161
|
|
|
2175
2162
|
fn paralyzeChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -2191,12 +2178,12 @@ pub const Effects = struct {
|
|
|
2191
2178
|
foe_stored.status = Status.init(.PAR);
|
|
2192
2179
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2193
2180
|
|
|
2194
|
-
try options.log.status(battle.active(player.foe()), foe_stored.status, .None);
|
|
2181
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2195
2182
|
}
|
|
2196
2183
|
|
|
2197
2184
|
fn payDay(battle: anytype, player: Player, options: anytype) !void {
|
|
2198
2185
|
if (!showdown or !battle.foe(player).active.volatiles.Substitute) {
|
|
2199
|
-
try options.log.fieldactivate();
|
|
2186
|
+
try options.log.fieldactivate(.{});
|
|
2200
2187
|
}
|
|
2201
2188
|
}
|
|
2202
2189
|
|
|
@@ -2211,23 +2198,23 @@ pub const Effects = struct {
|
|
|
2211
2198
|
return;
|
|
2212
2199
|
} else if (foe.active.volatiles.Substitute) {
|
|
2213
2200
|
if (move.effect != .Poison) return;
|
|
2214
|
-
return log.fail(foe_ident, .None);
|
|
2201
|
+
return log.fail(.{ foe_ident, .None });
|
|
2215
2202
|
} else if (Status.any(foe_stored.status)) {
|
|
2216
2203
|
if (move.effect != .Poison) return if (showdown) battle.rng.advance(1);
|
|
2217
2204
|
// Pokémon Showdown considers Toxic to be a status even in Generation I and so
|
|
2218
2205
|
// will not include a fail reason for Toxic vs. Poison or vice-versa...
|
|
2219
|
-
return log.fail(foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2206
|
+
return log.fail(.{ foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2220
2207
|
if (!showdown)
|
|
2221
|
-
.Poison
|
|
2208
|
+
Fail.Poison
|
|
2222
2209
|
else if (toxic == (foe_stored.status == Status.TOX))
|
|
2223
|
-
if (toxic) .Toxic else .Poison
|
|
2210
|
+
if (toxic) Fail.Toxic else Fail.Poison
|
|
2224
2211
|
else
|
|
2225
|
-
.None
|
|
2212
|
+
Fail.None
|
|
2226
2213
|
else
|
|
2227
|
-
.None);
|
|
2214
|
+
Fail.None });
|
|
2228
2215
|
} else if (foe.active.types.includes(.Poison)) {
|
|
2229
2216
|
if (move.effect != .Poison) return if (showdown) battle.rng.advance(1);
|
|
2230
|
-
return log.immune(foe_ident, .None);
|
|
2217
|
+
return log.immune(.{ foe_ident, .None });
|
|
2231
2218
|
}
|
|
2232
2219
|
|
|
2233
2220
|
if (move.effect == .Poison) {
|
|
@@ -2246,7 +2233,7 @@ pub const Effects = struct {
|
|
|
2246
2233
|
foe.active.volatiles.toxic = 0;
|
|
2247
2234
|
}
|
|
2248
2235
|
|
|
2249
|
-
try log.status(foe_ident, foe_stored.status, .None);
|
|
2236
|
+
try log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2250
2237
|
}
|
|
2251
2238
|
|
|
2252
2239
|
fn rage(battle: anytype, player: Player) !void {
|
|
@@ -2266,22 +2253,23 @@ pub const Effects = struct {
|
|
|
2266
2253
|
@as(u8, if (side.last_selected_move == .Struggle) 2 else 4), 1));
|
|
2267
2254
|
stored.hp = @intCast(@max(@as(i16, @intCast(stored.hp)) - damage, 0));
|
|
2268
2255
|
|
|
2269
|
-
try options.log.
|
|
2256
|
+
try options.log.damage(.{
|
|
2270
2257
|
battle.active(player),
|
|
2271
2258
|
stored,
|
|
2272
2259
|
.RecoilOf,
|
|
2273
2260
|
battle.active(player.foe()),
|
|
2274
|
-
);
|
|
2261
|
+
});
|
|
2275
2262
|
if (showdown and stored.hp == 0) residual.* = false;
|
|
2276
2263
|
}
|
|
2277
2264
|
|
|
2278
2265
|
fn reflect(battle: anytype, player: Player, options: anytype) !void {
|
|
2279
2266
|
var side = battle.side(player);
|
|
2267
|
+
const ident = battle.active(player);
|
|
2280
2268
|
|
|
2281
|
-
if (side.active.volatiles.Reflect) return options.log.fail(
|
|
2269
|
+
if (side.active.volatiles.Reflect) return options.log.fail(.{ ident, .None });
|
|
2282
2270
|
side.active.volatiles.Reflect = true;
|
|
2283
2271
|
|
|
2284
|
-
try options.log.start(
|
|
2272
|
+
try options.log.start(.{ ident, .Reflect });
|
|
2285
2273
|
}
|
|
2286
2274
|
|
|
2287
2275
|
fn sleep(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -2295,10 +2283,10 @@ pub const Effects = struct {
|
|
|
2295
2283
|
// the Sleep Clause Mod might activate, causing us to not actually bypass
|
|
2296
2284
|
} else {
|
|
2297
2285
|
if (Status.any(foe_stored.status)) {
|
|
2298
|
-
return options.log.fail(
|
|
2286
|
+
return options.log.fail(.{
|
|
2299
2287
|
foe_ident,
|
|
2300
|
-
if (Status.is(foe_stored.status, .SLP)) .Sleep else .None,
|
|
2301
|
-
);
|
|
2288
|
+
if (Status.is(foe_stored.status, .SLP)) Fail.Sleep else Fail.None,
|
|
2289
|
+
});
|
|
2302
2290
|
}
|
|
2303
2291
|
// If checkHit in doMove didn't return true Pokémon Showdown wouldn't be in here
|
|
2304
2292
|
if (!showdown and !try checkHit(battle, player, move, options)) return;
|
|
@@ -2315,17 +2303,17 @@ pub const Effects = struct {
|
|
|
2315
2303
|
|
|
2316
2304
|
foe_stored.status = Status.slp(Rolls.sleepDuration(battle, player, options));
|
|
2317
2305
|
const last = battle.side(player).last_selected_move;
|
|
2318
|
-
try options.log.
|
|
2306
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .From, last });
|
|
2319
2307
|
}
|
|
2320
2308
|
|
|
2321
2309
|
fn splash(battle: anytype, player: Player, options: anytype) !void {
|
|
2322
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2310
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2323
2311
|
}
|
|
2324
2312
|
|
|
2325
2313
|
fn substitute(battle: anytype, player: Player, residual: *bool, options: anytype) !void {
|
|
2326
2314
|
var side = battle.side(player);
|
|
2327
2315
|
if (side.active.volatiles.Substitute) {
|
|
2328
|
-
return options.log.fail(battle.active(player), .Substitute);
|
|
2316
|
+
return options.log.fail(.{ battle.active(player), .Substitute });
|
|
2329
2317
|
}
|
|
2330
2318
|
|
|
2331
2319
|
assert(side.stored().stats.hp <= 1023);
|
|
@@ -2336,15 +2324,17 @@ pub const Effects = struct {
|
|
|
2336
2324
|
// the target's HP is exactly divisible by 4 (here we're using an inlined divCeil routine to
|
|
2337
2325
|
// avoid having to convert to floating point)
|
|
2338
2326
|
const required_hp = if (showdown) @divFloor(side.stored().stats.hp - 1, 4) + 1 else hp;
|
|
2339
|
-
if (side.stored().hp < required_hp)
|
|
2327
|
+
if (side.stored().hp < required_hp) {
|
|
2328
|
+
return options.log.fail(.{ battle.active(player), .Weak });
|
|
2329
|
+
}
|
|
2340
2330
|
|
|
2341
2331
|
// GLITCH: can leave the user with 0 HP (faints later) because didn't check '<=' above
|
|
2342
2332
|
side.stored().hp -= hp;
|
|
2343
2333
|
side.active.volatiles.substitute = hp + 1;
|
|
2344
2334
|
side.active.volatiles.Substitute = true;
|
|
2345
|
-
try options.log.start(battle.active(player), .Substitute);
|
|
2335
|
+
try options.log.start(.{ battle.active(player), .Substitute });
|
|
2346
2336
|
if (hp > 0) {
|
|
2347
|
-
try options.log.damage(battle.active(player), side.stored(), .None);
|
|
2337
|
+
try options.log.damage(.{ battle.active(player), side.stored(), .None });
|
|
2348
2338
|
if (showdown and side.stored().hp == 0) residual.* = false;
|
|
2349
2339
|
}
|
|
2350
2340
|
}
|
|
@@ -2358,8 +2348,8 @@ pub const Effects = struct {
|
|
|
2358
2348
|
}
|
|
2359
2349
|
battle.last_damage = 0;
|
|
2360
2350
|
} else {
|
|
2361
|
-
try options.log.fail(battle.active(player), .None);
|
|
2362
|
-
try options.log.laststill();
|
|
2351
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2352
|
+
try options.log.laststill(.{});
|
|
2363
2353
|
}
|
|
2364
2354
|
}
|
|
2365
2355
|
|
|
@@ -2398,7 +2388,7 @@ pub const Effects = struct {
|
|
|
2398
2388
|
side.active.moves[i].pp = if (m.id != .None) 5 else 0;
|
|
2399
2389
|
}
|
|
2400
2390
|
|
|
2401
|
-
try options.log.transform(battle.active(player), foe_ident);
|
|
2391
|
+
try options.log.transform(.{ battle.active(player), foe_ident });
|
|
2402
2392
|
}
|
|
2403
2393
|
|
|
2404
2394
|
fn binding(battle: anytype, player: Player, rewrap: bool, options: anytype) !void {
|
|
@@ -2428,87 +2418,87 @@ pub const Effects = struct {
|
|
|
2428
2418
|
switch (move.effect) {
|
|
2429
2419
|
.AttackUp1, .AttackUp2, .Rage => {
|
|
2430
2420
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2431
|
-
if (boosts.atk == 6) return log.fail(ident, .None);
|
|
2421
|
+
if (boosts.atk == 6) return log.fail(.{ ident, .None });
|
|
2432
2422
|
const n: u2 = if (move.effect == .AttackUp2) 2 else 1;
|
|
2433
2423
|
boosts.atk = @as(i4, @intCast(@min(6, @as(i8, boosts.atk) + n)));
|
|
2434
|
-
const reason = if (move.effect == .Rage)
|
|
2424
|
+
const reason: Boost = if (move.effect == .Rage) .Rage else .Attack;
|
|
2435
2425
|
if (stats.atk == MAX_STAT_VALUE) {
|
|
2436
2426
|
boosts.atk -= 1;
|
|
2437
2427
|
if (showdown) {
|
|
2438
|
-
try log.boost(ident, reason, n);
|
|
2439
|
-
try log.boost(ident,
|
|
2428
|
+
try log.boost(.{ ident, reason, n });
|
|
2429
|
+
try log.boost(.{ ident, .Attack, -1 });
|
|
2440
2430
|
if (move.effect == .Rage) return;
|
|
2441
2431
|
}
|
|
2442
|
-
return log.fail(ident, .None);
|
|
2432
|
+
return log.fail(.{ ident, .None });
|
|
2443
2433
|
}
|
|
2444
|
-
|
|
2434
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.atk) + 6))];
|
|
2445
2435
|
const stat = unmodifiedStats(battle, side).atk;
|
|
2446
2436
|
stats.atk = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2447
|
-
try log.boost(ident, reason, n);
|
|
2437
|
+
try log.boost(.{ ident, reason, n });
|
|
2448
2438
|
// Pokémon Showdown doesn't re-apply status modifiers after Rage boosts
|
|
2449
2439
|
if (showdown and move.effect == .Rage) return;
|
|
2450
2440
|
},
|
|
2451
2441
|
.DefenseUp1, .DefenseUp2 => {
|
|
2452
2442
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2453
|
-
if (boosts.def == 6) return log.fail(ident, .None);
|
|
2443
|
+
if (boosts.def == 6) return log.fail(.{ ident, .None });
|
|
2454
2444
|
const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
|
|
2455
2445
|
boosts.def = @intCast(@min(6, @as(i8, boosts.def) + n));
|
|
2456
2446
|
if (stats.def == MAX_STAT_VALUE) {
|
|
2457
2447
|
boosts.def -= 1;
|
|
2458
2448
|
if (showdown) {
|
|
2459
|
-
try log.boost(ident, .Defense, n);
|
|
2460
|
-
try log.boost(ident, .Defense, -1);
|
|
2449
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2450
|
+
try log.boost(.{ ident, .Defense, -1 });
|
|
2461
2451
|
}
|
|
2462
|
-
return log.fail(ident, .None);
|
|
2452
|
+
return log.fail(.{ ident, .None });
|
|
2463
2453
|
}
|
|
2464
|
-
|
|
2454
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.def) + 6))];
|
|
2465
2455
|
const stat = unmodifiedStats(battle, side).def;
|
|
2466
2456
|
stats.def = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2467
|
-
try log.boost(ident, .Defense, n);
|
|
2457
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2468
2458
|
},
|
|
2469
2459
|
.SpeedUp2 => {
|
|
2470
2460
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2471
|
-
if (boosts.spe == 6) return log.fail(ident, .None);
|
|
2461
|
+
if (boosts.spe == 6) return log.fail(.{ ident, .None });
|
|
2472
2462
|
boosts.spe = @intCast(@min(6, @as(i8, boosts.spe) + 2));
|
|
2473
2463
|
if (stats.spe == MAX_STAT_VALUE) {
|
|
2474
2464
|
boosts.spe -= 1;
|
|
2475
2465
|
if (showdown) {
|
|
2476
|
-
try log.boost(ident, .Speed, 2);
|
|
2477
|
-
try log.boost(ident, .Speed, -1);
|
|
2466
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2467
|
+
try log.boost(.{ ident, .Speed, -1 });
|
|
2478
2468
|
}
|
|
2479
|
-
return log.fail(ident, .None);
|
|
2469
|
+
return log.fail(.{ ident, .None });
|
|
2480
2470
|
}
|
|
2481
|
-
|
|
2471
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spe) + 6))];
|
|
2482
2472
|
const stat = unmodifiedStats(battle, side).spe;
|
|
2483
2473
|
stats.spe = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2484
|
-
try log.boost(ident, .Speed, 2);
|
|
2474
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2485
2475
|
},
|
|
2486
2476
|
.SpecialUp1, .SpecialUp2 => {
|
|
2487
2477
|
assert(boosts.spc >= -6 and boosts.spc <= 6);
|
|
2488
|
-
if (boosts.spc == 6) return log.fail(ident, .None);
|
|
2478
|
+
if (boosts.spc == 6) return log.fail(.{ ident, .None });
|
|
2489
2479
|
const n: u2 = if (move.effect == .SpecialUp2) 2 else 1;
|
|
2490
2480
|
boosts.spc = @intCast(@min(6, @as(i8, boosts.spc) + n));
|
|
2491
2481
|
if (stats.spc == MAX_STAT_VALUE) {
|
|
2492
2482
|
boosts.spc -= 1;
|
|
2493
2483
|
if (showdown) {
|
|
2494
|
-
try log.boost(ident, .SpecialAttack, n);
|
|
2495
|
-
try log.boost(ident, .SpecialAttack, -1);
|
|
2496
|
-
try log.boost(ident, .SpecialDefense, n);
|
|
2497
|
-
try log.boost(ident, .SpecialDefense, -1);
|
|
2484
|
+
try log.boost(.{ ident, .SpecialAttack, n });
|
|
2485
|
+
try log.boost(.{ ident, .SpecialAttack, -1 });
|
|
2486
|
+
try log.boost(.{ ident, .SpecialDefense, n });
|
|
2487
|
+
try log.boost(.{ ident, .SpecialDefense, -1 });
|
|
2498
2488
|
}
|
|
2499
|
-
return log.fail(ident, .None);
|
|
2489
|
+
return log.fail(.{ ident, .None });
|
|
2500
2490
|
}
|
|
2501
|
-
|
|
2491
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spc) + 6))];
|
|
2502
2492
|
const stat = unmodifiedStats(battle, side).spc;
|
|
2503
2493
|
stats.spc = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2504
|
-
try log.boost(ident, .SpecialAttack, n);
|
|
2505
|
-
try log.boost(ident, .SpecialDefense, n);
|
|
2494
|
+
try log.boost(.{ ident, .SpecialAttack, n });
|
|
2495
|
+
try log.boost(.{ ident, .SpecialDefense, n });
|
|
2506
2496
|
},
|
|
2507
2497
|
.EvasionUp1 => {
|
|
2508
2498
|
assert(boosts.evasion >= -6 and boosts.evasion <= 6);
|
|
2509
|
-
if (boosts.evasion == 6) return log.fail(ident, .None);
|
|
2499
|
+
if (boosts.evasion == 6) return log.fail(.{ ident, .None });
|
|
2510
2500
|
boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
|
|
2511
|
-
try log.boost(ident, .Evasion, 1);
|
|
2501
|
+
try log.boost(.{ ident, .Evasion, 1 });
|
|
2512
2502
|
},
|
|
2513
2503
|
else => unreachable,
|
|
2514
2504
|
}
|
|
@@ -2523,7 +2513,7 @@ pub const Effects = struct {
|
|
|
2523
2513
|
const foe_ident = battle.active(player.foe());
|
|
2524
2514
|
const secondary = move.effect.isStatDownChance();
|
|
2525
2515
|
|
|
2526
|
-
if (foe.active.volatiles.Substitute) return if (!secondary) log.fail(foe_ident, .None);
|
|
2516
|
+
if (foe.active.volatiles.Substitute) return if (!secondary) log.fail(.{ foe_ident, .None });
|
|
2527
2517
|
|
|
2528
2518
|
if (secondary) {
|
|
2529
2519
|
const proc = try Rolls.unboost(battle, player, options);
|
|
@@ -2541,82 +2531,82 @@ pub const Effects = struct {
|
|
|
2541
2531
|
switch (move.effect) {
|
|
2542
2532
|
.AttackDown1, .AttackDownChance => {
|
|
2543
2533
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2544
|
-
if (boosts.atk == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2534
|
+
if (boosts.atk == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2545
2535
|
boosts.atk = @intCast(@max(-6, @as(i8, boosts.atk) - 1));
|
|
2546
2536
|
if (stats.atk == 1) {
|
|
2547
2537
|
boosts.atk += 1;
|
|
2548
2538
|
if (showdown) {
|
|
2549
|
-
try log.boost(foe_ident, .Attack, -1);
|
|
2550
|
-
try log.boost(foe_ident, .Attack, 1);
|
|
2539
|
+
try log.boost(.{ foe_ident, .Attack, -1 });
|
|
2540
|
+
try log.boost(.{ foe_ident, .Attack, 1 });
|
|
2551
2541
|
}
|
|
2552
|
-
return log.fail(foe_ident, .None);
|
|
2542
|
+
return log.fail(.{ foe_ident, .None });
|
|
2553
2543
|
}
|
|
2554
|
-
|
|
2544
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.atk) + 6))];
|
|
2555
2545
|
const stat = unmodifiedStats(battle, foe).atk;
|
|
2556
2546
|
stats.atk = @max(1, stat * mod[0] / mod[1]);
|
|
2557
|
-
try log.boost(foe_ident, .Attack, -1);
|
|
2547
|
+
try log.boost(.{ foe_ident, .Attack, -1 });
|
|
2558
2548
|
},
|
|
2559
2549
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2560
2550
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2561
|
-
if (boosts.def == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2551
|
+
if (boosts.def == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2562
2552
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2563
2553
|
boosts.def = @intCast(@max(-6, @as(i8, boosts.def) - n));
|
|
2564
2554
|
if (stats.def == 1) {
|
|
2565
2555
|
boosts.def += 1;
|
|
2566
2556
|
if (showdown) {
|
|
2567
|
-
try log.boost(foe_ident, .Defense, -@as(i8, n));
|
|
2568
|
-
try log.boost(foe_ident, .Defense, 1);
|
|
2557
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2558
|
+
try log.boost(.{ foe_ident, .Defense, 1 });
|
|
2569
2559
|
}
|
|
2570
|
-
return log.fail(foe_ident, .None);
|
|
2560
|
+
return log.fail(.{ foe_ident, .None });
|
|
2571
2561
|
}
|
|
2572
|
-
|
|
2562
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.def) + 6))];
|
|
2573
2563
|
const stat = unmodifiedStats(battle, foe).def;
|
|
2574
2564
|
stats.def = @max(1, stat * mod[0] / mod[1]);
|
|
2575
|
-
try log.boost(foe_ident, .Defense, -@as(i8, n));
|
|
2565
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2576
2566
|
},
|
|
2577
2567
|
.SpeedDown1, .SpeedDownChance => {
|
|
2578
2568
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2579
|
-
if (boosts.spe == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2569
|
+
if (boosts.spe == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2580
2570
|
boosts.spe = @intCast(@max(-6, @as(i8, boosts.spe) - 1));
|
|
2581
2571
|
if (stats.spe == 1) {
|
|
2582
2572
|
boosts.spe += 1;
|
|
2583
2573
|
if (showdown) {
|
|
2584
|
-
try log.boost(foe_ident, .Speed, -1);
|
|
2585
|
-
try log.boost(foe_ident, .Speed, 1);
|
|
2574
|
+
try log.boost(.{ foe_ident, .Speed, -1 });
|
|
2575
|
+
try log.boost(.{ foe_ident, .Speed, 1 });
|
|
2586
2576
|
}
|
|
2587
|
-
return log.fail(foe_ident, .None);
|
|
2577
|
+
return log.fail(.{ foe_ident, .None });
|
|
2588
2578
|
}
|
|
2589
|
-
|
|
2579
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spe) + 6))];
|
|
2590
2580
|
const stat = unmodifiedStats(battle, foe).spe;
|
|
2591
2581
|
stats.spe = @max(1, stat * mod[0] / mod[1]);
|
|
2592
|
-
try log.boost(foe_ident, .Speed, -1);
|
|
2582
|
+
try log.boost(.{ foe_ident, .Speed, -1 });
|
|
2593
2583
|
assert(boosts.spe >= -6);
|
|
2594
2584
|
},
|
|
2595
2585
|
.SpecialDownChance => {
|
|
2596
2586
|
assert(boosts.spc >= -6 and boosts.spc <= 6);
|
|
2597
|
-
if (boosts.spc == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2587
|
+
if (boosts.spc == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2598
2588
|
boosts.spc = @intCast(@max(-6, @as(i8, boosts.spc) - 1));
|
|
2599
2589
|
if (stats.spc == 1) {
|
|
2600
2590
|
boosts.spc += 1;
|
|
2601
2591
|
if (showdown) {
|
|
2602
|
-
try log.boost(foe_ident, .SpecialAttack, -1);
|
|
2603
|
-
try log.boost(foe_ident, .SpecialAttack, 1);
|
|
2604
|
-
try log.boost(foe_ident, .SpecialDefense, -1);
|
|
2605
|
-
try log.boost(foe_ident, .SpecialDefense, 1);
|
|
2592
|
+
try log.boost(.{ foe_ident, .SpecialAttack, -1 });
|
|
2593
|
+
try log.boost(.{ foe_ident, .SpecialAttack, 1 });
|
|
2594
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2595
|
+
try log.boost(.{ foe_ident, .SpecialDefense, 1 });
|
|
2606
2596
|
}
|
|
2607
|
-
return log.fail(foe_ident, .None);
|
|
2597
|
+
return log.fail(.{ foe_ident, .None });
|
|
2608
2598
|
}
|
|
2609
|
-
|
|
2599
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spc) + 6))];
|
|
2610
2600
|
const stat = unmodifiedStats(battle, foe).spc;
|
|
2611
2601
|
stats.spc = @max(1, stat * mod[0] / mod[1]);
|
|
2612
|
-
try log.boost(foe_ident, .SpecialAttack, -1);
|
|
2613
|
-
try log.boost(foe_ident, .SpecialDefense, -1);
|
|
2602
|
+
try log.boost(.{ foe_ident, .SpecialAttack, -1 });
|
|
2603
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2614
2604
|
},
|
|
2615
2605
|
.AccuracyDown1 => {
|
|
2616
2606
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
2617
|
-
if (boosts.accuracy == -6) return if (fail) try log.fail(foe_ident, .None);
|
|
2607
|
+
if (boosts.accuracy == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2618
2608
|
boosts.accuracy = @intCast(@max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2619
|
-
try log.boost(foe_ident, .Accuracy, -1);
|
|
2609
|
+
try log.boost(.{ foe_ident, .Accuracy, -1 });
|
|
2620
2610
|
},
|
|
2621
2611
|
else => unreachable,
|
|
2622
2612
|
}
|
|
@@ -2656,24 +2646,24 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
|
|
|
2656
2646
|
if (volatiles.disable_move != 0) {
|
|
2657
2647
|
volatiles.disable_move = 0;
|
|
2658
2648
|
volatiles.disable_duration = 0;
|
|
2659
|
-
try log.end(ident, .DisableSilent);
|
|
2649
|
+
try log.end(.{ ident, .DisableSilent });
|
|
2660
2650
|
}
|
|
2661
2651
|
if (volatiles.Confusion) {
|
|
2662
2652
|
// volatiles.confusion is left unchanged
|
|
2663
2653
|
volatiles.Confusion = false;
|
|
2664
|
-
try log.end(ident, .ConfusionSilent);
|
|
2654
|
+
try log.end(.{ ident, .ConfusionSilent });
|
|
2665
2655
|
}
|
|
2666
2656
|
if (volatiles.Mist) {
|
|
2667
2657
|
volatiles.Mist = false;
|
|
2668
|
-
try log.end(ident, .MistSilent);
|
|
2658
|
+
try log.end(.{ ident, .MistSilent });
|
|
2669
2659
|
}
|
|
2670
2660
|
if (volatiles.FocusEnergy) {
|
|
2671
2661
|
volatiles.FocusEnergy = false;
|
|
2672
|
-
try log.end(ident, .FocusEnergySilent);
|
|
2662
|
+
try log.end(.{ ident, .FocusEnergySilent });
|
|
2673
2663
|
}
|
|
2674
2664
|
if (volatiles.LeechSeed) {
|
|
2675
2665
|
volatiles.LeechSeed = false;
|
|
2676
|
-
try log.end(ident, .LeechSeedSilent);
|
|
2666
|
+
try log.end(.{ ident, .LeechSeedSilent });
|
|
2677
2667
|
}
|
|
2678
2668
|
if (!showdown and volatiles.Toxic) {
|
|
2679
2669
|
volatiles.Toxic = false;
|
|
@@ -2681,17 +2671,17 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
|
|
|
2681
2671
|
}
|
|
2682
2672
|
if (volatiles.LightScreen) {
|
|
2683
2673
|
volatiles.LightScreen = false;
|
|
2684
|
-
try log.end(ident, .LightScreenSilent);
|
|
2674
|
+
try log.end(.{ ident, .LightScreenSilent });
|
|
2685
2675
|
}
|
|
2686
2676
|
if (volatiles.Reflect) {
|
|
2687
2677
|
volatiles.Reflect = false;
|
|
2688
|
-
try log.end(ident, .ReflectSilent);
|
|
2678
|
+
try log.end(.{ ident, .ReflectSilent });
|
|
2689
2679
|
}
|
|
2690
2680
|
if (showdown and volatiles.Toxic) {
|
|
2691
2681
|
volatiles.Toxic = false;
|
|
2692
2682
|
// Pokémon Showdown erroneously clears the toxic counter
|
|
2693
2683
|
volatiles.toxic = 0;
|
|
2694
|
-
try log.end(ident, .ToxicSilent);
|
|
2684
|
+
try log.end(.{ ident, .ToxicSilent });
|
|
2695
2685
|
}
|
|
2696
2686
|
}
|
|
2697
2687
|
|
|
@@ -2699,6 +2689,8 @@ pub const Rolls = struct {
|
|
|
2699
2689
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2700
2690
|
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2701
2691
|
player == .P1
|
|
2692
|
+
else if (@hasDecl(@TypeOf(battle.rng), "speedTie"))
|
|
2693
|
+
battle.rng.speedTie()
|
|
2702
2694
|
else if (showdown)
|
|
2703
2695
|
battle.rng.range(u8, 0, 2) == 0
|
|
2704
2696
|
else
|
|
@@ -2711,6 +2703,8 @@ pub const Rolls = struct {
|
|
|
2711
2703
|
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2712
2704
|
const crit = if (options.calc.overridden(player, .critical_hit)) |val|
|
|
2713
2705
|
val == .true
|
|
2706
|
+
else if (@hasDecl(@TypeOf(battle.rng), "criticalHit"))
|
|
2707
|
+
battle.rng.criticalHit(player, rate)
|
|
2714
2708
|
else if (showdown)
|
|
2715
2709
|
battle.rng.chance(u8, rate, 256)
|
|
2716
2710
|
else
|
|
@@ -2723,6 +2717,8 @@ pub const Rolls = struct {
|
|
|
2723
2717
|
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2724
2718
|
const roll = if (options.calc.overridden(player, .damage)) |val|
|
|
2725
2719
|
val
|
|
2720
|
+
else if (@hasDecl(@TypeOf(battle.rng), "damage"))
|
|
2721
|
+
battle.rng.damage(player)
|
|
2726
2722
|
else roll: {
|
|
2727
2723
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
2728
2724
|
while (true) {
|
|
@@ -2739,6 +2735,8 @@ pub const Rolls = struct {
|
|
|
2739
2735
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) bool {
|
|
2740
2736
|
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2741
2737
|
val == .true
|
|
2738
|
+
else if (@hasDecl(@TypeOf(battle.rng), "hit"))
|
|
2739
|
+
battle.rng.hit(player, accuracy)
|
|
2742
2740
|
else if (showdown)
|
|
2743
2741
|
battle.rng.chance(u8, accuracy, 256)
|
|
2744
2742
|
else
|
|
@@ -2751,6 +2749,8 @@ pub const Rolls = struct {
|
|
|
2751
2749
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2752
2750
|
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2753
2751
|
val == .true
|
|
2752
|
+
else if (@hasDecl(@TypeOf(battle.rng), "confused"))
|
|
2753
|
+
battle.rng.confused(player)
|
|
2754
2754
|
else if (showdown)
|
|
2755
2755
|
!battle.rng.chance(u8, 128, 256)
|
|
2756
2756
|
else
|
|
@@ -2763,6 +2763,8 @@ pub const Rolls = struct {
|
|
|
2763
2763
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2764
2764
|
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2765
2765
|
val == .true
|
|
2766
|
+
else if (@hasDecl(@TypeOf(battle.rng), "paralyzed"))
|
|
2767
|
+
battle.rng.paralyzed(player)
|
|
2766
2768
|
else if (showdown)
|
|
2767
2769
|
battle.rng.chance(u8, 63, 256)
|
|
2768
2770
|
else
|
|
@@ -2775,6 +2777,8 @@ pub const Rolls = struct {
|
|
|
2775
2777
|
fn confusionChance(battle: anytype, player: Player, options: anytype) !bool {
|
|
2776
2778
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2777
2779
|
val == .true
|
|
2780
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2781
|
+
battle.rng.secondaryChance(player, 25)
|
|
2778
2782
|
else if (showdown)
|
|
2779
2783
|
battle.rng.chance(u8, 25, 256)
|
|
2780
2784
|
else
|
|
@@ -2789,6 +2793,8 @@ pub const Rolls = struct {
|
|
|
2789
2793
|
|
|
2790
2794
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2791
2795
|
val == .true
|
|
2796
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2797
|
+
battle.rng.secondaryChance(player, rate)
|
|
2792
2798
|
else if (showdown)
|
|
2793
2799
|
battle.rng.chance(u8, rate, 256)
|
|
2794
2800
|
else
|
|
@@ -2803,6 +2809,8 @@ pub const Rolls = struct {
|
|
|
2803
2809
|
|
|
2804
2810
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2805
2811
|
val == .true
|
|
2812
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2813
|
+
battle.rng.secondaryChance(player, rate)
|
|
2806
2814
|
else if (showdown)
|
|
2807
2815
|
battle.rng.chance(u8, rate, 256)
|
|
2808
2816
|
else
|
|
@@ -2815,6 +2823,8 @@ pub const Rolls = struct {
|
|
|
2815
2823
|
fn unboost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2816
2824
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2817
2825
|
val == .true
|
|
2826
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2827
|
+
battle.rng.secondaryChance(player, 85)
|
|
2818
2828
|
else if (showdown)
|
|
2819
2829
|
battle.rng.chance(u8, 85, 256)
|
|
2820
2830
|
else
|
|
@@ -2827,6 +2837,8 @@ pub const Rolls = struct {
|
|
|
2827
2837
|
fn metronome(battle: anytype, player: Player, options: anytype) !Move {
|
|
2828
2838
|
const move: Move = if (options.calc.overridden(player, .metronome)) |val|
|
|
2829
2839
|
val
|
|
2840
|
+
else if (@hasDecl(@TypeOf(battle.rng), "metronome"))
|
|
2841
|
+
battle.rng.metronome(player)
|
|
2830
2842
|
else if (showdown)
|
|
2831
2843
|
Move.METRONOME[battle.rng.range(u8, 0, Move.METRONOME.len)]
|
|
2832
2844
|
else move: {
|
|
@@ -2845,6 +2857,8 @@ pub const Rolls = struct {
|
|
|
2845
2857
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
2846
2858
|
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
2847
2859
|
val - 1
|
|
2860
|
+
else if (@hasDecl(@TypeOf(battle.rng), "psywave"))
|
|
2861
|
+
battle.rng.psywave(player)
|
|
2848
2862
|
else if (showdown)
|
|
2849
2863
|
battle.rng.range(u8, 0, max)
|
|
2850
2864
|
else power: {
|
|
@@ -2862,6 +2876,8 @@ pub const Rolls = struct {
|
|
|
2862
2876
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2863
2877
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2864
2878
|
@intCast(val)
|
|
2879
|
+
else if (@hasDecl(@TypeOf(battle.rng), "sleepDuration"))
|
|
2880
|
+
battle.rng.sleepDuration(player)
|
|
2865
2881
|
else if (showdown)
|
|
2866
2882
|
battle.rng.range(u3, 1, 8)
|
|
2867
2883
|
else duration: {
|
|
@@ -2879,6 +2895,8 @@ pub const Rolls = struct {
|
|
|
2879
2895
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
2880
2896
|
const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
|
|
2881
2897
|
val
|
|
2898
|
+
else if (@hasDecl(@TypeOf(battle.rng), "disableDuration"))
|
|
2899
|
+
battle.rng.disableDuration(player)
|
|
2882
2900
|
else if (showdown)
|
|
2883
2901
|
battle.rng.range(u4, 1, 9)
|
|
2884
2902
|
else
|
|
@@ -2892,6 +2910,8 @@ pub const Rolls = struct {
|
|
|
2892
2910
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
2893
2911
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2894
2912
|
@intCast(val)
|
|
2913
|
+
else if (@hasDecl(@TypeOf(battle.rng), "confusionDuration"))
|
|
2914
|
+
battle.rng.confusionDuration(player, self)
|
|
2895
2915
|
else if (showdown)
|
|
2896
2916
|
battle.rng.range(u3, 2, 6)
|
|
2897
2917
|
else
|
|
@@ -2905,6 +2925,8 @@ pub const Rolls = struct {
|
|
|
2905
2925
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2906
2926
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2907
2927
|
@intCast(val)
|
|
2928
|
+
else if (@hasDecl(@TypeOf(battle.rng), "attackingDuration"))
|
|
2929
|
+
battle.rng.attackingDuration(player)
|
|
2908
2930
|
else if (showdown)
|
|
2909
2931
|
battle.rng.range(u3, 2, 4)
|
|
2910
2932
|
else
|
|
@@ -2926,6 +2948,8 @@ pub const Rolls = struct {
|
|
|
2926
2948
|
const roll = if (effect == .Binding) .duration else .multi_hit;
|
|
2927
2949
|
const n: u3 = if (options.calc.overridden(player, roll)) |val|
|
|
2928
2950
|
@intCast(val)
|
|
2951
|
+
else if (@hasDecl(@TypeOf(battle.rng), "distribution"))
|
|
2952
|
+
battle.rng.distribution(player)
|
|
2929
2953
|
else if (showdown)
|
|
2930
2954
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
2931
2955
|
else n: {
|
|
@@ -2958,7 +2982,9 @@ pub const Rolls = struct {
|
|
|
2958
2982
|
else
|
|
2959
2983
|
null;
|
|
2960
2984
|
|
|
2961
|
-
const slot: u4 = overridden orelse
|
|
2985
|
+
const slot: u4 = overridden orelse if (@hasDecl(@TypeOf(battle.rng), "moveSlot"))
|
|
2986
|
+
battle.rng.moveSlot(player, moves, check_pp)
|
|
2987
|
+
else slot: {
|
|
2962
2988
|
if (showdown) {
|
|
2963
2989
|
if (check_pp == 0) {
|
|
2964
2990
|
var i: usize = moves.len;
|
|
@@ -3038,7 +3064,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3038
3064
|
const side = battle.side(player);
|
|
3039
3065
|
const foe = battle.foe(player);
|
|
3040
3066
|
|
|
3041
|
-
|
|
3067
|
+
const active = &side.active;
|
|
3042
3068
|
const stored = side.stored();
|
|
3043
3069
|
|
|
3044
3070
|
// While players are not given any input options on the cartridge in these cases,
|