@pkmn/engine 0.1.0-dev.d56bdadd → 0.1.0-dev.d952b7c9
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 +46 -19
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +43 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +96 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon.d.ts +2 -0
- package/build/pkg/addon.js +35 -58
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +24 -10
- package/build/pkg/index.js +15 -10
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +109 -107
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +36 -68
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +128 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +132 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +206 -81
- package/package.json +51 -30
- package/src/bin/install-pkmn-engine +29 -19
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +11 -6
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +14 -10
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +389 -407
- package/src/lib/common/rational.zig +121 -27
- package/src/lib/common/rng.zig +7 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +657 -133
- package/src/lib/gen1/chance.zig +376 -267
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +17 -16
- package/src/lib/gen1/helpers.zig +95 -538
- package/src/lib/gen1/mechanics.zig +492 -389
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +84 -496
- package/src/lib/gen2/data.zig +8 -7
- package/src/lib/gen2/generated.zig +4 -6
- package/src/lib/gen2/helpers.zig +41 -13
- package/src/lib/gen2/mechanics.zig +448 -377
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +32 -15
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +351 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -6,7 +6,6 @@ const common = @import("../common/data.zig");
|
|
|
6
6
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
7
|
const protocol = @import("../common/protocol.zig");
|
|
8
8
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
9
|
|
|
11
10
|
const data = @import("data.zig");
|
|
12
11
|
const generated = @import("generated.zig");
|
|
@@ -26,25 +25,20 @@ const Result = common.Result;
|
|
|
26
25
|
|
|
27
26
|
const Activate = protocol.Activate;
|
|
28
27
|
const Boost = protocol.Boost;
|
|
29
|
-
const Cant = protocol.Cant;
|
|
30
28
|
const Damage = protocol.Damage;
|
|
31
29
|
const End = protocol.End;
|
|
30
|
+
const Fail = protocol.Fail;
|
|
32
31
|
const Heal = protocol.Heal;
|
|
33
32
|
const Start = protocol.Start;
|
|
34
33
|
|
|
35
34
|
const Gen12 = rng.Gen12;
|
|
36
35
|
|
|
37
|
-
const PointerType = util.PointerType;
|
|
38
|
-
|
|
39
36
|
const ActivePokemon = data.ActivePokemon;
|
|
40
37
|
const Boosts = data.Boosts;
|
|
41
38
|
const Effectiveness = data.Effectiveness;
|
|
42
39
|
const Item = data.Item;
|
|
43
40
|
const Move = data.Move;
|
|
44
|
-
const MoveSlot = data.MoveSlot;
|
|
45
|
-
const Pokemon = data.Pokemon;
|
|
46
41
|
const Side = data.Side;
|
|
47
|
-
const Species = data.Species;
|
|
48
42
|
const Stats = data.Stats;
|
|
49
43
|
const Status = data.Status;
|
|
50
44
|
const TriAttack = data.TriAttack;
|
|
@@ -106,6 +100,7 @@ pub const State = struct {
|
|
|
106
100
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
101
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
102
|
move: Move, // wCurPlayerMove
|
|
103
|
+
hit: u3 = 0,
|
|
109
104
|
from: Move = .None,
|
|
110
105
|
mslot: u4, // wCurMoveNum
|
|
111
106
|
first: bool, // wEnemyGoesFirst
|
|
@@ -140,11 +135,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
140
135
|
const p1 = battle.side(.P1);
|
|
141
136
|
const p2 = battle.side(.P2);
|
|
142
137
|
|
|
143
|
-
|
|
138
|
+
const p1_slot = findFirstAlive(p1);
|
|
144
139
|
assert(!showdown or p1_slot == 1);
|
|
145
140
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
146
141
|
|
|
147
|
-
|
|
142
|
+
const p2_slot = findFirstAlive(p2);
|
|
148
143
|
assert(!showdown or p2_slot == 1);
|
|
149
144
|
if (p2_slot == 0) return Result.Win;
|
|
150
145
|
|
|
@@ -252,15 +247,14 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
252
247
|
statusModify(incoming.status, &active.stats);
|
|
253
248
|
|
|
254
249
|
if (kind == .Drag) {
|
|
255
|
-
try options.log.drag(battle.active(player), incoming);
|
|
250
|
+
try options.log.drag(.{ battle.active(player), incoming });
|
|
256
251
|
} else {
|
|
257
|
-
try options.log.switched(battle.active(player), incoming);
|
|
252
|
+
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
253
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
254
|
|
|
261
255
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
256
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
263
|
-
try options.log.damage(battle.active(player), incoming, .Spikes);
|
|
257
|
+
try options.log.damage(.{ battle.active(player), incoming, .Spikes });
|
|
264
258
|
}
|
|
265
259
|
}
|
|
266
260
|
|
|
@@ -403,7 +397,9 @@ fn executeMove(
|
|
|
403
397
|
|
|
404
398
|
assert(choice.type == .Move);
|
|
405
399
|
var mslot: u4 = @intCast(choice.data);
|
|
400
|
+
_ = &mslot;
|
|
406
401
|
var move = Move.Pound; // TODO
|
|
402
|
+
_ = &move;
|
|
407
403
|
|
|
408
404
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
409
405
|
|
|
@@ -433,31 +429,22 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
433
429
|
if (volatiles.Recharging) {
|
|
434
430
|
volatiles.Recharging = false;
|
|
435
431
|
resetCant(volatiles);
|
|
436
|
-
try log.cant(ident, .Recharge);
|
|
432
|
+
try log.cant(.{ ident, .Recharge });
|
|
437
433
|
return false;
|
|
438
434
|
}
|
|
439
435
|
|
|
440
436
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
441
437
|
const before = stored.status;
|
|
442
438
|
// Even if the EXT bit is set this will still correctly modify the sleep duration
|
|
443
|
-
// if (options.calc.modify(player, .sleep)) |extend| {
|
|
444
|
-
// if (!extend) stored.status = 0;
|
|
445
|
-
// } else {
|
|
446
439
|
stored.status -= 1;
|
|
447
|
-
// }
|
|
448
|
-
|
|
449
|
-
const duration = Status.duration(stored.status);
|
|
450
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
451
|
-
try options.chance.sleep(player, duration);
|
|
452
|
-
}
|
|
453
440
|
|
|
454
|
-
if (duration == 0) {
|
|
455
|
-
try log.curestatus(ident, before, .Message);
|
|
441
|
+
if (Status.duration(stored.status) == 0) {
|
|
442
|
+
try log.curestatus(.{ ident, before, .Message });
|
|
456
443
|
stored.status = 0; // clears EXT if present
|
|
457
444
|
resetCant(volatiles);
|
|
458
445
|
volatiles.Nightmare = false;
|
|
459
446
|
} else {
|
|
460
|
-
try log.cant(ident, .Sleep);
|
|
447
|
+
try log.cant(.{ ident, .Sleep });
|
|
461
448
|
if (move == .Snore or move == .SleepTalk) break :slp;
|
|
462
449
|
resetCant(volatiles);
|
|
463
450
|
return false;
|
|
@@ -466,7 +453,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
466
453
|
|
|
467
454
|
if (Status.is(stored.status, .FRZ) and !(move == .FlameWheel or move == .SacredFire)) {
|
|
468
455
|
resetCant(volatiles);
|
|
469
|
-
try log.cant(ident, .Freeze);
|
|
456
|
+
try log.cant(.{ ident, .Freeze });
|
|
470
457
|
return false;
|
|
471
458
|
}
|
|
472
459
|
|
|
@@ -476,39 +463,28 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
476
463
|
// if (!showdown) volatiles.Flinch = false;
|
|
477
464
|
volatiles.Flinch = false;
|
|
478
465
|
resetCant(volatiles);
|
|
479
|
-
try log.cant(ident, .Flinch);
|
|
466
|
+
try log.cant(.{ ident, .Flinch });
|
|
480
467
|
return false;
|
|
481
468
|
}
|
|
482
469
|
|
|
483
470
|
if (volatiles.disable.duration > 0) {
|
|
484
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
485
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
486
|
-
// } else {
|
|
487
471
|
volatiles.disable.duration -= 1;
|
|
488
|
-
// }
|
|
489
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
490
472
|
|
|
491
473
|
if (volatiles.disable.duration == 0) {
|
|
492
474
|
volatiles.disable.move = 0;
|
|
493
|
-
try log.end(ident, .Disable);
|
|
475
|
+
try log.end(.{ ident, .Disable });
|
|
494
476
|
}
|
|
495
477
|
}
|
|
496
478
|
|
|
497
479
|
if (volatiles.Confusion) {
|
|
498
480
|
assert(volatiles.confusion > 0);
|
|
499
|
-
|
|
500
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
501
|
-
// if (!extend) volatiles.confusion = 0;
|
|
502
|
-
// } else {
|
|
503
481
|
volatiles.confusion -= 1;
|
|
504
|
-
// }
|
|
505
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
506
482
|
|
|
507
483
|
if (volatiles.confusion == 0) {
|
|
508
484
|
volatiles.Confusion = false;
|
|
509
|
-
try log.end(ident, .Confusion);
|
|
485
|
+
try log.end(.{ ident, .Confusion });
|
|
510
486
|
} else {
|
|
511
|
-
try log.activate(ident, .Confusion);
|
|
487
|
+
try log.activate(.{ ident, .Confusion });
|
|
512
488
|
|
|
513
489
|
if (try Rolls.confused(battle, player, options)) {
|
|
514
490
|
volatiles.BeatUp = false;
|
|
@@ -523,11 +499,11 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
523
499
|
}
|
|
524
500
|
|
|
525
501
|
if (volatiles.Attract) {
|
|
526
|
-
try log.activate(ident, .Attract);
|
|
502
|
+
try log.activate(.{ ident, .Attract });
|
|
527
503
|
|
|
528
504
|
if (try Rolls.attract(battle, player, options)) {
|
|
529
505
|
resetCant(volatiles);
|
|
530
|
-
try log.cant(ident, .Attract);
|
|
506
|
+
try log.cant(.{ ident, .Attract });
|
|
531
507
|
return false;
|
|
532
508
|
}
|
|
533
509
|
}
|
|
@@ -538,14 +514,14 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
538
514
|
if (m != .None and m == move) {
|
|
539
515
|
side.active.volatiles.Charging = false;
|
|
540
516
|
resetCant(volatiles);
|
|
541
|
-
try options.log.
|
|
517
|
+
try options.log.cant(.{ ident, .Disable, move });
|
|
542
518
|
return false;
|
|
543
519
|
}
|
|
544
520
|
}
|
|
545
521
|
|
|
546
522
|
if (Status.is(stored.status, .PAR) and try Rolls.paralyzed(battle, player, options)) {
|
|
547
523
|
resetCant(volatiles);
|
|
548
|
-
try log.cant(ident, .Paralysis);
|
|
524
|
+
try log.cant(.{ ident, .Paralysis });
|
|
549
525
|
return false;
|
|
550
526
|
}
|
|
551
527
|
|
|
@@ -565,11 +541,11 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
565
541
|
|
|
566
542
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
567
543
|
var side = battle.side(player);
|
|
568
|
-
|
|
544
|
+
const volatiles = &side.active.volatiles;
|
|
569
545
|
|
|
570
546
|
// FIXME: pass in target to avoid lookup?
|
|
571
547
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
572
|
-
try options.log.
|
|
548
|
+
try options.log.move(.{ battle.active(player), state.move, battle.active(target), state.from });
|
|
573
549
|
|
|
574
550
|
const charging = false; // TODO charging uses doUse
|
|
575
551
|
const skip_pp = charging or state.move == .Struggle or
|
|
@@ -663,7 +639,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
663
639
|
}
|
|
664
640
|
assert(stage <= 4);
|
|
665
641
|
|
|
666
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
642
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
667
643
|
}
|
|
668
644
|
|
|
669
645
|
pub fn calcDamage(
|
|
@@ -731,7 +707,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
731
707
|
|
|
732
708
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
733
709
|
if (state.damage <= 1) return;
|
|
734
|
-
const random = try Rolls.damage(battle, player, options);
|
|
710
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
735
711
|
options.calc.base(player.foe(), state.damage);
|
|
736
712
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
737
713
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -849,13 +825,13 @@ fn checkFaint(
|
|
|
849
825
|
if (foe_fainted) try faint(battle, player.foe(), !more, options);
|
|
850
826
|
|
|
851
827
|
if (player_out and foe_out) {
|
|
852
|
-
try options.log.tie();
|
|
828
|
+
try options.log.tie(.{});
|
|
853
829
|
return Result.Tie;
|
|
854
830
|
} else if (player_out) {
|
|
855
|
-
try options.log.win(player.foe());
|
|
831
|
+
try options.log.win(.{player.foe()});
|
|
856
832
|
return if (player == .P1) Result.Lose else Result.Win;
|
|
857
833
|
} else if (foe_out) {
|
|
858
|
-
try options.log.win(player);
|
|
834
|
+
try options.log.win(.{player});
|
|
859
835
|
return if (player == .P1) Result.Win else Result.Lose;
|
|
860
836
|
}
|
|
861
837
|
|
|
@@ -885,19 +861,19 @@ fn faint(battle: anytype, player: Player, done: bool, options: anytype) !void {
|
|
|
885
861
|
side.stored().stats.spe;
|
|
886
862
|
}
|
|
887
863
|
|
|
888
|
-
try options.log.faint(battle.active(player), done);
|
|
864
|
+
try options.log.faint(.{ battle.active(player), done });
|
|
889
865
|
options.calc.capped(player);
|
|
890
866
|
}
|
|
891
867
|
|
|
892
868
|
// TODO: see if we can move *before* applyDamage and include failuretext logic
|
|
893
869
|
pub fn reportOutcome(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
894
870
|
const foe_ident = battle.active(player.foe());
|
|
895
|
-
if (state.crit) try options.log.crit(foe_ident);
|
|
871
|
+
if (state.crit) try options.log.crit(.{foe_ident});
|
|
896
872
|
if (!state.immune()) {
|
|
897
873
|
if (state.effectiveness > Effectiveness.neutral) {
|
|
898
|
-
try options.log.supereffective(foe_ident);
|
|
874
|
+
try options.log.supereffective(.{foe_ident});
|
|
899
875
|
} else if (state.effectiveness < Effectiveness.neutral) {
|
|
900
|
-
try options.log.resisted(foe_ident);
|
|
876
|
+
try options.log.resisted(.{foe_ident});
|
|
901
877
|
}
|
|
902
878
|
}
|
|
903
879
|
}
|
|
@@ -930,8 +906,8 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
930
906
|
}
|
|
931
907
|
|
|
932
908
|
stored.hp -|= damage;
|
|
933
|
-
// Pokémon Showdown uses
|
|
934
|
-
try options.log.damage(ident, stored, if (brn) Damage.Burn else Damage.Poison);
|
|
909
|
+
// Pokémon Showdown uses damage [of] here but its not relevant in Generation II
|
|
910
|
+
try options.log.damage(.{ ident, stored, if (brn) Damage.Burn else Damage.Poison });
|
|
935
911
|
if (stored.hp == 0) return true;
|
|
936
912
|
}
|
|
937
913
|
|
|
@@ -947,27 +923,27 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
947
923
|
|
|
948
924
|
const damage = @max(stored.stats.hp / 8, 1);
|
|
949
925
|
stored.hp -|= damage;
|
|
950
|
-
// As above, Pokémon Showdown uses
|
|
951
|
-
try options.log.damage(ident, stored, .LeechSeed);
|
|
926
|
+
// As above, Pokémon Showdown uses damage [of] but its not relevant
|
|
927
|
+
try options.log.damage(.{ ident, stored, .LeechSeed });
|
|
952
928
|
|
|
953
929
|
const before = foe_stored.hp;
|
|
954
930
|
foe_stored.hp = @min(foe_stored.hp + damage, foe_stored.stats.hp);
|
|
955
931
|
// Pokémon Showdown uses the less specific heal here instead of drain... because reasons?
|
|
956
|
-
if (foe_stored.hp > before) try options.log.heal(foe_ident, foe_stored, .Silent);
|
|
932
|
+
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored, .Silent });
|
|
957
933
|
if (stored.hp == 0) return true;
|
|
958
934
|
}
|
|
959
935
|
|
|
960
936
|
if (volatiles.Nightmare) {
|
|
961
937
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
962
938
|
// ibid
|
|
963
|
-
try options.log.damage(ident, stored, .Nightmare);
|
|
939
|
+
try options.log.damage(.{ ident, stored, .Nightmare });
|
|
964
940
|
if (stored.hp == 0) return true;
|
|
965
941
|
}
|
|
966
942
|
|
|
967
943
|
if (volatiles.Curse) {
|
|
968
944
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
969
945
|
// ibid
|
|
970
|
-
try options.log.damage(ident, stored, .Curse);
|
|
946
|
+
try options.log.damage(.{ ident, stored, .Curse });
|
|
971
947
|
if (stored.hp == 0) return true;
|
|
972
948
|
}
|
|
973
949
|
|
|
@@ -988,7 +964,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
988
964
|
future_sight.count -= 1;
|
|
989
965
|
assert(future_sight.count != 0);
|
|
990
966
|
if (future_sight.count == 1) {
|
|
991
|
-
try options.log.end(battle.active(player), .FutureSight);
|
|
967
|
+
try options.log.end(.{ battle.active(player), .FutureSight });
|
|
992
968
|
// TODO doMove
|
|
993
969
|
}
|
|
994
970
|
}
|
|
@@ -997,21 +973,24 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
997
973
|
|
|
998
974
|
// Weather
|
|
999
975
|
if (battle.field.weather != .None) {
|
|
1000
|
-
assert(battle.field.weather_duration > 0);
|
|
1001
976
|
battle.field.weather_duration -= 1;
|
|
1002
977
|
|
|
1003
978
|
if (battle.field.weather_duration == 0) {
|
|
1004
979
|
battle.field.weather = .None;
|
|
1005
|
-
try options.log.weather(battle.field.weather, .None);
|
|
980
|
+
try options.log.weather(.{ battle.field.weather, protocol.Weather.None });
|
|
1006
981
|
} else {
|
|
1007
|
-
try options.log.weather(battle.field.weather, .Upkeep);
|
|
982
|
+
try options.log.weather(.{ battle.field.weather, protocol.Weather.Upkeep });
|
|
1008
983
|
if (battle.field.weather == .Sandstorm) {
|
|
1009
984
|
inline for (players) |player| {
|
|
1010
985
|
var side = battle.side(player);
|
|
1011
986
|
const active = side.active;
|
|
1012
987
|
if (!(active.volatiles.Underground or active.types.sandstormImmune())) {
|
|
1013
988
|
side.stored().hp -|= @max(side.stored().stats.hp / 8, 1);
|
|
1014
|
-
try options.log.damage(
|
|
989
|
+
try options.log.damage(.{
|
|
990
|
+
battle.active(player),
|
|
991
|
+
side.stored(),
|
|
992
|
+
.Sandstorm,
|
|
993
|
+
});
|
|
1015
994
|
}
|
|
1016
995
|
}
|
|
1017
996
|
if (try checkFaint(battle, .P1, options)) |r| return r;
|
|
@@ -1023,19 +1002,24 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1023
1002
|
inline for (players) |player| {
|
|
1024
1003
|
var side = battle.side(player);
|
|
1025
1004
|
var volatiles = &side.active.volatiles;
|
|
1005
|
+
const ident = battle.active(player);
|
|
1026
1006
|
|
|
1027
1007
|
if (volatiles.bind.duration > 0 and !volatiles.Substitute) {
|
|
1028
1008
|
volatiles.bind.duration -= 1;
|
|
1029
1009
|
if (volatiles.bind.duration == 0) {
|
|
1030
1010
|
const reason = @intFromEnum(End.Bind) + volatiles.bind.reason - 1;
|
|
1031
|
-
try options.log.end(
|
|
1011
|
+
try options.log.end(.{ ident, @as(End, @enumFromInt(reason)) });
|
|
1032
1012
|
} else {
|
|
1033
1013
|
var reason = @intFromEnum(Activate.Bind) + volatiles.bind.reason - 1;
|
|
1034
|
-
try options.log.activate(
|
|
1014
|
+
try options.log.activate(.{ ident, @as(Activate, @enumFromInt(reason)) });
|
|
1035
1015
|
|
|
1036
1016
|
reason = @intFromEnum(Damage.Bind) + volatiles.bind.reason - 1;
|
|
1037
1017
|
side.stored().hp -|= @max(side.stored().stats.hp / 16, 1);
|
|
1038
|
-
try options.log.damage(
|
|
1018
|
+
try options.log.damage(.{
|
|
1019
|
+
ident,
|
|
1020
|
+
side.stored(),
|
|
1021
|
+
@as(Damage, @enumFromInt(reason)),
|
|
1022
|
+
});
|
|
1039
1023
|
}
|
|
1040
1024
|
}
|
|
1041
1025
|
}
|
|
@@ -1047,12 +1031,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1047
1031
|
var volatiles = &side.active.volatiles;
|
|
1048
1032
|
|
|
1049
1033
|
if (volatiles.PerishSong) {
|
|
1050
|
-
assert(volatiles.perish_song > 0);
|
|
1051
1034
|
volatiles.perish_song -= 1;
|
|
1052
1035
|
|
|
1053
1036
|
assert(volatiles.perish_song < 3);
|
|
1054
1037
|
const reason = @intFromEnum(Start.PerishSong0) + volatiles.perish_song;
|
|
1055
|
-
try options.log.start(battle.active(player), @enumFromInt(reason));
|
|
1038
|
+
try options.log.start(.{ battle.active(player), @as(Start, @enumFromInt(reason)) });
|
|
1056
1039
|
|
|
1057
1040
|
if (volatiles.perish_song == 0) {
|
|
1058
1041
|
volatiles.PerishSong = false;
|
|
@@ -1069,13 +1052,15 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1069
1052
|
if (stored.item == .Leftovers) {
|
|
1070
1053
|
const before = stored.hp;
|
|
1071
1054
|
stored.hp = @min(stored.hp + @max(stored.stats.hp / 16, 1), stored.stats.hp);
|
|
1072
|
-
if (stored.hp > before)
|
|
1055
|
+
if (stored.hp > before) {
|
|
1056
|
+
try options.log.heal(.{ battle.active(player), stored, .Leftovers });
|
|
1057
|
+
}
|
|
1073
1058
|
}
|
|
1074
1059
|
}
|
|
1075
1060
|
|
|
1076
1061
|
// Mystery Berry
|
|
1077
1062
|
inline for (players) |player| {
|
|
1078
|
-
|
|
1063
|
+
const stored = battle.side(player).stored();
|
|
1079
1064
|
|
|
1080
1065
|
if (stored.item == .MysteryBerry) {
|
|
1081
1066
|
// TODO stored and active moves, transform, etc
|
|
@@ -1090,7 +1075,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1090
1075
|
!side.active.volatiles.frozen and
|
|
1091
1076
|
try Rolls.defrost(battle, player, options))
|
|
1092
1077
|
{
|
|
1093
|
-
try options.log.curestatus(battle.active(player), side.stored().status, .Message);
|
|
1078
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1094
1079
|
side.stored().status = 0;
|
|
1095
1080
|
}
|
|
1096
1081
|
}
|
|
@@ -1131,14 +1116,14 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1131
1116
|
stored.hp < stored.stats.hp;
|
|
1132
1117
|
if (proc) {
|
|
1133
1118
|
assert(stored.hp != 0);
|
|
1134
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1119
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1135
1120
|
stored.item = .None;
|
|
1136
1121
|
|
|
1137
1122
|
const offset = num - @intFromEnum(Item.Berry);
|
|
1138
1123
|
assert(offset < 3);
|
|
1139
1124
|
stored.hp = @min(stored.hp + ((offset + 1) * 10), stored.stats.hp);
|
|
1140
1125
|
const reason: Heal = @enumFromInt(@intFromEnum(Heal.Berry) + offset);
|
|
1141
|
-
try options.log.heal(ident, stored, reason);
|
|
1126
|
+
try options.log.heal(.{ ident, stored, reason });
|
|
1142
1127
|
}
|
|
1143
1128
|
} else if (num < @intFromEnum(Item.BitterBerry)) {
|
|
1144
1129
|
const proc = if (stored.item == .MiracleBerry)
|
|
@@ -1148,7 +1133,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1148
1133
|
|
|
1149
1134
|
if (proc) {
|
|
1150
1135
|
assert(Status.any(stored.status));
|
|
1151
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1136
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1152
1137
|
stored.item = .None;
|
|
1153
1138
|
|
|
1154
1139
|
// FIXME wtf?
|
|
@@ -1156,21 +1141,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1156
1141
|
// side.active.volatiles.Nightmare = false;
|
|
1157
1142
|
if (stored.item == .MiracleBerry) {
|
|
1158
1143
|
side.active.volatiles.Confusion = false;
|
|
1159
|
-
try options.log.end(ident, .Confusion);
|
|
1144
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1160
1145
|
}
|
|
1161
1146
|
stored.status = 0;
|
|
1162
1147
|
|
|
1163
1148
|
// FIXME recalc stats
|
|
1164
1149
|
|
|
1165
|
-
try options.log.curestatus(ident, stored.status, .Message);
|
|
1150
|
+
try options.log.curestatus(.{ ident, stored.status, .Message });
|
|
1166
1151
|
}
|
|
1167
1152
|
} else if (num == @intFromEnum(Item.BitterBerry)) {
|
|
1168
1153
|
if (side.active.volatiles.Confusion) {
|
|
1169
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1154
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1170
1155
|
stored.item = .None;
|
|
1171
1156
|
|
|
1172
1157
|
side.active.volatiles.Confusion = false;
|
|
1173
|
-
try options.log.end(ident, .Confusion);
|
|
1158
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1174
1159
|
}
|
|
1175
1160
|
}
|
|
1176
1161
|
}
|
|
@@ -1182,12 +1167,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1182
1167
|
var volatiles = &side.active.volatiles;
|
|
1183
1168
|
|
|
1184
1169
|
if (volatiles.Encore) {
|
|
1185
|
-
assert(volatiles.encore > 0);
|
|
1186
1170
|
volatiles.encore -= 1;
|
|
1187
1171
|
|
|
1188
1172
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
1189
1173
|
volatiles.Encore = false;
|
|
1190
|
-
try options.log.end(battle.active(player), .Encore);
|
|
1174
|
+
try options.log.end(.{ battle.active(player), .Encore });
|
|
1191
1175
|
}
|
|
1192
1176
|
}
|
|
1193
1177
|
}
|
|
@@ -1204,12 +1188,11 @@ fn updateSideCondition(
|
|
|
1204
1188
|
) !void {
|
|
1205
1189
|
var side = battle.side(player);
|
|
1206
1190
|
if (@field(side.conditions, key)) {
|
|
1207
|
-
assert(@field(side.conditions, value) > 0);
|
|
1208
1191
|
@field(side.conditions, value) -= 1;
|
|
1209
1192
|
|
|
1210
1193
|
if (@field(side.conditions, value) == 0) {
|
|
1211
1194
|
@field(side.conditions, key) = false;
|
|
1212
|
-
try options.log.sideend(player, @field(protocol.Side, key));
|
|
1195
|
+
try options.log.sideend(.{ player, @field(protocol.Side, key) });
|
|
1213
1196
|
}
|
|
1214
1197
|
}
|
|
1215
1198
|
}
|
|
@@ -1218,13 +1201,13 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
|
|
|
1218
1201
|
battle.turn += 1;
|
|
1219
1202
|
|
|
1220
1203
|
if (showdown and battle.turn >= 1000) {
|
|
1221
|
-
try options.log.tie();
|
|
1204
|
+
try options.log.tie(.{});
|
|
1222
1205
|
return Result.Tie;
|
|
1223
1206
|
} else if (battle.turn >= 65535) {
|
|
1224
1207
|
return Result.Error;
|
|
1225
1208
|
}
|
|
1226
1209
|
|
|
1227
|
-
try options.log.turn(battle.turn);
|
|
1210
|
+
try options.log.turn(.{battle.turn});
|
|
1228
1211
|
|
|
1229
1212
|
return Result.Default;
|
|
1230
1213
|
}
|
|
@@ -1240,7 +1223,7 @@ pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytyp
|
|
|
1240
1223
|
}
|
|
1241
1224
|
} else {
|
|
1242
1225
|
foe.active.volatiles.rage +|= 1;
|
|
1243
|
-
try options.log.activate(battle.active(player), .Rage);
|
|
1226
|
+
try options.log.activate(.{ battle.active(player), .Rage });
|
|
1244
1227
|
}
|
|
1245
1228
|
}
|
|
1246
1229
|
}
|
|
@@ -1263,7 +1246,7 @@ pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype)
|
|
|
1263
1246
|
|
|
1264
1247
|
if (foe.active.volatiles.DestinyBond) {
|
|
1265
1248
|
battle.side(player).stored().hp = 0;
|
|
1266
|
-
try options.log.activate(battle.active(player.foe()), .DestinyBond);
|
|
1249
|
+
try options.log.activate(.{ battle.active(player.foe()), .DestinyBond });
|
|
1267
1250
|
}
|
|
1268
1251
|
return true;
|
|
1269
1252
|
}
|
|
@@ -1276,14 +1259,14 @@ pub const Effects = struct {
|
|
|
1276
1259
|
const gender: u8 = @intFromEnum(side.stored().dvs.gender);
|
|
1277
1260
|
const foe_gender: u8 = @intFromEnum(foe.stored().dvs.gender);
|
|
1278
1261
|
if (gender + foe_gender != 1) {
|
|
1279
|
-
return options.log.immune(battle.active(player.foe()), .None);
|
|
1262
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1280
1263
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1281
|
-
try options.log.lastmiss();
|
|
1282
|
-
return options.log.miss(battle.active(player));
|
|
1264
|
+
try options.log.lastmiss(.{});
|
|
1265
|
+
return options.log.miss(.{battle.active(player)});
|
|
1283
1266
|
}
|
|
1284
1267
|
|
|
1285
1268
|
foe.active.volatiles.Attract = true;
|
|
1286
|
-
try options.log.start(battle.active(player.foe()), .Attract);
|
|
1269
|
+
try options.log.start(.{ battle.active(player.foe()), .Attract });
|
|
1287
1270
|
}
|
|
1288
1271
|
|
|
1289
1272
|
pub fn batonPass(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1295,14 +1278,32 @@ pub const Effects = struct {
|
|
|
1295
1278
|
}
|
|
1296
1279
|
|
|
1297
1280
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1281
|
+
assert(!state.miss);
|
|
1282
|
+
var side = battle.side(player);
|
|
1283
|
+
|
|
1284
|
+
const ident = battle.active(player);
|
|
1285
|
+
|
|
1286
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1287
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1288
|
+
return options.log.fail(.{ ident, .None });
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1292
|
+
if (side.stored().hp <= hp) {
|
|
1293
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1294
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1295
|
+
return options.log.fail(.{ ident, .None });
|
|
1296
|
+
} else {
|
|
1297
|
+
side.stored().hp -= hp;
|
|
1298
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1299
|
+
|
|
1300
|
+
const before = side.active.boosts.atk;
|
|
1301
|
+
var i: usize = 0;
|
|
1302
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1303
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1304
|
+
}
|
|
1305
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1306
|
+
}
|
|
1306
1307
|
}
|
|
1307
1308
|
|
|
1308
1309
|
pub const bide = struct {
|
|
@@ -1314,18 +1315,11 @@ pub const Effects = struct {
|
|
|
1314
1315
|
|
|
1315
1316
|
if (!volatiles.Bide) return;
|
|
1316
1317
|
|
|
1317
|
-
assert(volatiles.attacks > 0);
|
|
1318
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1319
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1320
|
-
// } else {
|
|
1321
1318
|
volatiles.attacks -= 1;
|
|
1322
|
-
|
|
1323
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1324
|
-
|
|
1325
|
-
if (volatiles.attacks != 0) return options.log.activate(ident, .Bide);
|
|
1319
|
+
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1326
1320
|
|
|
1327
1321
|
volatiles.Bide = false;
|
|
1328
|
-
try options.log.end(ident, .Bide);
|
|
1322
|
+
try options.log.end(.{ ident, .Bide });
|
|
1329
1323
|
|
|
1330
1324
|
state.damage = volatiles.bide *% 2;
|
|
1331
1325
|
volatiles.bide = 0;
|
|
@@ -1359,11 +1353,11 @@ pub const Effects = struct {
|
|
|
1359
1353
|
if (Status.any(foe_stored.status)) {
|
|
1360
1354
|
if (Status.is(foe_stored.status, .FRZ)) {
|
|
1361
1355
|
assert(Move.get(state.move).type == .Fire or state.move == .TriAttack);
|
|
1362
|
-
try options.log.curestatus(
|
|
1356
|
+
try options.log.curestatus(.{
|
|
1363
1357
|
battle.active(player.foe()),
|
|
1364
1358
|
foe_stored.status,
|
|
1365
1359
|
.Message,
|
|
1366
|
-
);
|
|
1360
|
+
});
|
|
1367
1361
|
foe_stored.status = 0;
|
|
1368
1362
|
}
|
|
1369
1363
|
return;
|
|
@@ -1378,7 +1372,8 @@ pub const Effects = struct {
|
|
|
1378
1372
|
|
|
1379
1373
|
foe_stored.status = Status.init(.BRN);
|
|
1380
1374
|
foe.active.stats.atk = @max(foe.active.stats.atk / 2, 1);
|
|
1381
|
-
|
|
1375
|
+
|
|
1376
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
1382
1377
|
|
|
1383
1378
|
// TODO: check for status berry
|
|
1384
1379
|
}
|
|
@@ -1398,7 +1393,7 @@ pub const Effects = struct {
|
|
|
1398
1393
|
for (side.active.moves) |m| {
|
|
1399
1394
|
if (convertible(side.active, m.id)) n += 1;
|
|
1400
1395
|
}
|
|
1401
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
1396
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1402
1397
|
|
|
1403
1398
|
side.active.types.type1 = try Rolls.conversion(battle, player, n, options);
|
|
1404
1399
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1408,9 +1403,9 @@ pub const Effects = struct {
|
|
|
1408
1403
|
var side = battle.side(player);
|
|
1409
1404
|
|
|
1410
1405
|
const last = battle.foe(player).lastMove(false);
|
|
1411
|
-
if (last == .None) return options.log.fail(battle.active(player), .None);
|
|
1406
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1412
1407
|
const move = Move.get(last);
|
|
1413
|
-
if (move.type == .@"???") return options.log.fail(battle.active(player), .None);
|
|
1408
|
+
if (move.type == .@"???") return options.log.fail(.{ battle.active(player), .None });
|
|
1414
1409
|
|
|
1415
1410
|
side.active.types.type1 = try Rolls.conversion2(battle, player, move.type, options);
|
|
1416
1411
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1452,41 +1447,41 @@ pub const Effects = struct {
|
|
|
1452
1447
|
|
|
1453
1448
|
if (side.active.types.includes(.Ghost)) {
|
|
1454
1449
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1455
|
-
try options.log.lastmiss();
|
|
1456
|
-
return options.log.miss(battle.active(player));
|
|
1450
|
+
try options.log.lastmiss(.{});
|
|
1451
|
+
return options.log.miss(.{battle.active(player)});
|
|
1457
1452
|
} else if (foe.active.volatiles.Substitute) {
|
|
1458
|
-
return options.log.
|
|
1453
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1459
1454
|
} else if (foe.active.volatiles.Curse) {
|
|
1460
|
-
return options.log.fail(foe_ident, .None);
|
|
1455
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1461
1456
|
}
|
|
1462
1457
|
|
|
1463
1458
|
foe.active.volatiles.Curse = true;
|
|
1464
|
-
try options.log.start(foe_ident, .Curse);
|
|
1459
|
+
try options.log.start(.{ foe_ident, .Curse });
|
|
1465
1460
|
|
|
1466
1461
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1467
|
-
try options.log.damage(ident, side.stored(), .Curse);
|
|
1462
|
+
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1468
1463
|
} else {
|
|
1469
|
-
|
|
1470
|
-
|
|
1464
|
+
const stats = &side.active.stats;
|
|
1465
|
+
const boosts = &side.active.boosts;
|
|
1471
1466
|
|
|
1472
1467
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1473
1468
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
1474
|
-
if (max_atk and max_def) return options.log.fail(ident, .None);
|
|
1469
|
+
if (max_atk and max_def) return options.log.fail(.{ ident, .None });
|
|
1475
1470
|
|
|
1476
1471
|
if (!(boosts.spe == -6 or stats.spe == 1)) {
|
|
1477
1472
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
1478
1473
|
try unboostStat(battle, side, stats, boosts, "spe", 1);
|
|
1479
|
-
try options.log.boost(ident, .Speed, @as(i8, -1));
|
|
1474
|
+
try options.log.boost(.{ ident, .Speed, @as(i8, -1) });
|
|
1480
1475
|
}
|
|
1481
1476
|
if (!max_atk) {
|
|
1482
1477
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
1483
1478
|
try boostStat(battle, side, stats, boosts, "atk", 1);
|
|
1484
|
-
try options.log.boost(ident, .Attack, @as(i8, 1));
|
|
1479
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 1) });
|
|
1485
1480
|
}
|
|
1486
1481
|
if (!max_def) {
|
|
1487
1482
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
1488
1483
|
try boostStat(battle, side, stats, boosts, "def", 1);
|
|
1489
|
-
try options.log.boost(ident, .Defense, @as(i8, 1));
|
|
1484
|
+
try options.log.boost(.{ ident, .Defense, @as(i8, 1) });
|
|
1490
1485
|
}
|
|
1491
1486
|
}
|
|
1492
1487
|
}
|
|
@@ -1498,14 +1493,13 @@ pub const Effects = struct {
|
|
|
1498
1493
|
pub fn defrost(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1499
1494
|
var side = battle.side(player);
|
|
1500
1495
|
assert(Status.is(side.stored().status, .FRZ));
|
|
1501
|
-
|
|
1502
|
-
try options.log.curestatus(battle.active(player), side.stored().status, .Message);
|
|
1496
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1503
1497
|
side.stored().status = 0;
|
|
1504
1498
|
}
|
|
1505
1499
|
|
|
1506
1500
|
pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1507
1501
|
battle.side(player).active.volatiles.DestinyBond = true;
|
|
1508
|
-
try options.log.singlemove(battle.active(player), Move.DestinyBond);
|
|
1502
|
+
try options.log.singlemove(.{ battle.active(player), Move.DestinyBond });
|
|
1509
1503
|
}
|
|
1510
1504
|
|
|
1511
1505
|
pub fn disable(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -1513,14 +1507,10 @@ pub const Effects = struct {
|
|
|
1513
1507
|
var volatiles = &foe.active.volatiles;
|
|
1514
1508
|
const foe_ident = battle.active(player.foe());
|
|
1515
1509
|
|
|
1516
|
-
if (volatiles.disable.move != 0) {
|
|
1517
|
-
return options.log.fail(foe_ident, .None);
|
|
1518
|
-
}
|
|
1510
|
+
if (volatiles.disable.move != 0) return options.log.fail(.{ foe_ident, .None });
|
|
1519
1511
|
|
|
1520
1512
|
const last = battle.foe(player).lastMove(false);
|
|
1521
|
-
if (last == .None or last == .Struggle) {
|
|
1522
|
-
return options.log.fail(foe_ident, .None);
|
|
1523
|
-
}
|
|
1513
|
+
if (last == .None or last == .Struggle) return options.log.fail(.{ foe_ident, .None });
|
|
1524
1514
|
|
|
1525
1515
|
var slot: u3 = 0;
|
|
1526
1516
|
for (foe.active.moves) |m| {
|
|
@@ -1529,12 +1519,12 @@ pub const Effects = struct {
|
|
|
1529
1519
|
if (m.id == last) break;
|
|
1530
1520
|
}
|
|
1531
1521
|
const move = foe.active.move(slot);
|
|
1532
|
-
if (move.pp == 0) return options.log.fail(foe_ident, .None);
|
|
1522
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1533
1523
|
|
|
1534
1524
|
volatiles.disable.move = slot;
|
|
1535
1525
|
volatiles.disable.duration = Rolls.disableDuration(battle, player, options);
|
|
1536
1526
|
|
|
1537
|
-
try options.log.
|
|
1527
|
+
try options.log.start(.{ foe_ident, .Disable, move.id });
|
|
1538
1528
|
}
|
|
1539
1529
|
|
|
1540
1530
|
pub const double = struct {
|
|
@@ -1553,13 +1543,14 @@ pub const Effects = struct {
|
|
|
1553
1543
|
|
|
1554
1544
|
pub fn drainHP(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1555
1545
|
var stored = battle.side(player).stored();
|
|
1546
|
+
const ident = battle.active(player);
|
|
1556
1547
|
|
|
1557
1548
|
assert(state.damage != 0);
|
|
1558
1549
|
|
|
1559
1550
|
if (stored.hp == stored.stats.hp) return;
|
|
1560
1551
|
stored.hp = @min(stored.stats.hp, stored.hp + @max(state.damage / 2, 1));
|
|
1561
1552
|
|
|
1562
|
-
try options.log.
|
|
1553
|
+
try options.log.heal(.{ ident, stored, .Drain, battle.active(player.foe()) });
|
|
1563
1554
|
}
|
|
1564
1555
|
|
|
1565
1556
|
pub fn encore(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1571,7 +1562,7 @@ pub const Effects = struct {
|
|
|
1571
1562
|
const failed = volatiles.Encore or
|
|
1572
1563
|
(last == .None or last == .Struggle or
|
|
1573
1564
|
last == .Encore or .last == .MirrorMove);
|
|
1574
|
-
if (failed) return options.log.fail(foe_ident, .None);
|
|
1565
|
+
if (failed) return options.log.fail(.{ foe_ident, .None });
|
|
1575
1566
|
|
|
1576
1567
|
var slot: u3 = 0;
|
|
1577
1568
|
for (foe.active.moves) |m| {
|
|
@@ -1580,7 +1571,7 @@ pub const Effects = struct {
|
|
|
1580
1571
|
if (m.id == last) break;
|
|
1581
1572
|
}
|
|
1582
1573
|
const move = foe.active.move(slot);
|
|
1583
|
-
if (move.pp == 0) return options.log.fail(foe_ident, .None);
|
|
1574
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1584
1575
|
|
|
1585
1576
|
volatiles.Encore = true;
|
|
1586
1577
|
volatiles.encore = Rolls.encoreDuration(battle, player, options);
|
|
@@ -1588,11 +1579,19 @@ pub const Effects = struct {
|
|
|
1588
1579
|
// TODO ???
|
|
1589
1580
|
_ = state;
|
|
1590
1581
|
|
|
1591
|
-
try options.log.
|
|
1582
|
+
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1592
1583
|
}
|
|
1593
1584
|
|
|
1594
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1595
|
-
|
|
1585
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1586
|
+
var side = battle.side(player);
|
|
1587
|
+
var stored = side.stored();
|
|
1588
|
+
|
|
1589
|
+
stored.hp = 0;
|
|
1590
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1591
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1592
|
+
if (!showdown) stored.status = 0;
|
|
1593
|
+
side.active.volatiles.LeechSeed = false;
|
|
1594
|
+
side.active.volatiles.DestinyBond = false;
|
|
1596
1595
|
}
|
|
1597
1596
|
|
|
1598
1597
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1642,12 +1641,11 @@ pub const Effects = struct {
|
|
|
1642
1641
|
var side = battle.side(player);
|
|
1643
1642
|
|
|
1644
1643
|
if (side.active.volatiles.FocusEnergy) {
|
|
1645
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
1646
|
-
return;
|
|
1644
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
1647
1645
|
}
|
|
1648
1646
|
|
|
1649
1647
|
side.active.volatiles.FocusEnergy = true;
|
|
1650
|
-
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1648
|
+
try options.log.start(.{ battle.active(player), .FocusEnergy });
|
|
1651
1649
|
}
|
|
1652
1650
|
|
|
1653
1651
|
var SLOTS: [5]u4 = .{ 0, 0, 0, 0, 0 };
|
|
@@ -1657,7 +1655,7 @@ pub const Effects = struct {
|
|
|
1657
1655
|
|
|
1658
1656
|
const foe = battle.foe(player);
|
|
1659
1657
|
|
|
1660
|
-
if (state.first) return options.log.fail(battle.active(player), .None);
|
|
1658
|
+
if (state.first) return options.log.fail(.{ battle.active(player), .None });
|
|
1661
1659
|
|
|
1662
1660
|
var n: u4 = 0;
|
|
1663
1661
|
var i: u4 = 0;
|
|
@@ -1671,7 +1669,7 @@ pub const Effects = struct {
|
|
|
1671
1669
|
}
|
|
1672
1670
|
}
|
|
1673
1671
|
|
|
1674
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
1672
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1675
1673
|
|
|
1676
1674
|
const slot = try Rolls.forceSwitch(battle, player, SLOTS[0..i], n, options);
|
|
1677
1675
|
try switchIn(battle, player.foe(), slot, .Drag, options);
|
|
@@ -1681,14 +1679,14 @@ pub const Effects = struct {
|
|
|
1681
1679
|
var foe = battle.foe(player);
|
|
1682
1680
|
|
|
1683
1681
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1684
|
-
try options.log.lastmiss();
|
|
1685
|
-
return options.log.miss(battle.active(player));
|
|
1682
|
+
try options.log.lastmiss(.{});
|
|
1683
|
+
return options.log.miss(.{battle.active(player)});
|
|
1686
1684
|
} else if (foe.active.volatiles.Foresight) {
|
|
1687
|
-
return options.log.fail(battle.active(player), .None);
|
|
1685
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
1688
1686
|
}
|
|
1689
1687
|
|
|
1690
1688
|
foe.active.volatiles.Foresight = true;
|
|
1691
|
-
try options.log.start(battle.active(player.foe()), .FocusEnergy);
|
|
1689
|
+
try options.log.start(.{ battle.active(player.foe()), .FocusEnergy });
|
|
1692
1690
|
}
|
|
1693
1691
|
|
|
1694
1692
|
pub fn freezeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1711,7 +1709,7 @@ pub const Effects = struct {
|
|
|
1711
1709
|
if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1712
1710
|
|
|
1713
1711
|
foe_stored.status = Status.init(.FRZ);
|
|
1714
|
-
try options.log.status(foe_ident, foe_stored.status, .None);
|
|
1712
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
1715
1713
|
|
|
1716
1714
|
// TODO: check for status berry
|
|
1717
1715
|
|
|
@@ -1719,8 +1717,26 @@ pub const Effects = struct {
|
|
|
1719
1717
|
foe.active.volatiles.frozen = true;
|
|
1720
1718
|
}
|
|
1721
1719
|
|
|
1722
|
-
|
|
1723
|
-
|
|
1720
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1721
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1722
|
+
var side = battle.side(player);
|
|
1723
|
+
var volatiles = &side.active.volatiles;
|
|
1724
|
+
|
|
1725
|
+
if (state.miss) {
|
|
1726
|
+
volatiles.fury_cutter = 0;
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1731
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1732
|
+
volatiles.fury_cutter += 1;
|
|
1733
|
+
|
|
1734
|
+
if (showdown) {
|
|
1735
|
+
state.bp *= 2;
|
|
1736
|
+
assert(state.bp <= 160);
|
|
1737
|
+
} else {
|
|
1738
|
+
state.damage *|= 2;
|
|
1739
|
+
}
|
|
1724
1740
|
}
|
|
1725
1741
|
|
|
1726
1742
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1746,7 +1762,7 @@ pub const Effects = struct {
|
|
|
1746
1762
|
inline for (STATS) |s| @field(foe.active.stats, s) = @field(stats, s);
|
|
1747
1763
|
statusModify(foe.stored().status, &foe.active.stats);
|
|
1748
1764
|
|
|
1749
|
-
try options.log.clearallboost();
|
|
1765
|
+
try options.log.clearallboost(.{});
|
|
1750
1766
|
}
|
|
1751
1767
|
|
|
1752
1768
|
pub fn heal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1754,7 +1770,7 @@ pub const Effects = struct {
|
|
|
1754
1770
|
var stored = side.stored();
|
|
1755
1771
|
const ident = battle.active(player);
|
|
1756
1772
|
|
|
1757
|
-
if (stored.stats.hp == stored.hp) return options.log.fail(ident, .None);
|
|
1773
|
+
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident, .None });
|
|
1758
1774
|
|
|
1759
1775
|
const rest = state.move == .Rest;
|
|
1760
1776
|
if (rest) {
|
|
@@ -1763,19 +1779,19 @@ pub const Effects = struct {
|
|
|
1763
1779
|
side.active.volatiles.Toxic = false;
|
|
1764
1780
|
stored.status = Status.slf(2);
|
|
1765
1781
|
recomputeStats(battle, side);
|
|
1766
|
-
try options.log.
|
|
1782
|
+
try options.log.status(.{ ident, stored.status, .From, Move.Rest });
|
|
1767
1783
|
stored.hp = stored.stats.hp;
|
|
1768
1784
|
} else {
|
|
1769
1785
|
stored.hp = @min(stored.stats.hp, stored.hp + (stored.stats.hp / 2));
|
|
1770
1786
|
}
|
|
1771
|
-
try options.log.heal(ident, stored, if (rest) Heal.Silent else Heal.None);
|
|
1787
|
+
try options.log.heal(.{ ident, stored, if (rest) Heal.Silent else Heal.None });
|
|
1772
1788
|
}
|
|
1773
1789
|
|
|
1774
1790
|
pub fn healBell(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1775
1791
|
var side = battle.side(player);
|
|
1776
1792
|
side.active.volatiles.Nightmare = false;
|
|
1777
1793
|
|
|
1778
|
-
try options.log.cureteam(battle.active(player));
|
|
1794
|
+
try options.log.cureteam(.{battle.active(player)});
|
|
1779
1795
|
for (&side.pokemon) |*p| p.status = 0;
|
|
1780
1796
|
|
|
1781
1797
|
// We technically only need to recompute stats if the active Pokémon was burned/paralyzed,
|
|
@@ -1791,7 +1807,7 @@ pub const Effects = struct {
|
|
|
1791
1807
|
|
|
1792
1808
|
pub fn hyperBeam(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1793
1809
|
battle.side(player).active.volatiles.Recharging = true;
|
|
1794
|
-
try options.log.mustrecharge(battle.active(player));
|
|
1810
|
+
try options.log.mustrecharge(.{battle.active(player)});
|
|
1795
1811
|
}
|
|
1796
1812
|
|
|
1797
1813
|
pub fn leechSeed(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1801,12 +1817,12 @@ pub const Effects = struct {
|
|
|
1801
1817
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
1802
1818
|
const invulnerable = foe.active.volatiles.Underground or foe.active.volatiles.Flying;
|
|
1803
1819
|
if (!invulnerable and foe.active.types.includes(.Grass)) {
|
|
1804
|
-
return options.log.immune(battle.active(player.foe()), .None);
|
|
1820
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1805
1821
|
}
|
|
1806
1822
|
try checkHit(battle, player, state, options);
|
|
1807
1823
|
if (state.miss) {
|
|
1808
|
-
try options.log.lastmiss();
|
|
1809
|
-
return options.log.miss(battle.active(player));
|
|
1824
|
+
try options.log.lastmiss(.{});
|
|
1825
|
+
return options.log.miss(.{battle.active(player)});
|
|
1810
1826
|
}
|
|
1811
1827
|
if (foe.active.volatiles.LeechSeed) return;
|
|
1812
1828
|
} else {
|
|
@@ -1814,14 +1830,14 @@ pub const Effects = struct {
|
|
|
1814
1830
|
const miss =
|
|
1815
1831
|
state.miss or foe.active.types.includes(.Grass) or foe.active.volatiles.LeechSeed;
|
|
1816
1832
|
if (miss) {
|
|
1817
|
-
try options.log.lastmiss();
|
|
1818
|
-
return options.log.miss(battle.active(player));
|
|
1833
|
+
try options.log.lastmiss(.{});
|
|
1834
|
+
return options.log.miss(.{battle.active(player)});
|
|
1819
1835
|
}
|
|
1820
1836
|
}
|
|
1821
1837
|
|
|
1822
1838
|
foe.active.volatiles.LeechSeed = true;
|
|
1823
1839
|
|
|
1824
|
-
try options.log.start(battle.active(player.foe()), .LeechSeed);
|
|
1840
|
+
try options.log.start(.{ battle.active(player.foe()), .LeechSeed });
|
|
1825
1841
|
}
|
|
1826
1842
|
|
|
1827
1843
|
pub fn lockOn(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1829,20 +1845,20 @@ pub const Effects = struct {
|
|
|
1829
1845
|
const foe_ident = battle.active(player.foe());
|
|
1830
1846
|
|
|
1831
1847
|
if (foe.active.volatiles.Substitute) {
|
|
1832
|
-
return options.log.
|
|
1848
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1833
1849
|
} else if (state.miss) {
|
|
1834
|
-
return options.log.fail(foe_ident, .None);
|
|
1850
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1835
1851
|
}
|
|
1836
1852
|
|
|
1837
1853
|
foe.active.volatiles.LockOn = true;
|
|
1838
|
-
try options.log.activate(battle.active(player), .LockOn);
|
|
1854
|
+
try options.log.activate(.{ battle.active(player), .LockOn });
|
|
1839
1855
|
}
|
|
1840
1856
|
|
|
1841
1857
|
const MAGNITUDE_POWER = [_]u8{ 10, 30, 50, 70, 90, 110, 150 };
|
|
1842
1858
|
|
|
1843
1859
|
pub fn magnitude(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1844
1860
|
const num = try Rolls.magnitude(battle, player, options);
|
|
1845
|
-
try options.log.
|
|
1861
|
+
try options.log.activate(.{ battle.active(player), .Magnitude, num });
|
|
1846
1862
|
state.bp = MAGNITUDE_POWER[num - 4];
|
|
1847
1863
|
}
|
|
1848
1864
|
|
|
@@ -1850,11 +1866,11 @@ pub const Effects = struct {
|
|
|
1850
1866
|
var foe = battle.foe(player);
|
|
1851
1867
|
|
|
1852
1868
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1853
|
-
if (!showdown) return options.log.fail(battle.active(player.foe()), .None);
|
|
1854
|
-
try options.log.lastmiss();
|
|
1855
|
-
return options.log.miss(battle.active(player));
|
|
1869
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1870
|
+
try options.log.lastmiss(.{});
|
|
1871
|
+
return options.log.miss(.{battle.active(player)});
|
|
1856
1872
|
} else if (foe.active.volatiles.Trapped) {
|
|
1857
|
-
return options.log.fail(battle.active(player.foe()), .None);
|
|
1873
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1858
1874
|
}
|
|
1859
1875
|
|
|
1860
1876
|
foe.active.volatiles.Trapped = true;
|
|
@@ -1881,7 +1897,29 @@ pub const Effects = struct {
|
|
|
1881
1897
|
}
|
|
1882
1898
|
|
|
1883
1899
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1884
|
-
|
|
1900
|
+
var side = battle.side(player);
|
|
1901
|
+
|
|
1902
|
+
if (state.miss) {
|
|
1903
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1904
|
+
try options.log.lastmiss(.{});
|
|
1905
|
+
return options.log.miss(.{battle.active(player)});
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
const move = battle.foe(player).lastMove(false);
|
|
1909
|
+
if (move == .None or move == .Struggle) {
|
|
1910
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
var slot: u4 = 0;
|
|
1914
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1915
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1916
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1917
|
+
}
|
|
1918
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1919
|
+
|
|
1920
|
+
side.active.move(slot).id = move;
|
|
1921
|
+
side.active.move(slot).pp = 5;
|
|
1922
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1885
1923
|
}
|
|
1886
1924
|
|
|
1887
1925
|
pub fn mirrorMove(
|
|
@@ -1898,11 +1936,11 @@ pub const Effects = struct {
|
|
|
1898
1936
|
|
|
1899
1937
|
const last = foe.lastMove(false);
|
|
1900
1938
|
|
|
1901
|
-
if (last == .None) return options.log.fail(battle.active(player), .None);
|
|
1939
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1902
1940
|
const knows = knows: for (side.active.moves) |m| {
|
|
1903
1941
|
if (last == m.id) break :knows true;
|
|
1904
1942
|
} else false;
|
|
1905
|
-
if (knows) return options.log.fail(battle.active(player), .None);
|
|
1943
|
+
if (knows) return options.log.fail(.{ battle.active(player), .None });
|
|
1906
1944
|
|
|
1907
1945
|
// if (wPlayerCharging) ???
|
|
1908
1946
|
// wPlayerCharging = 1
|
|
@@ -1916,17 +1954,17 @@ pub const Effects = struct {
|
|
|
1916
1954
|
pub fn mist(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1917
1955
|
var side = battle.side(player);
|
|
1918
1956
|
|
|
1919
|
-
if (side.active.volatiles.Mist) return options.log.fail(battle.active(player), .None);
|
|
1957
|
+
if (side.active.volatiles.Mist) return options.log.fail(.{ battle.active(player), .None });
|
|
1920
1958
|
side.active.volatiles.Mist = true;
|
|
1921
1959
|
|
|
1922
|
-
try options.log.start(battle.active(player), .Mist);
|
|
1960
|
+
try options.log.start(.{ battle.active(player), .Mist });
|
|
1923
1961
|
}
|
|
1924
1962
|
|
|
1925
1963
|
pub fn weatherHeal(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1926
1964
|
var stored = battle.side(player).stored();
|
|
1927
1965
|
const ident = battle.active(player);
|
|
1928
1966
|
|
|
1929
|
-
if (stored.hp == stored.stats.hp) return options.log.fail(ident, .None);
|
|
1967
|
+
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident, .None });
|
|
1930
1968
|
|
|
1931
1969
|
stored.hp = switch (battle.field.weather) {
|
|
1932
1970
|
.Sun => stored.stats.hp,
|
|
@@ -1934,7 +1972,7 @@ pub const Effects = struct {
|
|
|
1934
1972
|
else => @min(stored.stats.hp, stored.hp + (stored.stats.hp / 4)),
|
|
1935
1973
|
};
|
|
1936
1974
|
|
|
1937
|
-
try options.log.heal(ident, stored,
|
|
1975
|
+
try options.log.heal(.{ ident, stored, .None });
|
|
1938
1976
|
}
|
|
1939
1977
|
|
|
1940
1978
|
pub fn multiHit(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1947,17 +1985,17 @@ pub const Effects = struct {
|
|
|
1947
1985
|
const foe_ident = battle.active(player.foe());
|
|
1948
1986
|
|
|
1949
1987
|
if (foe_volatiles.Flying or foe_volatiles.Underground) {
|
|
1950
|
-
if (!showdown) return options.log.fail(battle.active(player.foe()), .None);
|
|
1951
|
-
try options.log.lastmiss();
|
|
1952
|
-
return options.log.miss(battle.active(player));
|
|
1988
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1989
|
+
try options.log.lastmiss(.{});
|
|
1990
|
+
return options.log.miss(.{battle.active(player)});
|
|
1953
1991
|
} else if (foe_volatiles.Substitute) {
|
|
1954
|
-
return options.log.
|
|
1992
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1955
1993
|
} else if (!Status.is(foe.stored().status, .SLP) or foe_volatiles.Nightmare) {
|
|
1956
|
-
return options.log.fail(foe_ident, .None);
|
|
1994
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1957
1995
|
}
|
|
1958
1996
|
|
|
1959
1997
|
foe_volatiles.Nightmare = true;
|
|
1960
|
-
try options.log.start(battle.active(player.foe()), .Nightmare);
|
|
1998
|
+
try options.log.start(.{ battle.active(player.foe()), .Nightmare });
|
|
1961
1999
|
}
|
|
1962
2000
|
|
|
1963
2001
|
pub const ohko = struct {
|
|
@@ -1981,9 +2019,9 @@ pub const Effects = struct {
|
|
|
1981
2019
|
const foe_ident = battle.active(player.foe());
|
|
1982
2020
|
|
|
1983
2021
|
state.damage = 0;
|
|
1984
|
-
if (state.immune()) return options.log.immune(foe_ident, .None);
|
|
2022
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
1985
2023
|
const delta = side.stored().level -| foe.stored().level;
|
|
1986
|
-
if (delta == 0) return options.log.immune(foe_ident, .OHKO);
|
|
2024
|
+
if (delta == 0) return options.log.immune(.{ foe_ident, .OHKO });
|
|
1987
2025
|
|
|
1988
2026
|
state.accuracy = 2 * delta +| Gen12.percent(30);
|
|
1989
2027
|
try checkHit(battle, player, state, options);
|
|
@@ -1999,37 +2037,39 @@ pub const Effects = struct {
|
|
|
1999
2037
|
const ident = battle.active(player);
|
|
2000
2038
|
const foe_ident = battle.active(player.foe());
|
|
2001
2039
|
|
|
2002
|
-
if (state.miss) return options.log.fail(foe_ident, .None);
|
|
2040
|
+
if (state.miss) return options.log.fail(.{ foe_ident, .None });
|
|
2003
2041
|
if (foe.active.volatiles.Substitute) {
|
|
2004
|
-
return options.log.
|
|
2042
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2005
2043
|
}
|
|
2006
2044
|
|
|
2007
2045
|
const avg = @max((foe.stored().hp + side.stored().hp) / 2, 1);
|
|
2008
2046
|
foe.stored().hp -= avg;
|
|
2009
2047
|
side.stored().hp = avg;
|
|
2010
2048
|
|
|
2011
|
-
try options.log.sethp(foe_ident, foe.stored(), .Silent);
|
|
2012
|
-
try options.log.sethp(ident, side.stored(), .None);
|
|
2049
|
+
try options.log.sethp(.{ foe_ident, foe.stored(), .Silent });
|
|
2050
|
+
try options.log.sethp(.{ ident, side.stored(), .None });
|
|
2013
2051
|
}
|
|
2014
2052
|
|
|
2015
2053
|
pub fn paralyze(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2016
2054
|
var foe = battle.foe(player);
|
|
2017
2055
|
const foe_ident = battle.active(player.foe());
|
|
2018
2056
|
|
|
2019
|
-
if (Status.is(foe.stored().status, .PAR))
|
|
2020
|
-
|
|
2021
|
-
|
|
2057
|
+
if (Status.is(foe.stored().status, .PAR)) {
|
|
2058
|
+
return options.log.fail(.{ foe_ident, .Paralysis });
|
|
2059
|
+
}
|
|
2060
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
2061
|
+
if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident, .None });
|
|
2022
2062
|
if (state.miss) {
|
|
2023
|
-
try options.log.lastmiss();
|
|
2024
|
-
return options.log.miss(battle.active(player));
|
|
2063
|
+
try options.log.lastmiss(.{});
|
|
2064
|
+
return options.log.miss(.{battle.active(player)});
|
|
2025
2065
|
} else if (foe.active.volatiles.Substitute) {
|
|
2026
|
-
return options.log.
|
|
2066
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2027
2067
|
}
|
|
2028
2068
|
|
|
2029
2069
|
foe.stored().status = Status.init(.PAR);
|
|
2030
2070
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2031
2071
|
|
|
2032
|
-
try options.log.status(foe_ident, foe.stored().status, .None);
|
|
2072
|
+
try options.log.status(.{ foe_ident, foe.stored().status, .None });
|
|
2033
2073
|
}
|
|
2034
2074
|
|
|
2035
2075
|
pub fn paralyzeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2044,14 +2084,15 @@ pub const Effects = struct {
|
|
|
2044
2084
|
|
|
2045
2085
|
foe_stored.status = Status.init(.PAR);
|
|
2046
2086
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2047
|
-
|
|
2087
|
+
|
|
2088
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2048
2089
|
|
|
2049
2090
|
// TODO: check for status berry
|
|
2050
2091
|
}
|
|
2051
2092
|
|
|
2052
2093
|
pub fn payDay(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2053
2094
|
if (!showdown or !battle.foe(player).active.volatiles.Substitute) {
|
|
2054
|
-
try options.log.fieldactivate(); // FIXME .PayDay
|
|
2095
|
+
try options.log.fieldactivate(.{}); // FIXME .PayDay
|
|
2055
2096
|
}
|
|
2056
2097
|
}
|
|
2057
2098
|
|
|
@@ -2060,17 +2101,17 @@ pub const Effects = struct {
|
|
|
2060
2101
|
var foe = battle.foe(player);
|
|
2061
2102
|
|
|
2062
2103
|
if (foe.active.volatiles.PerishSong) {
|
|
2063
|
-
return options.log.fail(battle.active(player.foe()), .None);
|
|
2104
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2064
2105
|
} else if (!side.active.volatiles.PerishSong) {
|
|
2065
2106
|
side.active.volatiles.PerishSong = true;
|
|
2066
2107
|
side.active.volatiles.perish_song = 4;
|
|
2067
|
-
try options.log.start(battle.active(player), .PerishSong3Silent);
|
|
2108
|
+
try options.log.start(.{ battle.active(player), .PerishSong3Silent });
|
|
2068
2109
|
}
|
|
2069
2110
|
foe.active.volatiles.PerishSong = true;
|
|
2070
2111
|
foe.active.volatiles.perish_song = 4;
|
|
2071
|
-
try options.log.start(battle.active(player.foe()), .PerishSong3Silent);
|
|
2112
|
+
try options.log.start(.{ battle.active(player.foe()), .PerishSong3Silent });
|
|
2072
2113
|
|
|
2073
|
-
try options.log.fieldactivate(); // FIXME .PerishSong
|
|
2114
|
+
try options.log.fieldactivate(.{}); // FIXME .PerishSong
|
|
2074
2115
|
}
|
|
2075
2116
|
|
|
2076
2117
|
pub fn poison(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2080,24 +2121,24 @@ pub const Effects = struct {
|
|
|
2080
2121
|
|
|
2081
2122
|
const toxic = state.move == .Toxic;
|
|
2082
2123
|
if (state.immune() or foe.active.types.includes(.Poison)) {
|
|
2083
|
-
return options.log.immune(foe_ident, .None);
|
|
2124
|
+
return options.log.immune(.{ foe_ident, .None });
|
|
2084
2125
|
} else if (Status.any(foe_stored.status)) {
|
|
2085
2126
|
// Pokémon Showdown considers Toxic to be a status even in Generation II and so
|
|
2086
2127
|
// will not include a fail reason for Toxic vs. Poison or vice-versa...
|
|
2087
|
-
return options.log.fail(foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2128
|
+
return options.log.fail(.{ foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2088
2129
|
if (!showdown)
|
|
2089
|
-
.Poison
|
|
2130
|
+
Fail.Poison
|
|
2090
2131
|
else if (toxic == (foe_stored.status == Status.TOX))
|
|
2091
|
-
if (toxic) .Toxic else .Poison
|
|
2132
|
+
if (toxic) Fail.Toxic else Fail.Poison
|
|
2092
2133
|
else
|
|
2093
|
-
.None
|
|
2134
|
+
Fail.None
|
|
2094
2135
|
else
|
|
2095
|
-
.None);
|
|
2136
|
+
Fail.None });
|
|
2096
2137
|
} else if (foe.active.volatiles.Substitute) {
|
|
2097
|
-
return options.log.
|
|
2138
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2098
2139
|
} else if (state.miss) {
|
|
2099
|
-
try options.log.lastmiss();
|
|
2100
|
-
return options.log.miss(battle.active(player));
|
|
2140
|
+
try options.log.lastmiss(.{});
|
|
2141
|
+
return options.log.miss(.{battle.active(player)});
|
|
2101
2142
|
}
|
|
2102
2143
|
|
|
2103
2144
|
foe_stored.status = Status.init(.PSN);
|
|
@@ -2107,7 +2148,7 @@ pub const Effects = struct {
|
|
|
2107
2148
|
foe.active.volatiles.toxic = 0;
|
|
2108
2149
|
}
|
|
2109
2150
|
|
|
2110
|
-
try options.log.status(foe_ident, foe_stored.status, .None);
|
|
2151
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2111
2152
|
|
|
2112
2153
|
// TODO: check for status berry
|
|
2113
2154
|
}
|
|
@@ -2123,7 +2164,8 @@ pub const Effects = struct {
|
|
|
2123
2164
|
if (foe.conditions.Safeguard) return;
|
|
2124
2165
|
|
|
2125
2166
|
foe_stored.status = Status.init(.PSN);
|
|
2126
|
-
|
|
2167
|
+
|
|
2168
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2127
2169
|
|
|
2128
2170
|
// TODO: check for status berry
|
|
2129
2171
|
}
|
|
@@ -2138,9 +2180,7 @@ pub const Effects = struct {
|
|
|
2138
2180
|
|
|
2139
2181
|
const failed = !state.first or volatiles.Substitute or
|
|
2140
2182
|
!try Rolls.protect(battle, player, volatiles.protect, options);
|
|
2141
|
-
if (failed) {
|
|
2142
|
-
return options.log.fail(battle.active(player), .None);
|
|
2143
|
-
}
|
|
2183
|
+
if (failed) return options.log.fail(.{ battle.active(player), .None });
|
|
2144
2184
|
|
|
2145
2185
|
volatiles.protect = @max(8, volatiles.protect + 1);
|
|
2146
2186
|
if (state.move == .Endure) {
|
|
@@ -2148,7 +2188,7 @@ pub const Effects = struct {
|
|
|
2148
2188
|
} else {
|
|
2149
2189
|
volatiles.Protect = true;
|
|
2150
2190
|
}
|
|
2151
|
-
try options.log.singleturn(battle.active(player), state.move);
|
|
2191
|
+
try options.log.singleturn(.{ battle.active(player), state.move });
|
|
2152
2192
|
}
|
|
2153
2193
|
|
|
2154
2194
|
pub fn psychUp(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2157,11 +2197,11 @@ pub const Effects = struct {
|
|
|
2157
2197
|
|
|
2158
2198
|
// Pokémon Showdown should fail when the enemy has no boosts/unboosts but doesn't
|
|
2159
2199
|
if (!showdown and @as(u32, @bitCast(foe.active.boosts)) == 0) {
|
|
2160
|
-
return options.log.fail(battle.active(player), .None);
|
|
2200
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2161
2201
|
}
|
|
2162
2202
|
|
|
2163
2203
|
side.active.boosts = foe.active.boosts;
|
|
2164
|
-
try options.log.copyboost(battle.active(player), battle.active(player.foe()));
|
|
2204
|
+
try options.log.copyboost(.{ battle.active(player), battle.active(player.foe()) });
|
|
2165
2205
|
}
|
|
2166
2206
|
|
|
2167
2207
|
pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -2172,15 +2212,14 @@ pub const Effects = struct {
|
|
|
2172
2212
|
pub const rage = struct {
|
|
2173
2213
|
pub fn start(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2174
2214
|
battle.side(player).active.volatiles.Rage = true;
|
|
2175
|
-
try options.log.singlemove(battle.active(player), Move.Rage);
|
|
2215
|
+
try options.log.singlemove(.{ battle.active(player), Move.Rage });
|
|
2176
2216
|
}
|
|
2177
2217
|
|
|
2178
2218
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2179
|
-
|
|
2180
|
-
var volatiles = &side.active.volatiles;
|
|
2219
|
+
const side = battle.side(player);
|
|
2181
2220
|
|
|
2182
|
-
assert(volatiles.Rage);
|
|
2183
|
-
state.damage *|= (volatiles.rage +| 1);
|
|
2221
|
+
assert(side.active.volatiles.Rage);
|
|
2222
|
+
state.damage *|= (side.active.volatiles.rage +| 1);
|
|
2184
2223
|
}
|
|
2185
2224
|
};
|
|
2186
2225
|
|
|
@@ -2192,18 +2231,18 @@ pub const Effects = struct {
|
|
|
2192
2231
|
var side = battle.side(player);
|
|
2193
2232
|
|
|
2194
2233
|
if (side.active.volatiles.LeechSeed) {
|
|
2195
|
-
try options.log.end(battle.active(player), .LeechSeedFrom);
|
|
2234
|
+
try options.log.end(.{ battle.active(player), .LeechSeedFrom });
|
|
2196
2235
|
side.active.volatiles.LeechSeed = false;
|
|
2197
2236
|
}
|
|
2198
2237
|
|
|
2199
2238
|
if (side.conditions.Spikes) {
|
|
2200
|
-
try options.log.
|
|
2239
|
+
try options.log.sideend(.{ player, protocol.Side.Spikes, battle.active(player) });
|
|
2201
2240
|
side.conditions.Spikes = false;
|
|
2202
2241
|
}
|
|
2203
2242
|
|
|
2204
2243
|
if (side.active.volatiles.bind.duration > 0) {
|
|
2205
2244
|
const reason = @intFromEnum(End.Bind) + side.active.volatiles.bind.reason - 1;
|
|
2206
|
-
try options.log.end(battle.active(player), @enumFromInt(reason));
|
|
2245
|
+
try options.log.end(.{ battle.active(player), @as(End, @enumFromInt(reason)) });
|
|
2207
2246
|
side.active.volatiles.bind = .{};
|
|
2208
2247
|
}
|
|
2209
2248
|
}
|
|
@@ -2211,18 +2250,14 @@ pub const Effects = struct {
|
|
|
2211
2250
|
pub fn recoil(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2212
2251
|
var side = battle.side(player);
|
|
2213
2252
|
var stored = side.stored();
|
|
2253
|
+
const ident = battle.active(player);
|
|
2214
2254
|
|
|
2215
2255
|
assert(state.damage > 0);
|
|
2216
2256
|
|
|
2217
2257
|
const damage: i16 = @intCast(@max(state.damage / 4, 1));
|
|
2218
2258
|
stored.hp = @intCast(@max(@as(i16, @intCast(stored.hp)) - damage, 0));
|
|
2219
2259
|
|
|
2220
|
-
try options.log.
|
|
2221
|
-
battle.active(player),
|
|
2222
|
-
stored,
|
|
2223
|
-
.RecoilOf,
|
|
2224
|
-
battle.active(player.foe()),
|
|
2225
|
-
);
|
|
2260
|
+
try options.log.damage(.{ ident, stored, .RecoilOf, battle.active(player.foe()) });
|
|
2226
2261
|
}
|
|
2227
2262
|
|
|
2228
2263
|
pub fn reversal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2285,7 +2320,7 @@ pub const Effects = struct {
|
|
|
2285
2320
|
|
|
2286
2321
|
pub fn sandstorm(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2287
2322
|
if (battle.field.weather != .Sandstorm) return weather(battle, .Sandstorm, options);
|
|
2288
|
-
return options.log.fail(battle.active(player), .None);
|
|
2323
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2289
2324
|
}
|
|
2290
2325
|
|
|
2291
2326
|
pub fn safeguard(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2309,11 +2344,13 @@ pub const Effects = struct {
|
|
|
2309
2344
|
) !void {
|
|
2310
2345
|
var side = battle.side(player);
|
|
2311
2346
|
|
|
2312
|
-
if (@field(side.conditions, key))
|
|
2347
|
+
if (@field(side.conditions, key)) {
|
|
2348
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2349
|
+
}
|
|
2313
2350
|
|
|
2314
2351
|
@field(side.conditions, key) = true;
|
|
2315
2352
|
@field(side.conditions, value) = 5;
|
|
2316
|
-
try options.log.sidestart(player, @field(protocol.Side, key));
|
|
2353
|
+
try options.log.sidestart(.{ player, @field(protocol.Side, key) });
|
|
2317
2354
|
}
|
|
2318
2355
|
|
|
2319
2356
|
pub fn sketch(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2324,9 +2361,9 @@ pub const Effects = struct {
|
|
|
2324
2361
|
|
|
2325
2362
|
if (showdown) {
|
|
2326
2363
|
// yes, actually "Splash". '|nothing' gets blindly mapped to '|-activate||move:Splash'
|
|
2327
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2364
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2328
2365
|
} else {
|
|
2329
|
-
try options.log.fail(battle.active(player), .None);
|
|
2366
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2330
2367
|
}
|
|
2331
2368
|
}
|
|
2332
2369
|
|
|
@@ -2336,14 +2373,14 @@ pub const Effects = struct {
|
|
|
2336
2373
|
const foe_ident = battle.active(player.foe());
|
|
2337
2374
|
|
|
2338
2375
|
if (Status.any(foe_stored.status)) {
|
|
2339
|
-
return options.log.fail(
|
|
2376
|
+
return options.log.fail(.{
|
|
2340
2377
|
foe_ident,
|
|
2341
|
-
if (Status.is(foe_stored.status, .SLP)) .Sleep else .None,
|
|
2342
|
-
);
|
|
2378
|
+
if (Status.is(foe_stored.status, .SLP)) Fail.Sleep else Fail.None,
|
|
2379
|
+
});
|
|
2343
2380
|
}
|
|
2344
2381
|
|
|
2345
2382
|
if (foe.active.volatiles.Substitute) {
|
|
2346
|
-
return options.log.
|
|
2383
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2347
2384
|
}
|
|
2348
2385
|
|
|
2349
2386
|
// Sleep Clause Mod
|
|
@@ -2354,22 +2391,21 @@ pub const Effects = struct {
|
|
|
2354
2391
|
}
|
|
2355
2392
|
|
|
2356
2393
|
foe_stored.status = Status.slp(Rolls.sleepDuration(battle, player, options));
|
|
2357
|
-
try options.log.
|
|
2394
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .From, state.move });
|
|
2358
2395
|
}
|
|
2359
2396
|
|
|
2360
2397
|
pub fn sleepTalk(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2361
2398
|
const side = battle.side(player);
|
|
2362
2399
|
|
|
2363
2400
|
if (!Status.is(side.stored().status, .SLP)) {
|
|
2364
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
2365
|
-
return;
|
|
2401
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2366
2402
|
}
|
|
2367
2403
|
|
|
2368
2404
|
var n: u3 = 0;
|
|
2369
2405
|
for (side.active.moves, 0..) |m, i| {
|
|
2370
2406
|
if (state.mslot - 1 != i and Move.get(m.id).extra.sleep_talk) n += 1;
|
|
2371
2407
|
}
|
|
2372
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
2408
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
2373
2409
|
|
|
2374
2410
|
_ = try Rolls.sleepTalk(battle, player, n, state.mslot, options); // TODO
|
|
2375
2411
|
}
|
|
@@ -2378,55 +2414,75 @@ pub const Effects = struct {
|
|
|
2378
2414
|
if (Status.is(battle.side(player).stored().status, .SLP)) return true;
|
|
2379
2415
|
state.damage = 0;
|
|
2380
2416
|
state.miss = true; // TODO is this necessary?
|
|
2381
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
2417
|
+
if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2382
2418
|
return false;
|
|
2383
2419
|
}
|
|
2384
2420
|
|
|
2385
2421
|
pub fn spikes(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2386
2422
|
var foe = battle.foe(player);
|
|
2387
|
-
if (foe.conditions.Spikes) return options.log.fail(battle.active(player), .None);
|
|
2423
|
+
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player), .None });
|
|
2388
2424
|
|
|
2389
2425
|
foe.conditions.Spikes = true;
|
|
2390
|
-
try options.log.sidestart(player.foe(), .Spikes);
|
|
2426
|
+
try options.log.sidestart(.{ player.foe(), protocol.Side.Spikes });
|
|
2391
2427
|
}
|
|
2392
2428
|
|
|
2393
2429
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2394
|
-
|
|
2395
|
-
// var foe = battle.foe(player);
|
|
2430
|
+
var foe = battle.foe(player);
|
|
2396
2431
|
|
|
2397
|
-
|
|
2432
|
+
const last = foe.lastMove(false);
|
|
2433
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2434
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2435
|
+
}
|
|
2398
2436
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
//
|
|
2404
|
-
|
|
2437
|
+
var slot: u4 = 0;
|
|
2438
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2439
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2440
|
+
}
|
|
2441
|
+
// If the move was called via Metronome it might not exist
|
|
2442
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2443
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2447
|
+
const pp = @min(
|
|
2448
|
+
foe.active.move(slot).pp,
|
|
2449
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2450
|
+
);
|
|
2451
|
+
foe.active.move(slot).pp -= pp;
|
|
2452
|
+
if (!foe.active.volatiles.Transform) {
|
|
2453
|
+
var move_slot = foe.stored().move(slot);
|
|
2454
|
+
assert(move_slot.pp > 0);
|
|
2455
|
+
|
|
2456
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2457
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2405
2461
|
}
|
|
2406
2462
|
|
|
2407
2463
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2408
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2464
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2409
2465
|
}
|
|
2410
2466
|
|
|
2411
2467
|
pub fn substitute(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2412
2468
|
var side = battle.side(player);
|
|
2413
2469
|
if (side.active.volatiles.Substitute) {
|
|
2414
|
-
return options.log.fail(battle.active(player), .Substitute);
|
|
2470
|
+
return options.log.fail(.{ battle.active(player), .Substitute });
|
|
2415
2471
|
}
|
|
2416
2472
|
|
|
2417
2473
|
assert(side.stored().stats.hp <= 1023);
|
|
2418
2474
|
const hp: u8 = @intCast(side.stored().stats.hp / 4);
|
|
2419
2475
|
|
|
2420
2476
|
if (hp == 0 or side.stored().hp <= hp) {
|
|
2421
|
-
return options.log.fail(battle.active(player), .Weak);
|
|
2477
|
+
return options.log.fail(.{ battle.active(player), .Weak });
|
|
2422
2478
|
}
|
|
2423
2479
|
|
|
2424
2480
|
side.stored().hp -= hp;
|
|
2425
2481
|
side.active.volatiles.substitute = hp;
|
|
2426
2482
|
side.active.volatiles.Substitute = true;
|
|
2427
2483
|
side.active.volatiles.bind = .{};
|
|
2428
|
-
try options.log.start(battle.active(player), .Substitute);
|
|
2429
|
-
try options.log.damage(battle.active(player), side.stored(), .None);
|
|
2484
|
+
try options.log.start(.{ battle.active(player), .Substitute });
|
|
2485
|
+
try options.log.damage(.{ battle.active(player), side.stored(), .None });
|
|
2430
2486
|
}
|
|
2431
2487
|
|
|
2432
2488
|
pub fn sunnyDay(battle: anytype, _: Player, _: *State, options: anytype) !void {
|
|
@@ -2436,14 +2492,14 @@ pub const Effects = struct {
|
|
|
2436
2492
|
fn weather(battle: anytype, w: Weather, options: anytype) !void {
|
|
2437
2493
|
battle.field.weather = w;
|
|
2438
2494
|
battle.field.weather_duration = 5;
|
|
2439
|
-
try options.log.weather(w, .None);
|
|
2495
|
+
try options.log.weather(.{ w, protocol.Weather.None });
|
|
2440
2496
|
}
|
|
2441
2497
|
|
|
2442
2498
|
pub fn teleport(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2443
2499
|
if (showdown) return;
|
|
2444
2500
|
|
|
2445
|
-
try options.log.fail(battle.active(player), .None);
|
|
2446
|
-
try options.log.laststill();
|
|
2501
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2502
|
+
try options.log.laststill(.{});
|
|
2447
2503
|
}
|
|
2448
2504
|
|
|
2449
2505
|
pub fn thief(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2458,11 +2514,11 @@ pub const Effects = struct {
|
|
|
2458
2514
|
side.stored().item = foe.stored().item;
|
|
2459
2515
|
foe.stored().item = .None;
|
|
2460
2516
|
|
|
2461
|
-
try options.log.item(
|
|
2517
|
+
try options.log.item(.{
|
|
2462
2518
|
battle.active(player),
|
|
2463
2519
|
side.stored().item,
|
|
2464
2520
|
battle.active(player.foe()),
|
|
2465
|
-
);
|
|
2521
|
+
});
|
|
2466
2522
|
}
|
|
2467
2523
|
|
|
2468
2524
|
pub fn thrashing(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2484,11 +2540,11 @@ pub const Effects = struct {
|
|
|
2484
2540
|
side.active.volatiles.dirty = false;
|
|
2485
2541
|
|
|
2486
2542
|
if (foe.active.volatiles.Transform) {
|
|
2487
|
-
return options.log.fail(battle.active(player), .None);
|
|
2543
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2488
2544
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2489
|
-
if (!showdown) return options.log.fail(battle.active(player), .None);
|
|
2490
|
-
try options.log.lastmiss();
|
|
2491
|
-
return options.log.miss(battle.active(player));
|
|
2545
|
+
if (!showdown) return options.log.fail(.{ battle.active(player), .None });
|
|
2546
|
+
try options.log.lastmiss(.{});
|
|
2547
|
+
return options.log.miss(.{battle.active(player)});
|
|
2492
2548
|
}
|
|
2493
2549
|
|
|
2494
2550
|
side.active.volatiles.Transform = true;
|
|
@@ -2510,7 +2566,7 @@ pub const Effects = struct {
|
|
|
2510
2566
|
side.active.moves[i].pp = if (m.id != .None) if (m.id == .Sketch) 1 else 5 else 0;
|
|
2511
2567
|
}
|
|
2512
2568
|
|
|
2513
|
-
try options.log.transform(battle.active(player), foe_ident);
|
|
2569
|
+
try options.log.transform(.{ battle.active(player), foe_ident });
|
|
2514
2570
|
}
|
|
2515
2571
|
|
|
2516
2572
|
pub fn triAttack(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2535,17 +2591,17 @@ pub const Effects = struct {
|
|
|
2535
2591
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2536
2592
|
var side = battle.side(player);
|
|
2537
2593
|
|
|
2538
|
-
|
|
2539
|
-
|
|
2594
|
+
const stats = &side.active.stats;
|
|
2595
|
+
const boosts = &side.active.boosts;
|
|
2540
2596
|
|
|
2541
2597
|
if (!state.proc) return;
|
|
2542
2598
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
2543
2599
|
assert(@field(boosts, s) >= -6 and @field(boosts, s) <= 6);
|
|
2544
2600
|
if (@field(boosts, s) == 6 or @field(stats, s) == MAX_STAT_VALUE) {
|
|
2545
|
-
try options.log.boost(battle.active(player), r, 0);
|
|
2601
|
+
try options.log.boost(.{ battle.active(player), r, 0 });
|
|
2546
2602
|
} else {
|
|
2547
2603
|
try boostStat(battle, side, stats, boosts, s, 1);
|
|
2548
|
-
try options.log.boost(battle.active(player), r, 1);
|
|
2604
|
+
try options.log.boost(.{ battle.active(player), r, 1 });
|
|
2549
2605
|
}
|
|
2550
2606
|
}
|
|
2551
2607
|
}
|
|
@@ -2555,51 +2611,61 @@ pub const Effects = struct {
|
|
|
2555
2611
|
var side = battle.side(player);
|
|
2556
2612
|
const ident = battle.active(player);
|
|
2557
2613
|
|
|
2558
|
-
if (state.miss) return log.fail(ident, .None);
|
|
2614
|
+
if (state.miss) return log.fail(.{ ident, .None });
|
|
2559
2615
|
|
|
2560
|
-
|
|
2616
|
+
const stats = &side.active.stats;
|
|
2561
2617
|
var boosts = &side.active.boosts;
|
|
2562
2618
|
|
|
2563
2619
|
const move = Move.get(state.move);
|
|
2564
2620
|
switch (move.effect) {
|
|
2565
2621
|
.AttackUp1, .AttackUp2, .Rage, .Swagger => {
|
|
2566
2622
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2567
|
-
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE)
|
|
2623
|
+
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE) {
|
|
2624
|
+
return log.fail(.{ ident, .None });
|
|
2625
|
+
}
|
|
2568
2626
|
const n: u2 = if (move.effect == .AttackUp2 or move.effect == .Swagger) 2 else 1;
|
|
2569
2627
|
try boostStat(battle, side, stats, boosts, "atk", n);
|
|
2570
2628
|
const reason = if (showdown and move.effect == .Rage) Boost.Rage else Boost.Attack;
|
|
2571
|
-
try log.boost(ident, reason, n);
|
|
2629
|
+
try log.boost(.{ ident, reason, n });
|
|
2572
2630
|
},
|
|
2573
2631
|
.DefenseUp1, .DefenseUp2, .SkullBash => {
|
|
2574
2632
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2575
|
-
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE)
|
|
2633
|
+
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE) {
|
|
2634
|
+
return log.fail(.{ ident, .None });
|
|
2635
|
+
}
|
|
2576
2636
|
const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
|
|
2577
2637
|
try boostStat(battle, side, stats, boosts, "def", n);
|
|
2578
|
-
try log.boost(ident, .Defense, n);
|
|
2638
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2579
2639
|
},
|
|
2580
2640
|
.SpeedUp2 => {
|
|
2581
2641
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2582
|
-
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE)
|
|
2642
|
+
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE) {
|
|
2643
|
+
return log.fail(.{ ident, .None });
|
|
2644
|
+
}
|
|
2583
2645
|
try boostStat(battle, side, stats, boosts, "spe", 2);
|
|
2584
|
-
try log.boost(ident, .Speed, 2);
|
|
2646
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2585
2647
|
},
|
|
2586
2648
|
.SpAtkUp1 => {
|
|
2587
2649
|
assert(boosts.spa >= -6 and boosts.spa <= 6);
|
|
2588
|
-
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE)
|
|
2650
|
+
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE) {
|
|
2651
|
+
return log.fail(.{ ident, .None });
|
|
2652
|
+
}
|
|
2589
2653
|
try boostStat(battle, side, stats, boosts, "spa", 1);
|
|
2590
|
-
try log.boost(ident, .SpecialAttack, 1);
|
|
2654
|
+
try log.boost(.{ ident, .SpecialAttack, 1 });
|
|
2591
2655
|
},
|
|
2592
2656
|
.SpDefUp2 => {
|
|
2593
2657
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2594
|
-
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE)
|
|
2658
|
+
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE) {
|
|
2659
|
+
return log.fail(.{ ident, .None });
|
|
2660
|
+
}
|
|
2595
2661
|
try boostStat(battle, side, stats, boosts, "spd", 2);
|
|
2596
|
-
try log.boost(ident, .SpecialDefense, 2);
|
|
2662
|
+
try log.boost(.{ ident, .SpecialDefense, 2 });
|
|
2597
2663
|
},
|
|
2598
2664
|
.EvasionUp1 => {
|
|
2599
2665
|
assert(boosts.evasion >= -6 and boosts.evasion <= 6);
|
|
2600
|
-
if (boosts.evasion == 6) return log.fail(ident, .None);
|
|
2666
|
+
if (boosts.evasion == 6) return log.fail(.{ ident, .None });
|
|
2601
2667
|
boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
|
|
2602
|
-
try log.boost(ident, .Evasion, 1);
|
|
2668
|
+
try log.boost(.{ ident, .Evasion, 1 });
|
|
2603
2669
|
if (state.move == .Minimize) side.active.volatiles.minimized = true;
|
|
2604
2670
|
},
|
|
2605
2671
|
else => unreachable,
|
|
@@ -2612,79 +2678,83 @@ pub const Effects = struct {
|
|
|
2612
2678
|
const foe_ident = battle.active(player.foe());
|
|
2613
2679
|
|
|
2614
2680
|
if (foe.active.volatiles.Mist) {
|
|
2615
|
-
try log.activate(foe_ident, .Mist);
|
|
2616
|
-
return log.fail(foe_ident, .None);
|
|
2681
|
+
try log.activate(.{ foe_ident, .Mist });
|
|
2682
|
+
return log.fail(.{ foe_ident, .None });
|
|
2617
2683
|
}
|
|
2618
2684
|
|
|
2619
2685
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2620
2686
|
|
|
2621
|
-
|
|
2687
|
+
const stats = &foe.active.stats;
|
|
2622
2688
|
var boosts = &foe.active.boosts;
|
|
2623
2689
|
|
|
2624
2690
|
const move = Move.get(state.move);
|
|
2691
|
+
const none: Fail = .None;
|
|
2625
2692
|
switch (move.effect) {
|
|
2626
2693
|
.AttackDown1, .AttackDown2, .AttackDownChance => {
|
|
2627
2694
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2628
2695
|
if (fail or boosts.atk == -6) {
|
|
2629
|
-
return if (move.effect != .AttackDownChance) try log.fail(foe_ident,
|
|
2696
|
+
return if (move.effect != .AttackDownChance) try log.fail(.{ foe_ident, none });
|
|
2630
2697
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2631
|
-
try options.log.lastmiss();
|
|
2632
|
-
return options.log.miss(battle.active(player));
|
|
2698
|
+
try options.log.lastmiss(.{});
|
|
2699
|
+
return options.log.miss(.{battle.active(player)});
|
|
2633
2700
|
} else if (stats.atk == 1) {
|
|
2634
|
-
return if (move.effect != .AttackDownChance) try log.fail(foe_ident,
|
|
2701
|
+
return if (move.effect != .AttackDownChance) try log.fail(.{ foe_ident, none });
|
|
2635
2702
|
}
|
|
2636
2703
|
const n: u2 = if (move.effect == .AttackDown2) 2 else 1;
|
|
2637
2704
|
try unboostStat(battle, foe, stats, boosts, "atk", n);
|
|
2638
|
-
try log.boost(foe_ident, .Attack, -@as(i8, n));
|
|
2705
|
+
try log.boost(.{ foe_ident, .Attack, -@as(i8, n) });
|
|
2639
2706
|
},
|
|
2640
2707
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2641
2708
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2642
2709
|
if (fail or boosts.def == -6) {
|
|
2643
|
-
|
|
2710
|
+
if (move.effect != .DefenseDownChance) try log.fail(.{ foe_ident, none });
|
|
2711
|
+
return;
|
|
2644
2712
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2645
|
-
try options.log.lastmiss();
|
|
2646
|
-
return options.log.miss(battle.active(player));
|
|
2713
|
+
try options.log.lastmiss(.{});
|
|
2714
|
+
return options.log.miss(.{battle.active(player)});
|
|
2647
2715
|
} else if (stats.def == 1) {
|
|
2648
|
-
|
|
2716
|
+
if (move.effect != .DefenseDownChance) try log.fail(.{ foe_ident, none });
|
|
2717
|
+
return;
|
|
2649
2718
|
}
|
|
2650
2719
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2651
2720
|
try unboostStat(battle, foe, stats, boosts, "def", n);
|
|
2652
|
-
try log.boost(foe_ident, .Defense, -@as(i8, n));
|
|
2721
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2653
2722
|
},
|
|
2654
2723
|
.SpeedDown1, .SpeedDown2, .SpeedDownChance => {
|
|
2655
2724
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2656
2725
|
if (fail or boosts.spe == -6) {
|
|
2657
|
-
return if (move.effect != .SpeedDownChance) try log.fail(foe_ident,
|
|
2726
|
+
return if (move.effect != .SpeedDownChance) try log.fail(.{ foe_ident, none });
|
|
2658
2727
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2659
|
-
try options.log.lastmiss();
|
|
2660
|
-
return options.log.miss(battle.active(player));
|
|
2728
|
+
try options.log.lastmiss(.{});
|
|
2729
|
+
return options.log.miss(.{battle.active(player)});
|
|
2661
2730
|
} else if (stats.spe == 1) {
|
|
2662
|
-
return if (move.effect != .SpeedDownChance) try log.fail(foe_ident,
|
|
2731
|
+
return if (move.effect != .SpeedDownChance) try log.fail(.{ foe_ident, none });
|
|
2663
2732
|
}
|
|
2664
2733
|
const n: u2 = if (move.effect == .SpeedDown2) 2 else 1;
|
|
2665
2734
|
try unboostStat(battle, foe, stats, boosts, "spe", n);
|
|
2666
|
-
try log.boost(foe_ident, .Speed, -@as(i8, n));
|
|
2735
|
+
try log.boost(.{ foe_ident, .Speed, -@as(i8, n) });
|
|
2667
2736
|
},
|
|
2668
2737
|
.SpDefDownChance => {
|
|
2669
2738
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2670
2739
|
if (fail or boosts.spd == -6) return;
|
|
2671
2740
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2672
|
-
try options.log.lastmiss();
|
|
2673
|
-
return options.log.miss(battle.active(player));
|
|
2741
|
+
try options.log.lastmiss(.{});
|
|
2742
|
+
return options.log.miss(.{battle.active(player)});
|
|
2674
2743
|
} else if (stats.spd == 1) return;
|
|
2675
2744
|
try unboostStat(battle, foe, stats, boosts, "spd", 1);
|
|
2676
|
-
try log.boost(foe_ident, .SpecialDefense, -1);
|
|
2745
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2677
2746
|
},
|
|
2678
2747
|
.AccuracyDown1, .AccuracyDownChance => {
|
|
2679
2748
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
2680
2749
|
if (fail or boosts.accuracy == -6) {
|
|
2681
|
-
|
|
2750
|
+
if (move.effect != .AccuracyDownChance) try log.fail(.{ foe_ident, none });
|
|
2751
|
+
return;
|
|
2682
2752
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2683
|
-
try options.log.lastmiss();
|
|
2684
|
-
return options.log.miss(battle.active(player));
|
|
2753
|
+
try options.log.lastmiss(.{});
|
|
2754
|
+
return options.log.miss(.{battle.active(player)});
|
|
2685
2755
|
}
|
|
2686
2756
|
boosts.accuracy = @intCast(@max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2687
|
-
try log.boost(foe_ident, .Accuracy, -1);
|
|
2757
|
+
try log.boost(.{ foe_ident, .Accuracy, -1 });
|
|
2688
2758
|
},
|
|
2689
2759
|
else => unreachable,
|
|
2690
2760
|
}
|
|
@@ -2698,9 +2768,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2698
2768
|
}
|
|
2699
2769
|
|
|
2700
2770
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2701
|
-
|
|
2771
|
+
const stats = unmodifiedStats(battle, side);
|
|
2702
2772
|
inline for (STATS) |s| {
|
|
2703
|
-
|
|
2773
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2704
2774
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2705
2775
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2706
2776
|
}
|
|
@@ -2723,7 +2793,7 @@ fn boostStat(
|
|
|
2723
2793
|
n: u2,
|
|
2724
2794
|
) !void {
|
|
2725
2795
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2726
|
-
|
|
2796
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2727
2797
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2728
2798
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2729
2799
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2746,7 +2816,7 @@ fn unboostStat(
|
|
|
2746
2816
|
n: u2,
|
|
2747
2817
|
) !void {
|
|
2748
2818
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2749
|
-
|
|
2819
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2750
2820
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2751
2821
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2752
2822
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2774,7 +2844,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2774
2844
|
|
|
2775
2845
|
pub const Rolls = struct {
|
|
2776
2846
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2777
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2847
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2778
2848
|
player == .P1
|
|
2779
2849
|
else if (showdown)
|
|
2780
2850
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2786,7 +2856,7 @@ pub const Rolls = struct {
|
|
|
2786
2856
|
}
|
|
2787
2857
|
|
|
2788
2858
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2789
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2859
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2790
2860
|
val == .true
|
|
2791
2861
|
else if (showdown)
|
|
2792
2862
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2798,7 +2868,7 @@ pub const Rolls = struct {
|
|
|
2798
2868
|
}
|
|
2799
2869
|
|
|
2800
2870
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2801
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2871
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2802
2872
|
val == .true
|
|
2803
2873
|
else if (showdown)
|
|
2804
2874
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2809,20 +2879,22 @@ pub const Rolls = struct {
|
|
|
2809
2879
|
return ok;
|
|
2810
2880
|
}
|
|
2811
2881
|
|
|
2812
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2813
|
-
|
|
2882
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2883
|
+
_ = .{ player, options };
|
|
2884
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2814
2885
|
val == .true
|
|
2815
2886
|
else if (showdown)
|
|
2816
2887
|
battle.rng.chance(u8, rate, 256)
|
|
2817
2888
|
else
|
|
2818
2889
|
battle.rng.next() < rate;
|
|
2819
2890
|
|
|
2820
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2891
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2821
2892
|
return crit;
|
|
2822
2893
|
}
|
|
2823
2894
|
|
|
2824
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2825
|
-
|
|
2895
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2896
|
+
_ = .{ player, options };
|
|
2897
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2826
2898
|
val
|
|
2827
2899
|
else roll: {
|
|
2828
2900
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2833,12 +2905,12 @@ pub const Rolls = struct {
|
|
|
2833
2905
|
};
|
|
2834
2906
|
|
|
2835
2907
|
assert(roll >= 217 and roll <= 255);
|
|
2836
|
-
try options.chance.damage(player, roll);
|
|
2908
|
+
try options.chance.damage(player, i, roll);
|
|
2837
2909
|
return roll;
|
|
2838
2910
|
}
|
|
2839
2911
|
|
|
2840
2912
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2841
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2913
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2842
2914
|
val == .true
|
|
2843
2915
|
else if (showdown)
|
|
2844
2916
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2850,7 +2922,7 @@ pub const Rolls = struct {
|
|
|
2850
2922
|
}
|
|
2851
2923
|
|
|
2852
2924
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2853
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2925
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2854
2926
|
val == .true
|
|
2855
2927
|
else if (showdown)
|
|
2856
2928
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2862,7 +2934,7 @@ pub const Rolls = struct {
|
|
|
2862
2934
|
}
|
|
2863
2935
|
|
|
2864
2936
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2865
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2937
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2866
2938
|
val == .true
|
|
2867
2939
|
else if (showdown)
|
|
2868
2940
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2874,7 +2946,7 @@ pub const Rolls = struct {
|
|
|
2874
2946
|
}
|
|
2875
2947
|
|
|
2876
2948
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2877
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2949
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2878
2950
|
val == .true
|
|
2879
2951
|
else if (showdown)
|
|
2880
2952
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2887,7 +2959,7 @@ pub const Rolls = struct {
|
|
|
2887
2959
|
|
|
2888
2960
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2889
2961
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2890
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2962
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2891
2963
|
val == .true
|
|
2892
2964
|
else if (showdown)
|
|
2893
2965
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2899,7 +2971,7 @@ pub const Rolls = struct {
|
|
|
2899
2971
|
}
|
|
2900
2972
|
|
|
2901
2973
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2902
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2974
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2903
2975
|
val == .true
|
|
2904
2976
|
else if (showdown)
|
|
2905
2977
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2911,7 +2983,7 @@ pub const Rolls = struct {
|
|
|
2911
2983
|
}
|
|
2912
2984
|
|
|
2913
2985
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2914
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2986
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2915
2987
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2916
2988
|
else if (showdown)
|
|
2917
2989
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2927,7 +2999,7 @@ pub const Rolls = struct {
|
|
|
2927
2999
|
}
|
|
2928
3000
|
|
|
2929
3001
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2930
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3002
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2931
3003
|
(val - 1) * 40
|
|
2932
3004
|
else if (showdown) power: {
|
|
2933
3005
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2949,7 +3021,7 @@ pub const Rolls = struct {
|
|
|
2949
3021
|
}
|
|
2950
3022
|
|
|
2951
3023
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2952
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3024
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2953
3025
|
val + 3
|
|
2954
3026
|
else if (showdown) num: {
|
|
2955
3027
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2977,7 +3049,7 @@ pub const Rolls = struct {
|
|
|
2977
3049
|
}
|
|
2978
3050
|
|
|
2979
3051
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
2980
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3052
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
2981
3053
|
val
|
|
2982
3054
|
else if (showdown)
|
|
2983
3055
|
battle.rng.range(u2, 1, 4)
|
|
@@ -2993,13 +3065,12 @@ pub const Rolls = struct {
|
|
|
2993
3065
|
return hits;
|
|
2994
3066
|
}
|
|
2995
3067
|
|
|
2996
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
2997
|
-
|
|
3068
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3069
|
+
assert(!showdown);
|
|
3070
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
2998
3071
|
val + 1
|
|
2999
|
-
else if (showdown)
|
|
3000
|
-
battle.rng.range(u8, 2, 6)
|
|
3001
3072
|
else
|
|
3002
|
-
(battle.rng.next() & 3) + 2;
|
|
3073
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3003
3074
|
|
|
3004
3075
|
assert(pp >= 2 and pp <= 5);
|
|
3005
3076
|
try options.chance.spite(player, pp);
|
|
@@ -3013,7 +3084,7 @@ pub const Rolls = struct {
|
|
|
3013
3084
|
var i: u8 = 0;
|
|
3014
3085
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3015
3086
|
assert(mtype != .@"???");
|
|
3016
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3087
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3017
3088
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3018
3089
|
else if (showdown) ty: {
|
|
3019
3090
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3047,7 +3118,7 @@ pub const Rolls = struct {
|
|
|
3047
3118
|
assert(count <= 8);
|
|
3048
3119
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3049
3120
|
|
|
3050
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3121
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3051
3122
|
val == .true
|
|
3052
3123
|
else if (showdown)
|
|
3053
3124
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3069,7 +3140,7 @@ pub const Rolls = struct {
|
|
|
3069
3140
|
const moves = active.moves;
|
|
3070
3141
|
|
|
3071
3142
|
// TODO: consider throwing error instead of rerolling?
|
|
3072
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3143
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3073
3144
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3074
3145
|
else
|
|
3075
3146
|
null;
|
|
@@ -3106,7 +3177,7 @@ pub const Rolls = struct {
|
|
|
3106
3177
|
const moves = active.moves;
|
|
3107
3178
|
|
|
3108
3179
|
// TODO: consider throwing error instead of rerolling?
|
|
3109
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3180
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3110
3181
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3111
3182
|
else
|
|
3112
3183
|
null;
|
|
@@ -3150,7 +3221,7 @@ pub const Rolls = struct {
|
|
|
3150
3221
|
assert(n > 0 and n <= 6);
|
|
3151
3222
|
|
|
3152
3223
|
// TODO: consider throwing error instead of rerolling?
|
|
3153
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3224
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3154
3225
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3155
3226
|
break :val null;
|
|
3156
3227
|
} else null;
|
|
@@ -3176,7 +3247,7 @@ pub const Rolls = struct {
|
|
|
3176
3247
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3177
3248
|
|
|
3178
3249
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3179
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3250
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3180
3251
|
@intCast(val)
|
|
3181
3252
|
else if (showdown)
|
|
3182
3253
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3191,7 +3262,7 @@ pub const Rolls = struct {
|
|
|
3191
3262
|
}
|
|
3192
3263
|
|
|
3193
3264
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3194
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3265
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3195
3266
|
val
|
|
3196
3267
|
else if (showdown)
|
|
3197
3268
|
battle.rng.range(u8, 1, max)
|
|
@@ -3217,14 +3288,14 @@ pub const Rolls = struct {
|
|
|
3217
3288
|
}
|
|
3218
3289
|
|
|
3219
3290
|
// TODO: consider throwing error instead of rerolling?
|
|
3220
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3291
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3221
3292
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3222
3293
|
break :val val;
|
|
3223
3294
|
} else null;
|
|
3224
3295
|
|
|
3225
3296
|
const move: Move = overridden orelse move: {
|
|
3226
3297
|
if (showdown) {
|
|
3227
|
-
var r = battle.rng.range(u8, 0, @intCast(Move.METRONOME.len - n));
|
|
3298
|
+
var r = battle.rng.range(u8, 0, @as(u8, @intCast(Move.METRONOME.len - n)));
|
|
3228
3299
|
for (moves) |m| {
|
|
3229
3300
|
if (m.id == .None) break;
|
|
3230
3301
|
if (@intFromEnum(m.id) > @intFromEnum(Move.METRONOME[r])) break;
|
|
@@ -3249,7 +3320,7 @@ pub const Rolls = struct {
|
|
|
3249
3320
|
}
|
|
3250
3321
|
|
|
3251
3322
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3252
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3323
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3253
3324
|
@intCast(val)
|
|
3254
3325
|
else if (showdown)
|
|
3255
3326
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3261,12 +3332,12 @@ pub const Rolls = struct {
|
|
|
3261
3332
|
};
|
|
3262
3333
|
|
|
3263
3334
|
assert(duration >= 1 and duration <= 6);
|
|
3264
|
-
options.chance.duration(
|
|
3335
|
+
options.chance.duration(player, duration);
|
|
3265
3336
|
return duration + 1;
|
|
3266
3337
|
}
|
|
3267
3338
|
|
|
3268
3339
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3269
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3340
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3270
3341
|
@intCast(val)
|
|
3271
3342
|
else if (showdown)
|
|
3272
3343
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3275,16 +3346,16 @@ pub const Rolls = struct {
|
|
|
3275
3346
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3276
3347
|
|
|
3277
3348
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3278
|
-
options.chance.duration(
|
|
3349
|
+
options.chance.duration(player, duration);
|
|
3279
3350
|
return duration;
|
|
3280
3351
|
}
|
|
3281
3352
|
|
|
3282
3353
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3283
|
-
const duration:
|
|
3354
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3284
3355
|
@intCast(val)
|
|
3285
3356
|
else if (showdown)
|
|
3286
3357
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3287
|
-
battle.rng.range(
|
|
3358
|
+
battle.rng.range(u3, 2, 6)
|
|
3288
3359
|
else duration: {
|
|
3289
3360
|
while (true) {
|
|
3290
3361
|
const r = battle.rng.next() & 7;
|
|
@@ -3293,12 +3364,12 @@ pub const Rolls = struct {
|
|
|
3293
3364
|
};
|
|
3294
3365
|
|
|
3295
3366
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3296
|
-
options.chance.duration(
|
|
3367
|
+
options.chance.duration(player, duration);
|
|
3297
3368
|
return duration;
|
|
3298
3369
|
}
|
|
3299
3370
|
|
|
3300
3371
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3301
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3372
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3302
3373
|
@intCast(val)
|
|
3303
3374
|
else if (showdown)
|
|
3304
3375
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3306,12 +3377,12 @@ pub const Rolls = struct {
|
|
|
3306
3377
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3307
3378
|
|
|
3308
3379
|
assert(duration >= 2 and duration <= 3);
|
|
3309
|
-
options.chance.duration(
|
|
3380
|
+
options.chance.duration(player, duration);
|
|
3310
3381
|
return duration;
|
|
3311
3382
|
}
|
|
3312
3383
|
|
|
3313
3384
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3314
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3385
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3315
3386
|
@intCast(val)
|
|
3316
3387
|
else if (showdown)
|
|
3317
3388
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3320,13 +3391,13 @@ pub const Rolls = struct {
|
|
|
3320
3391
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3321
3392
|
|
|
3322
3393
|
assert(duration >= 2 and duration <= 5);
|
|
3323
|
-
options.chance.duration(
|
|
3394
|
+
options.chance.duration(player, duration);
|
|
3324
3395
|
return duration + 1;
|
|
3325
3396
|
}
|
|
3326
3397
|
|
|
3327
3398
|
// TODO: consider sharing implementation with bindingDuration
|
|
3328
3399
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3329
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3400
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3330
3401
|
@intCast(val)
|
|
3331
3402
|
else if (showdown)
|
|
3332
3403
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3334,7 +3405,7 @@ pub const Rolls = struct {
|
|
|
3334
3405
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3335
3406
|
|
|
3336
3407
|
assert(duration >= 2 and duration <= 5);
|
|
3337
|
-
options.chance.duration(
|
|
3408
|
+
options.chance.duration(player, duration);
|
|
3338
3409
|
return duration + 1;
|
|
3339
3410
|
}
|
|
3340
3411
|
};
|
|
@@ -3447,7 +3518,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3447
3518
|
.Move => {
|
|
3448
3519
|
const side = battle.side(player);
|
|
3449
3520
|
|
|
3450
|
-
|
|
3521
|
+
const active = &side.active;
|
|
3451
3522
|
const stored = side.stored();
|
|
3452
3523
|
|
|
3453
3524
|
// While players are not given any input options on the cartridge in these cases,
|