@pkmn/engine 0.1.0-dev.30c63015 → 0.1.0-dev.32adfe77
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 +32 -9
- 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 +60 -56
- package/package.json +29 -21
- package/src/bin/install-pkmn-engine +14 -14
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/protocol.zig +211 -209
- 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 +21 -21
- 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 +198 -187
- package/src/lib/gen2/chance.zig +27 -27
- package/src/lib/gen2/data.zig +9 -9
- package/src/lib/gen2/generated.zig +1 -3
- package/src/lib/gen2/mechanics.zig +171 -198
- 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
|
@@ -21,13 +21,9 @@ const Player = common.Player;
|
|
|
21
21
|
const Result = common.Result;
|
|
22
22
|
|
|
23
23
|
const Boost = protocol.Boost;
|
|
24
|
-
const Cant = protocol.Cant;
|
|
25
|
-
const CureStatus = protocol.CureStatus;
|
|
26
24
|
const Damage = protocol.Damage;
|
|
27
|
-
const End = protocol.End;
|
|
28
25
|
const Fail = protocol.Fail;
|
|
29
26
|
const Heal = protocol.Heal;
|
|
30
|
-
const Immune = protocol.Immune;
|
|
31
27
|
|
|
32
28
|
const Gen12 = rng.Gen12;
|
|
33
29
|
|
|
@@ -75,8 +71,8 @@ pub fn update(battle: anytype, c1: Choice, c2: Choice, options: anytype) !Result
|
|
|
75
71
|
var p1 = battle.side(.P1);
|
|
76
72
|
var p2 = battle.side(.P2);
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
|
|
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;
|
|
80
76
|
|
|
81
77
|
if (try turnOrder(battle, c1, c2, options) == .P1) {
|
|
82
78
|
if (try doTurn(battle, .P1, c1, r1, s1, .P2, c2, r2, s2, options)) |r| return r;
|
|
@@ -94,11 +90,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
94
90
|
const p1 = battle.side(.P1);
|
|
95
91
|
const p2 = battle.side(.P2);
|
|
96
92
|
|
|
97
|
-
|
|
93
|
+
const p1_slot = findFirstAlive(p1);
|
|
98
94
|
assert(!showdown or p1_slot == 1);
|
|
99
95
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
100
96
|
|
|
101
|
-
|
|
97
|
+
const p2_slot = findFirstAlive(p2);
|
|
102
98
|
assert(!showdown or p2_slot == 1);
|
|
103
99
|
if (p2_slot == 0) return Result.Win;
|
|
104
100
|
|
|
@@ -267,7 +263,7 @@ fn switchIn(battle: anytype, player: Player, slot: u8, initial: bool, options: a
|
|
|
267
263
|
incoming.status = Status.init(.PSN);
|
|
268
264
|
// Technically, Pokémon Showdown adds these after *both* Pokémon have switched, but we'd
|
|
269
265
|
// rather not clutter up turnOrder just for this (incorrect) log message
|
|
270
|
-
try options.log.status(.{ battle.active(player), incoming.status,
|
|
266
|
+
try options.log.status(.{ battle.active(player), incoming.status, .Silent });
|
|
271
267
|
}
|
|
272
268
|
}
|
|
273
269
|
|
|
@@ -411,7 +407,7 @@ fn executeMove(
|
|
|
411
407
|
if (side.last_selected_move == .SKIP_TURN) {
|
|
412
408
|
assert(!showdown);
|
|
413
409
|
if (battle.foe(player).active.volatiles.Binding) {
|
|
414
|
-
try options.log.cant(.{ battle.active(player),
|
|
410
|
+
try options.log.cant(.{ battle.active(player), .Bound });
|
|
415
411
|
}
|
|
416
412
|
return null;
|
|
417
413
|
}
|
|
@@ -501,23 +497,23 @@ fn beforeMove(
|
|
|
501
497
|
}
|
|
502
498
|
|
|
503
499
|
if (duration == 0) {
|
|
504
|
-
try log.curestatus(.{ ident, before,
|
|
500
|
+
try log.curestatus(.{ ident, before, .Message });
|
|
505
501
|
stored.status = 0; // clears EXT if present
|
|
506
502
|
} else {
|
|
507
|
-
try log.cant(.{ ident,
|
|
503
|
+
try log.cant(.{ ident, .Sleep });
|
|
508
504
|
}
|
|
509
505
|
side.last_used_move = .None;
|
|
510
506
|
return .done;
|
|
511
507
|
}
|
|
512
508
|
|
|
513
509
|
if (Status.is(stored.status, .FRZ)) {
|
|
514
|
-
try log.cant(.{ ident,
|
|
510
|
+
try log.cant(.{ ident, .Freeze });
|
|
515
511
|
side.last_used_move = .None;
|
|
516
512
|
return .done;
|
|
517
513
|
}
|
|
518
514
|
|
|
519
515
|
if (skip or foe.active.volatiles.Binding) {
|
|
520
|
-
try log.cant(.{ ident,
|
|
516
|
+
try log.cant(.{ ident, .Bound });
|
|
521
517
|
return .done;
|
|
522
518
|
}
|
|
523
519
|
|
|
@@ -525,13 +521,13 @@ fn beforeMove(
|
|
|
525
521
|
// Pokémon Showdown doesn't clear Flinch until its imaginary "residual" phase, meaning
|
|
526
522
|
// Pokémon can sometimes flinch multiple times from the same original hit
|
|
527
523
|
if (!showdown) volatiles.Flinch = false;
|
|
528
|
-
try log.cant(.{ ident,
|
|
524
|
+
try log.cant(.{ ident, .Flinch });
|
|
529
525
|
return .done;
|
|
530
526
|
}
|
|
531
527
|
|
|
532
528
|
if (volatiles.Recharging) {
|
|
533
529
|
volatiles.Recharging = false;
|
|
534
|
-
try log.cant(.{ ident,
|
|
530
|
+
try log.cant(.{ ident, .Recharge });
|
|
535
531
|
return .done;
|
|
536
532
|
}
|
|
537
533
|
|
|
@@ -545,7 +541,7 @@ fn beforeMove(
|
|
|
545
541
|
|
|
546
542
|
if (volatiles.disable_duration == 0) {
|
|
547
543
|
volatiles.disable_move = 0;
|
|
548
|
-
try log.end(.{ ident,
|
|
544
|
+
try log.end(.{ ident, .Disable });
|
|
549
545
|
}
|
|
550
546
|
}
|
|
551
547
|
// Pokémon Showdown's disable condition has a single onBeforeMove handler
|
|
@@ -570,9 +566,9 @@ fn beforeMove(
|
|
|
570
566
|
|
|
571
567
|
if (volatiles.confusion == 0) {
|
|
572
568
|
volatiles.Confusion = false;
|
|
573
|
-
try log.end(.{ ident,
|
|
569
|
+
try log.end(.{ ident, .Confusion });
|
|
574
570
|
} else {
|
|
575
|
-
try log.activate(ident, .Confusion);
|
|
571
|
+
try log.activate(.{ ident, .Confusion });
|
|
576
572
|
|
|
577
573
|
if (try Rolls.confused(battle, player, options)) {
|
|
578
574
|
assert(!volatiles.MultiHit);
|
|
@@ -623,7 +619,7 @@ fn beforeMove(
|
|
|
623
619
|
volatiles.Binding = false;
|
|
624
620
|
options.chance.clearDurations(player, null);
|
|
625
621
|
// GLITCH: Invulnerable is not cleared, resulting in permanent Fly/Dig invulnerability
|
|
626
|
-
try log.cant(.{ ident,
|
|
622
|
+
try log.cant(.{ ident, .Paralysis });
|
|
627
623
|
return .done;
|
|
628
624
|
}
|
|
629
625
|
|
|
@@ -639,8 +635,6 @@ fn beforeMove(
|
|
|
639
635
|
volatiles.state +%= battle.last_damage;
|
|
640
636
|
}
|
|
641
637
|
|
|
642
|
-
assert(volatiles.attacks > 0);
|
|
643
|
-
|
|
644
638
|
if (options.calc.modify(player, .attacking)) |extend| {
|
|
645
639
|
if (!extend) volatiles.attacks = 0;
|
|
646
640
|
} else {
|
|
@@ -649,18 +643,18 @@ fn beforeMove(
|
|
|
649
643
|
try options.chance.attacking(player, volatiles.attacks);
|
|
650
644
|
|
|
651
645
|
if (volatiles.attacks != 0) {
|
|
652
|
-
try log.activate(ident, .Bide);
|
|
646
|
+
try log.activate(.{ ident, .Bide });
|
|
653
647
|
return .done;
|
|
654
648
|
}
|
|
655
649
|
|
|
656
650
|
volatiles.Bide = false;
|
|
657
|
-
try log.end(.{ ident,
|
|
651
|
+
try log.end(.{ ident, .Bide });
|
|
658
652
|
|
|
659
653
|
battle.last_damage = volatiles.state *% 2;
|
|
660
654
|
volatiles.state = 0;
|
|
661
655
|
|
|
662
656
|
if (battle.last_damage == 0) {
|
|
663
|
-
try log.fail(.{ ident,
|
|
657
|
+
try log.fail(.{ ident, .None });
|
|
664
658
|
return .done;
|
|
665
659
|
}
|
|
666
660
|
|
|
@@ -680,8 +674,6 @@ fn beforeMove(
|
|
|
680
674
|
}
|
|
681
675
|
|
|
682
676
|
if (volatiles.Thrashing) {
|
|
683
|
-
assert(volatiles.attacks > 0);
|
|
684
|
-
|
|
685
677
|
if (options.calc.modify(player, .attacking)) |extend| {
|
|
686
678
|
if (!extend) volatiles.attacks = 0;
|
|
687
679
|
} else {
|
|
@@ -690,7 +682,7 @@ fn beforeMove(
|
|
|
690
682
|
try options.chance.attacking(player, volatiles.attacks);
|
|
691
683
|
|
|
692
684
|
if (!showdown and handleThrashing(battle, active, player, options)) {
|
|
693
|
-
try log.start(battle.active(player), .ConfusionSilent);
|
|
685
|
+
try log.start(.{ battle.active(player), .ConfusionSilent });
|
|
694
686
|
}
|
|
695
687
|
try log.move(.{ ident, side.last_selected_move, battle.active(player.foe()) });
|
|
696
688
|
if (showdown) {
|
|
@@ -698,15 +690,13 @@ fn beforeMove(
|
|
|
698
690
|
// used in useMove and doesn't have the notion of skipping canMove semantics
|
|
699
691
|
side.last_used_move = side.last_selected_move;
|
|
700
692
|
if (handleThrashing(battle, active, player, options)) {
|
|
701
|
-
try log.start(battle.active(player), .ConfusionSilent);
|
|
693
|
+
try log.start(.{ battle.active(player), .ConfusionSilent });
|
|
702
694
|
}
|
|
703
695
|
}
|
|
704
696
|
return .skip_can;
|
|
705
697
|
}
|
|
706
698
|
|
|
707
699
|
if (volatiles.Binding) {
|
|
708
|
-
assert(volatiles.attacks > 0);
|
|
709
|
-
|
|
710
700
|
if (options.calc.modify(player, .binding)) |extend| {
|
|
711
701
|
if (!extend) volatiles.attacks = 0;
|
|
712
702
|
} else {
|
|
@@ -742,7 +732,6 @@ fn canMove(
|
|
|
742
732
|
const player_ident = battle.active(player);
|
|
743
733
|
const move = Move.get(side.last_selected_move);
|
|
744
734
|
|
|
745
|
-
var skip = skip_pp;
|
|
746
735
|
if (side.active.volatiles.Charging) {
|
|
747
736
|
side.active.volatiles.Charging = false;
|
|
748
737
|
side.active.volatiles.Invulnerable = false;
|
|
@@ -754,7 +743,7 @@ fn canMove(
|
|
|
754
743
|
}
|
|
755
744
|
|
|
756
745
|
side.last_used_move = side.last_selected_move;
|
|
757
|
-
if (!
|
|
746
|
+
if (!skip_pp) decrementPP(side, mslot, auto);
|
|
758
747
|
|
|
759
748
|
const target = if (move.target == .Self) player else player.foe();
|
|
760
749
|
try options.log.move(.{ player_ident, side.last_selected_move, battle.active(target), from });
|
|
@@ -906,7 +895,7 @@ fn doMove(
|
|
|
906
895
|
}
|
|
907
896
|
}
|
|
908
897
|
|
|
909
|
-
|
|
898
|
+
const zero = battle.last_damage == 0;
|
|
910
899
|
miss = if (showdown or skip)
|
|
911
900
|
miss
|
|
912
901
|
else
|
|
@@ -934,12 +923,12 @@ fn doMove(
|
|
|
934
923
|
ohko = (!showdown or (!immune and !invulnerable)) and
|
|
935
924
|
move.effect == .OHKO and side.active.stats.spe < foe.active.stats.spe;
|
|
936
925
|
if (ohko) {
|
|
937
|
-
try log.immune(.{ foe_ident,
|
|
926
|
+
try log.immune(.{ foe_ident, .OHKO });
|
|
938
927
|
} else if (immune and !invulnerable and (!showdown or move.effect != .Binding)) {
|
|
939
|
-
try log.immune(.{ foe_ident,
|
|
928
|
+
try log.immune(.{ foe_ident, .None });
|
|
940
929
|
} else if (mist) {
|
|
941
|
-
if (!foe.active.volatiles.Substitute) try log.activate(foe_ident, .Mist);
|
|
942
|
-
try log.fail(.{ foe_ident,
|
|
930
|
+
if (!foe.active.volatiles.Substitute) try log.activate(.{ foe_ident, .Mist });
|
|
931
|
+
try log.fail(.{ foe_ident, .None });
|
|
943
932
|
} else {
|
|
944
933
|
if (showdown or !zero) try options.chance.commit(player, .miss);
|
|
945
934
|
try log.lastmiss(.{});
|
|
@@ -1014,9 +1003,9 @@ fn doMove(
|
|
|
1014
1003
|
assert(foe.stored().hp > 0);
|
|
1015
1004
|
// Pokémon Showdown logs |-damage| here instead of |-immune| because logic...
|
|
1016
1005
|
if (sub) {
|
|
1017
|
-
try log.activate(battle.active(player.foe()), .Substitute);
|
|
1006
|
+
try log.activate(.{ battle.active(player.foe()), .Substitute });
|
|
1018
1007
|
} else {
|
|
1019
|
-
try log.damage(.{ battle.active(player.foe()), foe.stored(),
|
|
1008
|
+
try log.damage(.{ battle.active(player.foe()), foe.stored(), .None });
|
|
1020
1009
|
try buildRage(battle, player.foe(), options);
|
|
1021
1010
|
}
|
|
1022
1011
|
return null;
|
|
@@ -1064,7 +1053,7 @@ fn checkCriticalHit(battle: anytype, player: Player, move: Move.Data, options: a
|
|
|
1064
1053
|
const side = battle.side(player);
|
|
1065
1054
|
|
|
1066
1055
|
// Base speed is used for the critical hit calculation, even when Transform-ed
|
|
1067
|
-
var rate =
|
|
1056
|
+
var rate: u16 = Species.chance(side.stored().species);
|
|
1068
1057
|
// GLITCH: Focus Energy reduces critical hit rate instead of increasing it
|
|
1069
1058
|
rate = if (side.active.volatiles.FocusEnergy) rate / 2 else @min(rate * 2, 255);
|
|
1070
1059
|
rate = if (move.effect == .HighCritical) @min(rate * 4, 255) else rate / 2;
|
|
@@ -1121,9 +1110,9 @@ fn calcDamage(
|
|
|
1121
1110
|
def = @max((def / 4) & 255, if (showdown) 1 else 0);
|
|
1122
1111
|
}
|
|
1123
1112
|
|
|
1124
|
-
const lvl =
|
|
1113
|
+
const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
|
|
1125
1114
|
|
|
1126
|
-
def =
|
|
1115
|
+
def = if (move.effect == .Explode) @max(def / 2, 1) else def;
|
|
1127
1116
|
|
|
1128
1117
|
if (def == 0) return false;
|
|
1129
1118
|
|
|
@@ -1214,7 +1203,7 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1214
1203
|
const foe = battle.foe(player);
|
|
1215
1204
|
|
|
1216
1205
|
if (battle.last_damage == 0) {
|
|
1217
|
-
try options.log.fail(.{ battle.active(player),
|
|
1206
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
1218
1207
|
return null;
|
|
1219
1208
|
}
|
|
1220
1209
|
|
|
@@ -1232,14 +1221,14 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1232
1221
|
foe_last_selected_move.type == .Fighting);
|
|
1233
1222
|
|
|
1234
1223
|
if (!used and !selected) {
|
|
1235
|
-
try options.log.fail(.{ battle.active(player),
|
|
1224
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
1236
1225
|
return null;
|
|
1237
1226
|
}
|
|
1238
1227
|
|
|
1239
1228
|
if (!used or !selected) {
|
|
1240
1229
|
// GLITCH: Counter desync (covered by Desync Clause Mod on Pokémon Showdown)
|
|
1241
1230
|
if (!showdown) return Result.Error;
|
|
1242
|
-
try options.log.fail(.{ battle.active(player),
|
|
1231
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
1243
1232
|
return null;
|
|
1244
1233
|
}
|
|
1245
1234
|
|
|
@@ -1275,13 +1264,13 @@ fn applyDamage(
|
|
|
1275
1264
|
subbed.active.volatiles.substitute = 0;
|
|
1276
1265
|
subbed.active.volatiles.Substitute = false;
|
|
1277
1266
|
// battle.last_damage is not updated with the amount of HP the Substitute had
|
|
1278
|
-
try options.log.end(.{ battle.active(sub_player),
|
|
1267
|
+
try options.log.end(.{ battle.active(sub_player), .Substitute });
|
|
1279
1268
|
options.calc.capped(target_player);
|
|
1280
1269
|
return true;
|
|
1281
1270
|
} else {
|
|
1282
1271
|
// Safe to truncate since less than subbed.volatiles.substitute which is a u8
|
|
1283
1272
|
subbed.active.volatiles.substitute -= @intCast(battle.last_damage);
|
|
1284
|
-
try options.log.activate(battle.active(sub_player), .Substitute);
|
|
1273
|
+
try options.log.activate(.{ battle.active(sub_player), .Substitute });
|
|
1285
1274
|
return false;
|
|
1286
1275
|
}
|
|
1287
1276
|
}
|
|
@@ -1307,7 +1296,7 @@ fn mirrorMove(
|
|
|
1307
1296
|
side.last_selected_move = foe.last_used_move;
|
|
1308
1297
|
|
|
1309
1298
|
if (foe.last_used_move == .None or foe.last_used_move == .MirrorMove) {
|
|
1310
|
-
try options.log.fail(.{ battle.active(player),
|
|
1299
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
1311
1300
|
return null;
|
|
1312
1301
|
} else if (!showdown or foe.last_used_move != .Struggle) {
|
|
1313
1302
|
incrementPP(side, mslot);
|
|
@@ -1349,8 +1338,8 @@ fn checkHit(battle: anytype, player: Player, move: Move.Data, options: anytype)
|
|
|
1349
1338
|
if (mist) {
|
|
1350
1339
|
assert(!showdown);
|
|
1351
1340
|
const foe_ident = battle.active(player.foe());
|
|
1352
|
-
try options.log.activate(foe_ident, .Mist);
|
|
1353
|
-
try options.log.fail(.{ foe_ident,
|
|
1341
|
+
try options.log.activate(.{ foe_ident, .Mist });
|
|
1342
|
+
try options.log.fail(.{ foe_ident, .None });
|
|
1354
1343
|
} else {
|
|
1355
1344
|
try options.chance.commit(player, .miss);
|
|
1356
1345
|
try options.log.lastmiss(.{});
|
|
@@ -1406,7 +1395,7 @@ fn moveHit(
|
|
|
1406
1395
|
const overwritten = overwrite and state > 0;
|
|
1407
1396
|
assert(!overwritten or (0 < state and state <= 255 and !side.active.volatiles.Bide));
|
|
1408
1397
|
|
|
1409
|
-
var accuracy = if (overwritten) state else
|
|
1398
|
+
var accuracy: u16 = if (overwritten) state else move.accuracy;
|
|
1410
1399
|
var boost = BOOSTS[@as(u4, @intCast(@as(i8, side.active.boosts.accuracy) + 6))];
|
|
1411
1400
|
accuracy = accuracy * boost[0] / boost[1];
|
|
1412
1401
|
boost = BOOSTS[@as(u4, @intCast(@as(i8, -foe.active.boosts.evasion) + 6))];
|
|
@@ -1552,13 +1541,13 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !void {
|
|
|
1552
1541
|
stored.hp -= amount;
|
|
1553
1542
|
|
|
1554
1543
|
// As above, Pokémon Showdown uses damage [of] but its not relevant
|
|
1555
|
-
if (amount > 0) try options.log.damage(.{ ident, stored,
|
|
1544
|
+
if (amount > 0) try options.log.damage(.{ ident, stored, .LeechSeed });
|
|
1556
1545
|
|
|
1557
1546
|
const before = foe_stored.hp;
|
|
1558
1547
|
// Uncapped damage is added back to the foe
|
|
1559
1548
|
foe_stored.hp = @min(foe_stored.hp + damage, foe_stored.stats.hp);
|
|
1560
1549
|
// Pokémon Showdown uses the less specific heal here instead of drain... because reasons?
|
|
1561
|
-
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 });
|
|
1562
1551
|
}
|
|
1563
1552
|
}
|
|
1564
1553
|
|
|
@@ -1650,7 +1639,7 @@ fn disabled(side: *Side, ident: ID, options: anytype) !bool {
|
|
|
1650
1639
|
side.last_selected_move;
|
|
1651
1640
|
if (m != .None and m == last) {
|
|
1652
1641
|
side.active.volatiles.Charging = false;
|
|
1653
|
-
try options.log.cant(.{ ident,
|
|
1642
|
+
try options.log.cant(.{ ident, .Disable, last });
|
|
1654
1643
|
return true;
|
|
1655
1644
|
}
|
|
1656
1645
|
}
|
|
@@ -1683,7 +1672,6 @@ fn onBegin(
|
|
|
1683
1672
|
residual: *bool,
|
|
1684
1673
|
options: anytype,
|
|
1685
1674
|
) !void {
|
|
1686
|
-
assert(move.effect.onBegin());
|
|
1687
1675
|
return switch (move.effect) {
|
|
1688
1676
|
.Confusion => Effects.confusion(battle, player, move, options),
|
|
1689
1677
|
.Conversion => Effects.conversion(battle, player, options),
|
|
@@ -1706,7 +1694,6 @@ fn onBegin(
|
|
|
1706
1694
|
}
|
|
1707
1695
|
|
|
1708
1696
|
fn onEnd(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1709
|
-
assert(move.effect.onEnd());
|
|
1710
1697
|
return switch (move.effect) {
|
|
1711
1698
|
.Bide => Effects.bide(battle, player, options),
|
|
1712
1699
|
// zig fmt: off
|
|
@@ -1728,7 +1715,6 @@ fn alwaysHappens(
|
|
|
1728
1715
|
residual: *bool,
|
|
1729
1716
|
options: anytype,
|
|
1730
1717
|
) !void {
|
|
1731
|
-
assert(move.effect.alwaysHappens());
|
|
1732
1718
|
return switch (move.effect) {
|
|
1733
1719
|
.DrainHP, .DreamEater => Effects.drainHP(battle, player, options),
|
|
1734
1720
|
.Explode => Effects.explode(battle, player),
|
|
@@ -1767,7 +1753,7 @@ pub const Effects = struct {
|
|
|
1767
1753
|
side.active.volatiles.state = 0;
|
|
1768
1754
|
side.active.volatiles.attacks = Rolls.attackingDuration(battle, player, options);
|
|
1769
1755
|
|
|
1770
|
-
try options.log.start(battle.active(player), .Bide);
|
|
1756
|
+
try options.log.start(.{ battle.active(player), .Bide });
|
|
1771
1757
|
}
|
|
1772
1758
|
|
|
1773
1759
|
fn burnChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -1784,7 +1770,7 @@ pub const Effects = struct {
|
|
|
1784
1770
|
try options.log.curestatus(.{
|
|
1785
1771
|
battle.active(player.foe()),
|
|
1786
1772
|
foe_stored.status,
|
|
1787
|
-
|
|
1773
|
+
.Message,
|
|
1788
1774
|
});
|
|
1789
1775
|
foe_stored.status = 0;
|
|
1790
1776
|
}
|
|
@@ -1799,8 +1785,7 @@ pub const Effects = struct {
|
|
|
1799
1785
|
foe_stored.status = Status.init(.BRN);
|
|
1800
1786
|
foe.active.stats.atk = @max(foe.active.stats.atk / 2, 1);
|
|
1801
1787
|
|
|
1802
|
-
|
|
1803
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
|
|
1788
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
1804
1789
|
}
|
|
1805
1790
|
|
|
1806
1791
|
fn charge(battle: anytype, player: Player, options: anytype) !void {
|
|
@@ -1822,17 +1807,11 @@ pub const Effects = struct {
|
|
|
1822
1807
|
if (!try Rolls.confusionChance(battle, player, options)) return;
|
|
1823
1808
|
} else {
|
|
1824
1809
|
if (showdown) {
|
|
1825
|
-
if (!try checkHit(battle, player, move, options))
|
|
1826
|
-
|
|
1827
|
-
} else if (sub) {
|
|
1828
|
-
return options.log.fail(.{ battle.active(player.foe()), Fail.None });
|
|
1829
|
-
}
|
|
1810
|
+
if (!try checkHit(battle, player, move, options)) return;
|
|
1811
|
+
if (sub) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1830
1812
|
} else {
|
|
1831
|
-
if (sub) {
|
|
1832
|
-
|
|
1833
|
-
} else if (!try checkHit(battle, player, move, options)) {
|
|
1834
|
-
return;
|
|
1835
|
-
}
|
|
1813
|
+
if (sub) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1814
|
+
if (!try checkHit(battle, player, move, options)) return;
|
|
1836
1815
|
}
|
|
1837
1816
|
try options.chance.commit(player, .hit);
|
|
1838
1817
|
}
|
|
@@ -1841,7 +1820,7 @@ pub const Effects = struct {
|
|
|
1841
1820
|
foe.active.volatiles.Confusion = true;
|
|
1842
1821
|
foe.active.volatiles.confusion = Rolls.confusionDuration(battle, player, false, options);
|
|
1843
1822
|
|
|
1844
|
-
try options.log.start(battle.active(player.foe()), .Confusion);
|
|
1823
|
+
try options.log.start(.{ battle.active(player.foe()), .Confusion });
|
|
1845
1824
|
}
|
|
1846
1825
|
|
|
1847
1826
|
fn conversion(battle: anytype, player: Player, options: anytype) !void {
|
|
@@ -1853,11 +1832,12 @@ pub const Effects = struct {
|
|
|
1853
1832
|
}
|
|
1854
1833
|
|
|
1855
1834
|
battle.side(player).active.types = foe.active.types;
|
|
1856
|
-
return options.log.
|
|
1835
|
+
return options.log.start(.{
|
|
1857
1836
|
battle.active(player),
|
|
1837
|
+
.TypeChange,
|
|
1858
1838
|
foe.active.types,
|
|
1859
1839
|
battle.active(player.foe()),
|
|
1860
|
-
);
|
|
1840
|
+
});
|
|
1861
1841
|
}
|
|
1862
1842
|
|
|
1863
1843
|
fn disable(battle: anytype, player: Player, move: Move.Data, options: anytype) !?Result {
|
|
@@ -1872,7 +1852,7 @@ pub const Effects = struct {
|
|
|
1872
1852
|
}
|
|
1873
1853
|
|
|
1874
1854
|
if (volatiles.disable_move != 0) {
|
|
1875
|
-
try options.log.fail(.{ foe_ident,
|
|
1855
|
+
try options.log.fail(.{ foe_ident, .None });
|
|
1876
1856
|
return null;
|
|
1877
1857
|
}
|
|
1878
1858
|
|
|
@@ -1888,7 +1868,7 @@ pub const Effects = struct {
|
|
|
1888
1868
|
// Technically this is still considered simply a "miss" on the cartridge,
|
|
1889
1869
|
// but diverging from Pokémon Showdown here would mostly just be pedantic
|
|
1890
1870
|
if (n == 0) {
|
|
1891
|
-
try options.log.fail(.{ foe_ident,
|
|
1871
|
+
try options.log.fail(.{ foe_ident, .None });
|
|
1892
1872
|
return null;
|
|
1893
1873
|
} else if (err) {
|
|
1894
1874
|
// GLITCH: Transform + Mirror Move / Metronome PP softlock
|
|
@@ -1901,7 +1881,7 @@ pub const Effects = struct {
|
|
|
1901
1881
|
volatiles.disable_duration = Rolls.disableDuration(battle, player, options);
|
|
1902
1882
|
|
|
1903
1883
|
const id = foe.active.move(volatiles.disable_move).id;
|
|
1904
|
-
try options.log.
|
|
1884
|
+
try options.log.start(.{ foe_ident, .Disable, id });
|
|
1905
1885
|
return null;
|
|
1906
1886
|
}
|
|
1907
1887
|
|
|
@@ -1920,7 +1900,7 @@ pub const Effects = struct {
|
|
|
1920
1900
|
if (stored.hp == stored.stats.hp) return;
|
|
1921
1901
|
stored.hp = @min(stored.stats.hp, stored.hp + drain);
|
|
1922
1902
|
|
|
1923
|
-
try options.log.heal(.{ ident, stored,
|
|
1903
|
+
try options.log.heal(.{ ident, stored, .Drain, battle.active(player.foe()) });
|
|
1924
1904
|
}
|
|
1925
1905
|
|
|
1926
1906
|
fn explode(battle: anytype, player: Player) !void {
|
|
@@ -1952,7 +1932,7 @@ pub const Effects = struct {
|
|
|
1952
1932
|
if (side.active.volatiles.FocusEnergy) return;
|
|
1953
1933
|
side.active.volatiles.FocusEnergy = true;
|
|
1954
1934
|
|
|
1955
|
-
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1935
|
+
try options.log.start(.{ battle.active(player), .FocusEnergy });
|
|
1956
1936
|
}
|
|
1957
1937
|
|
|
1958
1938
|
fn freezeChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -1974,7 +1954,7 @@ pub const Effects = struct {
|
|
|
1974
1954
|
foe_stored.status = Status.init(.FRZ);
|
|
1975
1955
|
// GLITCH: Hyper Beam recharging status is not cleared
|
|
1976
1956
|
|
|
1977
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
1957
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
1978
1958
|
}
|
|
1979
1959
|
|
|
1980
1960
|
fn haze(battle: anytype, player: Player, options: anytype) !void {
|
|
@@ -1982,7 +1962,7 @@ pub const Effects = struct {
|
|
|
1982
1962
|
var side = battle.side(player);
|
|
1983
1963
|
var foe = battle.foe(player);
|
|
1984
1964
|
|
|
1985
|
-
|
|
1965
|
+
const side_stored = side.stored();
|
|
1986
1966
|
var foe_stored = foe.stored();
|
|
1987
1967
|
|
|
1988
1968
|
const player_ident = battle.active(player);
|
|
@@ -1994,7 +1974,7 @@ pub const Effects = struct {
|
|
|
1994
1974
|
side.active.stats = side_stored.stats;
|
|
1995
1975
|
foe.active.stats = foe_stored.stats;
|
|
1996
1976
|
|
|
1997
|
-
try log.activate(player_ident, .Haze);
|
|
1977
|
+
try log.activate(.{ player_ident, .Haze });
|
|
1998
1978
|
try log.clearallboost(.{});
|
|
1999
1979
|
|
|
2000
1980
|
// Pokémon Showdown clears P1 then P2 instead of status -> side -> foe
|
|
@@ -2003,11 +1983,11 @@ pub const Effects = struct {
|
|
|
2003
1983
|
const p: Player = @enumFromInt(i);
|
|
2004
1984
|
// Pokémon Showdown incorrectly does not prevent sleep/freeze from moving
|
|
2005
1985
|
if (p != player and Status.any(s.stored().status)) {
|
|
2006
|
-
try log.curestatus(.{ foe_ident, foe_stored.status,
|
|
1986
|
+
try log.curestatus(.{ foe_ident, foe_stored.status, .Silent });
|
|
2007
1987
|
s.stored().status = 0;
|
|
2008
1988
|
} else if (showdown and s.stored().status == Status.TOX) {
|
|
2009
1989
|
s.stored().status = Status.init(.PSN);
|
|
2010
|
-
try log.status(.{ battle.active(p), s.stored().status,
|
|
1990
|
+
try log.status(.{ battle.active(p), s.stored().status, .None });
|
|
2011
1991
|
}
|
|
2012
1992
|
try clearVolatiles(battle, p, p != player, options);
|
|
2013
1993
|
}
|
|
@@ -2016,7 +1996,7 @@ pub const Effects = struct {
|
|
|
2016
1996
|
if (Status.is(foe_stored.status, .FRZ) or Status.is(foe_stored.status, .SLP)) {
|
|
2017
1997
|
foe.last_selected_move = .SKIP_TURN;
|
|
2018
1998
|
}
|
|
2019
|
-
try log.curestatus(.{ foe_ident, foe_stored.status,
|
|
1999
|
+
try log.curestatus(.{ foe_ident, foe_stored.status, .Silent });
|
|
2020
2000
|
foe_stored.status = 0;
|
|
2021
2001
|
}
|
|
2022
2002
|
try clearVolatiles(battle, player, false, options);
|
|
@@ -2032,7 +2012,7 @@ pub const Effects = struct {
|
|
|
2032
2012
|
// GLITCH: HP recovery move failure glitches
|
|
2033
2013
|
const delta = stored.stats.hp - stored.hp;
|
|
2034
2014
|
if (delta == 0 or (delta & 255 == 255 and stored.hp % 256 != 0)) {
|
|
2035
|
-
return options.log.fail(.{ ident,
|
|
2015
|
+
return options.log.fail(.{ ident, .None });
|
|
2036
2016
|
}
|
|
2037
2017
|
|
|
2038
2018
|
const rest = side.last_selected_move == .Rest;
|
|
@@ -2040,7 +2020,7 @@ pub const Effects = struct {
|
|
|
2040
2020
|
// Adding the sleep status runs the sleep condition handler to roll duration
|
|
2041
2021
|
if (showdown) battle.rng.advance(1);
|
|
2042
2022
|
stored.status = Status.slf(2);
|
|
2043
|
-
try options.log.status(.{ ident, stored.status,
|
|
2023
|
+
try options.log.status(.{ ident, stored.status, .From, Move.Rest });
|
|
2044
2024
|
stored.hp = stored.stats.hp;
|
|
2045
2025
|
} else {
|
|
2046
2026
|
stored.hp = @min(stored.stats.hp, stored.hp + (stored.stats.hp / 2));
|
|
@@ -2059,7 +2039,7 @@ pub const Effects = struct {
|
|
|
2059
2039
|
if (showdown) {
|
|
2060
2040
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
2061
2041
|
if (!foe.active.volatiles.Invulnerable and foe.active.types.includes(.Grass)) {
|
|
2062
|
-
return options.log.immune(.{ battle.active(player.foe()),
|
|
2042
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
2063
2043
|
}
|
|
2064
2044
|
if (!try checkHit(battle, player, move, options)) return;
|
|
2065
2045
|
if (foe.active.volatiles.LeechSeed) return;
|
|
@@ -2074,18 +2054,17 @@ pub const Effects = struct {
|
|
|
2074
2054
|
try options.chance.commit(player, .hit);
|
|
2075
2055
|
foe.active.volatiles.LeechSeed = true;
|
|
2076
2056
|
|
|
2077
|
-
try options.log.start(battle.active(player.foe()), .LeechSeed);
|
|
2057
|
+
try options.log.start(.{ battle.active(player.foe()), .LeechSeed });
|
|
2078
2058
|
}
|
|
2079
2059
|
|
|
2080
2060
|
fn lightScreen(battle: anytype, player: Player, options: anytype) !void {
|
|
2081
2061
|
var side = battle.side(player);
|
|
2062
|
+
const ident = battle.active(player);
|
|
2082
2063
|
|
|
2083
|
-
if (side.active.volatiles.LightScreen) {
|
|
2084
|
-
return options.log.fail(.{ battle.active(player), Fail.None });
|
|
2085
|
-
}
|
|
2064
|
+
if (side.active.volatiles.LightScreen) return options.log.fail(.{ ident, .None });
|
|
2086
2065
|
side.active.volatiles.LightScreen = true;
|
|
2087
2066
|
|
|
2088
|
-
try options.log.start(
|
|
2067
|
+
try options.log.start(.{ ident, .LightScreen });
|
|
2089
2068
|
}
|
|
2090
2069
|
|
|
2091
2070
|
fn mimic(battle: anytype, player: Player, move: Move.Data, mslot: u8, options: anytype) !void {
|
|
@@ -2114,7 +2093,7 @@ pub const Effects = struct {
|
|
|
2114
2093
|
// Invulnerable foes or 1/256 miss can trigger |-miss| instead of |-fail|
|
|
2115
2094
|
if (!try checkHit(battle, player, move, options)) return;
|
|
2116
2095
|
try options.chance.commit(player, .hit);
|
|
2117
|
-
return options.log.fail(.{ battle.active(player.foe()),
|
|
2096
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2118
2097
|
}
|
|
2119
2098
|
}
|
|
2120
2099
|
if (!try checkHit(battle, player, move, options)) return;
|
|
@@ -2123,7 +2102,7 @@ pub const Effects = struct {
|
|
|
2123
2102
|
const rslot = try Rolls.moveSlot(battle, player, &foe.active.moves, 0, options);
|
|
2124
2103
|
side.active.move(oslot).id = foe.active.move(rslot).id;
|
|
2125
2104
|
|
|
2126
|
-
try options.log.
|
|
2105
|
+
try options.log.start(.{ battle.active(player), .Mimic, side.active.move(oslot).id });
|
|
2127
2106
|
}
|
|
2128
2107
|
|
|
2129
2108
|
fn mist(battle: anytype, player: Player, options: anytype) !void {
|
|
@@ -2132,7 +2111,7 @@ pub const Effects = struct {
|
|
|
2132
2111
|
if (side.active.volatiles.Mist) return;
|
|
2133
2112
|
side.active.volatiles.Mist = true;
|
|
2134
2113
|
|
|
2135
|
-
try options.log.start(battle.active(player), .Mist);
|
|
2114
|
+
try options.log.start(.{ battle.active(player), .Mist });
|
|
2136
2115
|
}
|
|
2137
2116
|
|
|
2138
2117
|
fn multiHit(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -2159,7 +2138,7 @@ pub const Effects = struct {
|
|
|
2159
2138
|
if (showdown) {
|
|
2160
2139
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
2161
2140
|
if (immune and !foe.active.volatiles.Invulnerable) {
|
|
2162
|
-
return log.immune(.{ foe_ident,
|
|
2141
|
+
return log.immune(.{ foe_ident, .None });
|
|
2163
2142
|
} else if (!try checkHit(battle, player, move, options)) return;
|
|
2164
2143
|
}
|
|
2165
2144
|
if (Status.any(foe_stored.status)) {
|
|
@@ -2169,7 +2148,7 @@ pub const Effects = struct {
|
|
|
2169
2148
|
});
|
|
2170
2149
|
}
|
|
2171
2150
|
if (!showdown) {
|
|
2172
|
-
if (immune) return log.immune(.{ foe_ident,
|
|
2151
|
+
if (immune) return log.immune(.{ foe_ident, .None });
|
|
2173
2152
|
if (!try checkHit(battle, player, move, options)) return;
|
|
2174
2153
|
}
|
|
2175
2154
|
try options.chance.commit(player, .hit);
|
|
@@ -2177,7 +2156,7 @@ pub const Effects = struct {
|
|
|
2177
2156
|
foe_stored.status = Status.init(.PAR);
|
|
2178
2157
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2179
2158
|
|
|
2180
|
-
try log.status(.{ foe_ident, foe_stored.status,
|
|
2159
|
+
try log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2181
2160
|
}
|
|
2182
2161
|
|
|
2183
2162
|
fn paralyzeChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -2199,8 +2178,7 @@ pub const Effects = struct {
|
|
|
2199
2178
|
foe_stored.status = Status.init(.PAR);
|
|
2200
2179
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2201
2180
|
|
|
2202
|
-
|
|
2203
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
|
|
2181
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2204
2182
|
}
|
|
2205
2183
|
|
|
2206
2184
|
fn payDay(battle: anytype, player: Player, options: anytype) !void {
|
|
@@ -2220,7 +2198,7 @@ pub const Effects = struct {
|
|
|
2220
2198
|
return;
|
|
2221
2199
|
} else if (foe.active.volatiles.Substitute) {
|
|
2222
2200
|
if (move.effect != .Poison) return;
|
|
2223
|
-
return log.fail(.{ foe_ident,
|
|
2201
|
+
return log.fail(.{ foe_ident, .None });
|
|
2224
2202
|
} else if (Status.any(foe_stored.status)) {
|
|
2225
2203
|
if (move.effect != .Poison) return if (showdown) battle.rng.advance(1);
|
|
2226
2204
|
// Pokémon Showdown considers Toxic to be a status even in Generation I and so
|
|
@@ -2236,7 +2214,7 @@ pub const Effects = struct {
|
|
|
2236
2214
|
Fail.None });
|
|
2237
2215
|
} else if (foe.active.types.includes(.Poison)) {
|
|
2238
2216
|
if (move.effect != .Poison) return if (showdown) battle.rng.advance(1);
|
|
2239
|
-
return log.immune(.{ foe_ident,
|
|
2217
|
+
return log.immune(.{ foe_ident, .None });
|
|
2240
2218
|
}
|
|
2241
2219
|
|
|
2242
2220
|
if (move.effect == .Poison) {
|
|
@@ -2255,7 +2233,7 @@ pub const Effects = struct {
|
|
|
2255
2233
|
foe.active.volatiles.toxic = 0;
|
|
2256
2234
|
}
|
|
2257
2235
|
|
|
2258
|
-
try log.status(.{ foe_ident, foe_stored.status,
|
|
2236
|
+
try log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2259
2237
|
}
|
|
2260
2238
|
|
|
2261
2239
|
fn rage(battle: anytype, player: Player) !void {
|
|
@@ -2278,7 +2256,7 @@ pub const Effects = struct {
|
|
|
2278
2256
|
try options.log.damage(.{
|
|
2279
2257
|
battle.active(player),
|
|
2280
2258
|
stored,
|
|
2281
|
-
|
|
2259
|
+
.RecoilOf,
|
|
2282
2260
|
battle.active(player.foe()),
|
|
2283
2261
|
});
|
|
2284
2262
|
if (showdown and stored.hp == 0) residual.* = false;
|
|
@@ -2286,13 +2264,12 @@ pub const Effects = struct {
|
|
|
2286
2264
|
|
|
2287
2265
|
fn reflect(battle: anytype, player: Player, options: anytype) !void {
|
|
2288
2266
|
var side = battle.side(player);
|
|
2267
|
+
const ident = battle.active(player);
|
|
2289
2268
|
|
|
2290
|
-
if (side.active.volatiles.Reflect) {
|
|
2291
|
-
return options.log.fail(.{ battle.active(player), Fail.None });
|
|
2292
|
-
}
|
|
2269
|
+
if (side.active.volatiles.Reflect) return options.log.fail(.{ ident, .None });
|
|
2293
2270
|
side.active.volatiles.Reflect = true;
|
|
2294
2271
|
|
|
2295
|
-
try options.log.start(
|
|
2272
|
+
try options.log.start(.{ ident, .Reflect });
|
|
2296
2273
|
}
|
|
2297
2274
|
|
|
2298
2275
|
fn sleep(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
@@ -2326,17 +2303,17 @@ pub const Effects = struct {
|
|
|
2326
2303
|
|
|
2327
2304
|
foe_stored.status = Status.slp(Rolls.sleepDuration(battle, player, options));
|
|
2328
2305
|
const last = battle.side(player).last_selected_move;
|
|
2329
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
2306
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .From, last });
|
|
2330
2307
|
}
|
|
2331
2308
|
|
|
2332
2309
|
fn splash(battle: anytype, player: Player, options: anytype) !void {
|
|
2333
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2310
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2334
2311
|
}
|
|
2335
2312
|
|
|
2336
2313
|
fn substitute(battle: anytype, player: Player, residual: *bool, options: anytype) !void {
|
|
2337
2314
|
var side = battle.side(player);
|
|
2338
2315
|
if (side.active.volatiles.Substitute) {
|
|
2339
|
-
return options.log.fail(.{ battle.active(player),
|
|
2316
|
+
return options.log.fail(.{ battle.active(player), .Substitute });
|
|
2340
2317
|
}
|
|
2341
2318
|
|
|
2342
2319
|
assert(side.stored().stats.hp <= 1023);
|
|
@@ -2348,16 +2325,16 @@ pub const Effects = struct {
|
|
|
2348
2325
|
// avoid having to convert to floating point)
|
|
2349
2326
|
const required_hp = if (showdown) @divFloor(side.stored().stats.hp - 1, 4) + 1 else hp;
|
|
2350
2327
|
if (side.stored().hp < required_hp) {
|
|
2351
|
-
return options.log.fail(.{ battle.active(player),
|
|
2328
|
+
return options.log.fail(.{ battle.active(player), .Weak });
|
|
2352
2329
|
}
|
|
2353
2330
|
|
|
2354
2331
|
// GLITCH: can leave the user with 0 HP (faints later) because didn't check '<=' above
|
|
2355
2332
|
side.stored().hp -= hp;
|
|
2356
2333
|
side.active.volatiles.substitute = hp + 1;
|
|
2357
2334
|
side.active.volatiles.Substitute = true;
|
|
2358
|
-
try options.log.start(battle.active(player), .Substitute);
|
|
2335
|
+
try options.log.start(.{ battle.active(player), .Substitute });
|
|
2359
2336
|
if (hp > 0) {
|
|
2360
|
-
try options.log.damage(.{ battle.active(player), side.stored(),
|
|
2337
|
+
try options.log.damage(.{ battle.active(player), side.stored(), .None });
|
|
2361
2338
|
if (showdown and side.stored().hp == 0) residual.* = false;
|
|
2362
2339
|
}
|
|
2363
2340
|
}
|
|
@@ -2371,7 +2348,7 @@ pub const Effects = struct {
|
|
|
2371
2348
|
}
|
|
2372
2349
|
battle.last_damage = 0;
|
|
2373
2350
|
} else {
|
|
2374
|
-
try options.log.fail(.{ battle.active(player),
|
|
2351
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2375
2352
|
try options.log.laststill(.{});
|
|
2376
2353
|
}
|
|
2377
2354
|
}
|
|
@@ -2441,20 +2418,20 @@ pub const Effects = struct {
|
|
|
2441
2418
|
switch (move.effect) {
|
|
2442
2419
|
.AttackUp1, .AttackUp2, .Rage => {
|
|
2443
2420
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2444
|
-
if (boosts.atk == 6) return log.fail(.{ ident,
|
|
2421
|
+
if (boosts.atk == 6) return log.fail(.{ ident, .None });
|
|
2445
2422
|
const n: u2 = if (move.effect == .AttackUp2) 2 else 1;
|
|
2446
2423
|
boosts.atk = @as(i4, @intCast(@min(6, @as(i8, boosts.atk) + n)));
|
|
2447
|
-
const reason = if (move.effect == .Rage)
|
|
2424
|
+
const reason: Boost = if (move.effect == .Rage) .Rage else .Attack;
|
|
2448
2425
|
if (stats.atk == MAX_STAT_VALUE) {
|
|
2449
2426
|
boosts.atk -= 1;
|
|
2450
2427
|
if (showdown) {
|
|
2451
2428
|
try log.boost(.{ ident, reason, n });
|
|
2452
|
-
try log.boost(.{ ident,
|
|
2429
|
+
try log.boost(.{ ident, .Attack, -1 });
|
|
2453
2430
|
if (move.effect == .Rage) return;
|
|
2454
2431
|
}
|
|
2455
|
-
return log.fail(.{ ident,
|
|
2432
|
+
return log.fail(.{ ident, .None });
|
|
2456
2433
|
}
|
|
2457
|
-
|
|
2434
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.atk) + 6))];
|
|
2458
2435
|
const stat = unmodifiedStats(battle, side).atk;
|
|
2459
2436
|
stats.atk = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2460
2437
|
try log.boost(.{ ident, reason, n });
|
|
@@ -2463,65 +2440,65 @@ pub const Effects = struct {
|
|
|
2463
2440
|
},
|
|
2464
2441
|
.DefenseUp1, .DefenseUp2 => {
|
|
2465
2442
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2466
|
-
if (boosts.def == 6) return log.fail(.{ ident,
|
|
2443
|
+
if (boosts.def == 6) return log.fail(.{ ident, .None });
|
|
2467
2444
|
const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
|
|
2468
2445
|
boosts.def = @intCast(@min(6, @as(i8, boosts.def) + n));
|
|
2469
2446
|
if (stats.def == MAX_STAT_VALUE) {
|
|
2470
2447
|
boosts.def -= 1;
|
|
2471
2448
|
if (showdown) {
|
|
2472
|
-
try log.boost(.{ ident,
|
|
2473
|
-
try log.boost(.{ ident,
|
|
2449
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2450
|
+
try log.boost(.{ ident, .Defense, -1 });
|
|
2474
2451
|
}
|
|
2475
|
-
return log.fail(.{ ident,
|
|
2452
|
+
return log.fail(.{ ident, .None });
|
|
2476
2453
|
}
|
|
2477
|
-
|
|
2454
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.def) + 6))];
|
|
2478
2455
|
const stat = unmodifiedStats(battle, side).def;
|
|
2479
2456
|
stats.def = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2480
|
-
try log.boost(.{ ident,
|
|
2457
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2481
2458
|
},
|
|
2482
2459
|
.SpeedUp2 => {
|
|
2483
2460
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2484
|
-
if (boosts.spe == 6) return log.fail(.{ ident,
|
|
2461
|
+
if (boosts.spe == 6) return log.fail(.{ ident, .None });
|
|
2485
2462
|
boosts.spe = @intCast(@min(6, @as(i8, boosts.spe) + 2));
|
|
2486
2463
|
if (stats.spe == MAX_STAT_VALUE) {
|
|
2487
2464
|
boosts.spe -= 1;
|
|
2488
2465
|
if (showdown) {
|
|
2489
|
-
try log.boost(.{ ident,
|
|
2490
|
-
try log.boost(.{ ident,
|
|
2466
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2467
|
+
try log.boost(.{ ident, .Speed, -1 });
|
|
2491
2468
|
}
|
|
2492
|
-
return log.fail(.{ ident,
|
|
2469
|
+
return log.fail(.{ ident, .None });
|
|
2493
2470
|
}
|
|
2494
|
-
|
|
2471
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spe) + 6))];
|
|
2495
2472
|
const stat = unmodifiedStats(battle, side).spe;
|
|
2496
2473
|
stats.spe = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2497
|
-
try log.boost(.{ ident,
|
|
2474
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2498
2475
|
},
|
|
2499
2476
|
.SpecialUp1, .SpecialUp2 => {
|
|
2500
2477
|
assert(boosts.spc >= -6 and boosts.spc <= 6);
|
|
2501
|
-
if (boosts.spc == 6) return log.fail(.{ ident,
|
|
2478
|
+
if (boosts.spc == 6) return log.fail(.{ ident, .None });
|
|
2502
2479
|
const n: u2 = if (move.effect == .SpecialUp2) 2 else 1;
|
|
2503
2480
|
boosts.spc = @intCast(@min(6, @as(i8, boosts.spc) + n));
|
|
2504
2481
|
if (stats.spc == MAX_STAT_VALUE) {
|
|
2505
2482
|
boosts.spc -= 1;
|
|
2506
2483
|
if (showdown) {
|
|
2507
|
-
try log.boost(.{ ident,
|
|
2508
|
-
try log.boost(.{ ident,
|
|
2509
|
-
try log.boost(.{ ident,
|
|
2510
|
-
try log.boost(.{ ident,
|
|
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 });
|
|
2511
2488
|
}
|
|
2512
|
-
return log.fail(.{ ident,
|
|
2489
|
+
return log.fail(.{ ident, .None });
|
|
2513
2490
|
}
|
|
2514
|
-
|
|
2491
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spc) + 6))];
|
|
2515
2492
|
const stat = unmodifiedStats(battle, side).spc;
|
|
2516
2493
|
stats.spc = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2517
|
-
try log.boost(.{ ident,
|
|
2518
|
-
try log.boost(.{ ident,
|
|
2494
|
+
try log.boost(.{ ident, .SpecialAttack, n });
|
|
2495
|
+
try log.boost(.{ ident, .SpecialDefense, n });
|
|
2519
2496
|
},
|
|
2520
2497
|
.EvasionUp1 => {
|
|
2521
2498
|
assert(boosts.evasion >= -6 and boosts.evasion <= 6);
|
|
2522
|
-
if (boosts.evasion == 6) return log.fail(.{ ident,
|
|
2499
|
+
if (boosts.evasion == 6) return log.fail(.{ ident, .None });
|
|
2523
2500
|
boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
|
|
2524
|
-
try log.boost(.{ ident,
|
|
2501
|
+
try log.boost(.{ ident, .Evasion, 1 });
|
|
2525
2502
|
},
|
|
2526
2503
|
else => unreachable,
|
|
2527
2504
|
}
|
|
@@ -2536,9 +2513,7 @@ pub const Effects = struct {
|
|
|
2536
2513
|
const foe_ident = battle.active(player.foe());
|
|
2537
2514
|
const secondary = move.effect.isStatDownChance();
|
|
2538
2515
|
|
|
2539
|
-
if (foe.active.volatiles.Substitute) {
|
|
2540
|
-
return if (!secondary) log.fail(.{ foe_ident, Fail.None });
|
|
2541
|
-
}
|
|
2516
|
+
if (foe.active.volatiles.Substitute) return if (!secondary) log.fail(.{ foe_ident, .None });
|
|
2542
2517
|
|
|
2543
2518
|
if (secondary) {
|
|
2544
2519
|
const proc = try Rolls.unboost(battle, player, options);
|
|
@@ -2556,82 +2531,82 @@ pub const Effects = struct {
|
|
|
2556
2531
|
switch (move.effect) {
|
|
2557
2532
|
.AttackDown1, .AttackDownChance => {
|
|
2558
2533
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2559
|
-
if (boosts.atk == -6) return if (fail) try log.fail(.{ foe_ident,
|
|
2534
|
+
if (boosts.atk == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2560
2535
|
boosts.atk = @intCast(@max(-6, @as(i8, boosts.atk) - 1));
|
|
2561
2536
|
if (stats.atk == 1) {
|
|
2562
2537
|
boosts.atk += 1;
|
|
2563
2538
|
if (showdown) {
|
|
2564
|
-
try log.boost(.{ foe_ident,
|
|
2565
|
-
try log.boost(.{ foe_ident,
|
|
2539
|
+
try log.boost(.{ foe_ident, .Attack, -1 });
|
|
2540
|
+
try log.boost(.{ foe_ident, .Attack, 1 });
|
|
2566
2541
|
}
|
|
2567
|
-
return log.fail(.{ foe_ident,
|
|
2542
|
+
return log.fail(.{ foe_ident, .None });
|
|
2568
2543
|
}
|
|
2569
|
-
|
|
2544
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.atk) + 6))];
|
|
2570
2545
|
const stat = unmodifiedStats(battle, foe).atk;
|
|
2571
2546
|
stats.atk = @max(1, stat * mod[0] / mod[1]);
|
|
2572
|
-
try log.boost(.{ foe_ident,
|
|
2547
|
+
try log.boost(.{ foe_ident, .Attack, -1 });
|
|
2573
2548
|
},
|
|
2574
2549
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2575
2550
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2576
|
-
if (boosts.def == -6) return if (fail) try log.fail(.{ foe_ident,
|
|
2551
|
+
if (boosts.def == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2577
2552
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2578
2553
|
boosts.def = @intCast(@max(-6, @as(i8, boosts.def) - n));
|
|
2579
2554
|
if (stats.def == 1) {
|
|
2580
2555
|
boosts.def += 1;
|
|
2581
2556
|
if (showdown) {
|
|
2582
|
-
try log.boost(.{ foe_ident,
|
|
2583
|
-
try log.boost(.{ foe_ident,
|
|
2557
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2558
|
+
try log.boost(.{ foe_ident, .Defense, 1 });
|
|
2584
2559
|
}
|
|
2585
|
-
return log.fail(.{ foe_ident,
|
|
2560
|
+
return log.fail(.{ foe_ident, .None });
|
|
2586
2561
|
}
|
|
2587
|
-
|
|
2562
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.def) + 6))];
|
|
2588
2563
|
const stat = unmodifiedStats(battle, foe).def;
|
|
2589
2564
|
stats.def = @max(1, stat * mod[0] / mod[1]);
|
|
2590
|
-
try log.boost(.{ foe_ident,
|
|
2565
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2591
2566
|
},
|
|
2592
2567
|
.SpeedDown1, .SpeedDownChance => {
|
|
2593
2568
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2594
|
-
if (boosts.spe == -6) return if (fail) try log.fail(.{ foe_ident,
|
|
2569
|
+
if (boosts.spe == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2595
2570
|
boosts.spe = @intCast(@max(-6, @as(i8, boosts.spe) - 1));
|
|
2596
2571
|
if (stats.spe == 1) {
|
|
2597
2572
|
boosts.spe += 1;
|
|
2598
2573
|
if (showdown) {
|
|
2599
|
-
try log.boost(.{ foe_ident,
|
|
2600
|
-
try log.boost(.{ foe_ident,
|
|
2574
|
+
try log.boost(.{ foe_ident, .Speed, -1 });
|
|
2575
|
+
try log.boost(.{ foe_ident, .Speed, 1 });
|
|
2601
2576
|
}
|
|
2602
|
-
return log.fail(.{ foe_ident,
|
|
2577
|
+
return log.fail(.{ foe_ident, .None });
|
|
2603
2578
|
}
|
|
2604
|
-
|
|
2579
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spe) + 6))];
|
|
2605
2580
|
const stat = unmodifiedStats(battle, foe).spe;
|
|
2606
2581
|
stats.spe = @max(1, stat * mod[0] / mod[1]);
|
|
2607
|
-
try log.boost(.{ foe_ident,
|
|
2582
|
+
try log.boost(.{ foe_ident, .Speed, -1 });
|
|
2608
2583
|
assert(boosts.spe >= -6);
|
|
2609
2584
|
},
|
|
2610
2585
|
.SpecialDownChance => {
|
|
2611
2586
|
assert(boosts.spc >= -6 and boosts.spc <= 6);
|
|
2612
|
-
if (boosts.spc == -6) return if (fail) try log.fail(.{ foe_ident,
|
|
2587
|
+
if (boosts.spc == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2613
2588
|
boosts.spc = @intCast(@max(-6, @as(i8, boosts.spc) - 1));
|
|
2614
2589
|
if (stats.spc == 1) {
|
|
2615
2590
|
boosts.spc += 1;
|
|
2616
2591
|
if (showdown) {
|
|
2617
|
-
try log.boost(.{ foe_ident,
|
|
2618
|
-
try log.boost(.{ foe_ident,
|
|
2619
|
-
try log.boost(.{ foe_ident,
|
|
2620
|
-
try log.boost(.{ foe_ident,
|
|
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 });
|
|
2621
2596
|
}
|
|
2622
|
-
return log.fail(.{ foe_ident,
|
|
2597
|
+
return log.fail(.{ foe_ident, .None });
|
|
2623
2598
|
}
|
|
2624
|
-
|
|
2599
|
+
const mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spc) + 6))];
|
|
2625
2600
|
const stat = unmodifiedStats(battle, foe).spc;
|
|
2626
2601
|
stats.spc = @max(1, stat * mod[0] / mod[1]);
|
|
2627
|
-
try log.boost(.{ foe_ident,
|
|
2628
|
-
try log.boost(.{ foe_ident,
|
|
2602
|
+
try log.boost(.{ foe_ident, .SpecialAttack, -1 });
|
|
2603
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2629
2604
|
},
|
|
2630
2605
|
.AccuracyDown1 => {
|
|
2631
2606
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
2632
|
-
if (boosts.accuracy == -6) return if (fail) try log.fail(.{ foe_ident,
|
|
2607
|
+
if (boosts.accuracy == -6) return if (fail) try log.fail(.{ foe_ident, .None });
|
|
2633
2608
|
boosts.accuracy = @intCast(@max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2634
|
-
try log.boost(.{ foe_ident,
|
|
2609
|
+
try log.boost(.{ foe_ident, .Accuracy, -1 });
|
|
2635
2610
|
},
|
|
2636
2611
|
else => unreachable,
|
|
2637
2612
|
}
|
|
@@ -2671,24 +2646,24 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
|
|
|
2671
2646
|
if (volatiles.disable_move != 0) {
|
|
2672
2647
|
volatiles.disable_move = 0;
|
|
2673
2648
|
volatiles.disable_duration = 0;
|
|
2674
|
-
try log.end(.{ ident,
|
|
2649
|
+
try log.end(.{ ident, .DisableSilent });
|
|
2675
2650
|
}
|
|
2676
2651
|
if (volatiles.Confusion) {
|
|
2677
2652
|
// volatiles.confusion is left unchanged
|
|
2678
2653
|
volatiles.Confusion = false;
|
|
2679
|
-
try log.end(.{ ident,
|
|
2654
|
+
try log.end(.{ ident, .ConfusionSilent });
|
|
2680
2655
|
}
|
|
2681
2656
|
if (volatiles.Mist) {
|
|
2682
2657
|
volatiles.Mist = false;
|
|
2683
|
-
try log.end(.{ ident,
|
|
2658
|
+
try log.end(.{ ident, .MistSilent });
|
|
2684
2659
|
}
|
|
2685
2660
|
if (volatiles.FocusEnergy) {
|
|
2686
2661
|
volatiles.FocusEnergy = false;
|
|
2687
|
-
try log.end(.{ ident,
|
|
2662
|
+
try log.end(.{ ident, .FocusEnergySilent });
|
|
2688
2663
|
}
|
|
2689
2664
|
if (volatiles.LeechSeed) {
|
|
2690
2665
|
volatiles.LeechSeed = false;
|
|
2691
|
-
try log.end(.{ ident,
|
|
2666
|
+
try log.end(.{ ident, .LeechSeedSilent });
|
|
2692
2667
|
}
|
|
2693
2668
|
if (!showdown and volatiles.Toxic) {
|
|
2694
2669
|
volatiles.Toxic = false;
|
|
@@ -2696,17 +2671,17 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
|
|
|
2696
2671
|
}
|
|
2697
2672
|
if (volatiles.LightScreen) {
|
|
2698
2673
|
volatiles.LightScreen = false;
|
|
2699
|
-
try log.end(.{ ident,
|
|
2674
|
+
try log.end(.{ ident, .LightScreenSilent });
|
|
2700
2675
|
}
|
|
2701
2676
|
if (volatiles.Reflect) {
|
|
2702
2677
|
volatiles.Reflect = false;
|
|
2703
|
-
try log.end(.{ ident,
|
|
2678
|
+
try log.end(.{ ident, .ReflectSilent });
|
|
2704
2679
|
}
|
|
2705
2680
|
if (showdown and volatiles.Toxic) {
|
|
2706
2681
|
volatiles.Toxic = false;
|
|
2707
2682
|
// Pokémon Showdown erroneously clears the toxic counter
|
|
2708
2683
|
volatiles.toxic = 0;
|
|
2709
|
-
try log.end(.{ ident,
|
|
2684
|
+
try log.end(.{ ident, .ToxicSilent });
|
|
2710
2685
|
}
|
|
2711
2686
|
}
|
|
2712
2687
|
|
|
@@ -2714,6 +2689,8 @@ pub const Rolls = struct {
|
|
|
2714
2689
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2715
2690
|
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2716
2691
|
player == .P1
|
|
2692
|
+
else if (@hasDecl(@TypeOf(battle.rng), "speedTie"))
|
|
2693
|
+
battle.rng.speedTie()
|
|
2717
2694
|
else if (showdown)
|
|
2718
2695
|
battle.rng.range(u8, 0, 2) == 0
|
|
2719
2696
|
else
|
|
@@ -2726,6 +2703,8 @@ pub const Rolls = struct {
|
|
|
2726
2703
|
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2727
2704
|
const crit = if (options.calc.overridden(player, .critical_hit)) |val|
|
|
2728
2705
|
val == .true
|
|
2706
|
+
else if (@hasDecl(@TypeOf(battle.rng), "criticalHit"))
|
|
2707
|
+
battle.rng.criticalHit(player, rate)
|
|
2729
2708
|
else if (showdown)
|
|
2730
2709
|
battle.rng.chance(u8, rate, 256)
|
|
2731
2710
|
else
|
|
@@ -2738,6 +2717,8 @@ pub const Rolls = struct {
|
|
|
2738
2717
|
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2739
2718
|
const roll = if (options.calc.overridden(player, .damage)) |val|
|
|
2740
2719
|
val
|
|
2720
|
+
else if (@hasDecl(@TypeOf(battle.rng), "damage"))
|
|
2721
|
+
battle.rng.damage(player)
|
|
2741
2722
|
else roll: {
|
|
2742
2723
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
2743
2724
|
while (true) {
|
|
@@ -2754,6 +2735,8 @@ pub const Rolls = struct {
|
|
|
2754
2735
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) bool {
|
|
2755
2736
|
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2756
2737
|
val == .true
|
|
2738
|
+
else if (@hasDecl(@TypeOf(battle.rng), "hit"))
|
|
2739
|
+
battle.rng.hit(player, accuracy)
|
|
2757
2740
|
else if (showdown)
|
|
2758
2741
|
battle.rng.chance(u8, accuracy, 256)
|
|
2759
2742
|
else
|
|
@@ -2766,6 +2749,8 @@ pub const Rolls = struct {
|
|
|
2766
2749
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2767
2750
|
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2768
2751
|
val == .true
|
|
2752
|
+
else if (@hasDecl(@TypeOf(battle.rng), "confused"))
|
|
2753
|
+
battle.rng.confused(player)
|
|
2769
2754
|
else if (showdown)
|
|
2770
2755
|
!battle.rng.chance(u8, 128, 256)
|
|
2771
2756
|
else
|
|
@@ -2778,6 +2763,8 @@ pub const Rolls = struct {
|
|
|
2778
2763
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2779
2764
|
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2780
2765
|
val == .true
|
|
2766
|
+
else if (@hasDecl(@TypeOf(battle.rng), "paralyzed"))
|
|
2767
|
+
battle.rng.paralyzed(player)
|
|
2781
2768
|
else if (showdown)
|
|
2782
2769
|
battle.rng.chance(u8, 63, 256)
|
|
2783
2770
|
else
|
|
@@ -2790,6 +2777,8 @@ pub const Rolls = struct {
|
|
|
2790
2777
|
fn confusionChance(battle: anytype, player: Player, options: anytype) !bool {
|
|
2791
2778
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2792
2779
|
val == .true
|
|
2780
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2781
|
+
battle.rng.secondaryChance(player, 25)
|
|
2793
2782
|
else if (showdown)
|
|
2794
2783
|
battle.rng.chance(u8, 25, 256)
|
|
2795
2784
|
else
|
|
@@ -2804,6 +2793,8 @@ pub const Rolls = struct {
|
|
|
2804
2793
|
|
|
2805
2794
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2806
2795
|
val == .true
|
|
2796
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2797
|
+
battle.rng.secondaryChance(player, rate)
|
|
2807
2798
|
else if (showdown)
|
|
2808
2799
|
battle.rng.chance(u8, rate, 256)
|
|
2809
2800
|
else
|
|
@@ -2818,6 +2809,8 @@ pub const Rolls = struct {
|
|
|
2818
2809
|
|
|
2819
2810
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2820
2811
|
val == .true
|
|
2812
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2813
|
+
battle.rng.secondaryChance(player, rate)
|
|
2821
2814
|
else if (showdown)
|
|
2822
2815
|
battle.rng.chance(u8, rate, 256)
|
|
2823
2816
|
else
|
|
@@ -2830,6 +2823,8 @@ pub const Rolls = struct {
|
|
|
2830
2823
|
fn unboost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2831
2824
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2832
2825
|
val == .true
|
|
2826
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2827
|
+
battle.rng.secondaryChance(player, 85)
|
|
2833
2828
|
else if (showdown)
|
|
2834
2829
|
battle.rng.chance(u8, 85, 256)
|
|
2835
2830
|
else
|
|
@@ -2842,6 +2837,8 @@ pub const Rolls = struct {
|
|
|
2842
2837
|
fn metronome(battle: anytype, player: Player, options: anytype) !Move {
|
|
2843
2838
|
const move: Move = if (options.calc.overridden(player, .metronome)) |val|
|
|
2844
2839
|
val
|
|
2840
|
+
else if (@hasDecl(@TypeOf(battle.rng), "metronome"))
|
|
2841
|
+
battle.rng.metronome(player)
|
|
2845
2842
|
else if (showdown)
|
|
2846
2843
|
Move.METRONOME[battle.rng.range(u8, 0, Move.METRONOME.len)]
|
|
2847
2844
|
else move: {
|
|
@@ -2860,6 +2857,8 @@ pub const Rolls = struct {
|
|
|
2860
2857
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
2861
2858
|
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
2862
2859
|
val - 1
|
|
2860
|
+
else if (@hasDecl(@TypeOf(battle.rng), "psywave"))
|
|
2861
|
+
battle.rng.psywave(player)
|
|
2863
2862
|
else if (showdown)
|
|
2864
2863
|
battle.rng.range(u8, 0, max)
|
|
2865
2864
|
else power: {
|
|
@@ -2877,6 +2876,8 @@ pub const Rolls = struct {
|
|
|
2877
2876
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2878
2877
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2879
2878
|
@intCast(val)
|
|
2879
|
+
else if (@hasDecl(@TypeOf(battle.rng), "sleepDuration"))
|
|
2880
|
+
battle.rng.sleepDuration(player)
|
|
2880
2881
|
else if (showdown)
|
|
2881
2882
|
battle.rng.range(u3, 1, 8)
|
|
2882
2883
|
else duration: {
|
|
@@ -2894,6 +2895,8 @@ pub const Rolls = struct {
|
|
|
2894
2895
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
2895
2896
|
const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
|
|
2896
2897
|
val
|
|
2898
|
+
else if (@hasDecl(@TypeOf(battle.rng), "disableDuration"))
|
|
2899
|
+
battle.rng.disableDuration(player)
|
|
2897
2900
|
else if (showdown)
|
|
2898
2901
|
battle.rng.range(u4, 1, 9)
|
|
2899
2902
|
else
|
|
@@ -2907,6 +2910,8 @@ pub const Rolls = struct {
|
|
|
2907
2910
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
2908
2911
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2909
2912
|
@intCast(val)
|
|
2913
|
+
else if (@hasDecl(@TypeOf(battle.rng), "confusionDuration"))
|
|
2914
|
+
battle.rng.confusionDuration(player, self)
|
|
2910
2915
|
else if (showdown)
|
|
2911
2916
|
battle.rng.range(u3, 2, 6)
|
|
2912
2917
|
else
|
|
@@ -2920,6 +2925,8 @@ pub const Rolls = struct {
|
|
|
2920
2925
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2921
2926
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2922
2927
|
@intCast(val)
|
|
2928
|
+
else if (@hasDecl(@TypeOf(battle.rng), "attackingDuration"))
|
|
2929
|
+
battle.rng.attackingDuration(player)
|
|
2923
2930
|
else if (showdown)
|
|
2924
2931
|
battle.rng.range(u3, 2, 4)
|
|
2925
2932
|
else
|
|
@@ -2941,6 +2948,8 @@ pub const Rolls = struct {
|
|
|
2941
2948
|
const roll = if (effect == .Binding) .duration else .multi_hit;
|
|
2942
2949
|
const n: u3 = if (options.calc.overridden(player, roll)) |val|
|
|
2943
2950
|
@intCast(val)
|
|
2951
|
+
else if (@hasDecl(@TypeOf(battle.rng), "distribution"))
|
|
2952
|
+
battle.rng.distribution(player)
|
|
2944
2953
|
else if (showdown)
|
|
2945
2954
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
2946
2955
|
else n: {
|
|
@@ -2973,7 +2982,9 @@ pub const Rolls = struct {
|
|
|
2973
2982
|
else
|
|
2974
2983
|
null;
|
|
2975
2984
|
|
|
2976
|
-
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: {
|
|
2977
2988
|
if (showdown) {
|
|
2978
2989
|
if (check_pp == 0) {
|
|
2979
2990
|
var i: usize = moves.len;
|
|
@@ -3053,7 +3064,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3053
3064
|
const side = battle.side(player);
|
|
3054
3065
|
const foe = battle.foe(player);
|
|
3055
3066
|
|
|
3056
|
-
|
|
3067
|
+
const active = &side.active;
|
|
3057
3068
|
const stored = side.stored();
|
|
3058
3069
|
|
|
3059
3070
|
// While players are not given any input options on the cartridge in these cases,
|