@pkmn/engine 0.1.0-dev.900756a9 → 0.1.0-dev.9257876b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -3
- package/README.md +39 -16
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +3 -2
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +1 -1
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js.map +1 -1
- package/build.zig +72 -64
- package/package.json +31 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/protocol.zig +381 -405
- package/src/lib/common/rational.zig +7 -3
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/gen1/calc.zig +5 -5
- package/src/lib/gen1/chance.zig +27 -26
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +18 -18
- package/src/lib/gen1/helpers.zig +7 -7
- package/src/lib/gen1/mechanics.zig +255 -229
- package/src/lib/gen2/chance.zig +38 -37
- package/src/lib/gen2/data.zig +9 -9
- package/src/lib/gen2/generated.zig +4 -6
- package/src/lib/gen2/mechanics.zig +398 -299
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +26 -3
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
|
@@ -26,9 +26,9 @@ const Result = common.Result;
|
|
|
26
26
|
|
|
27
27
|
const Activate = protocol.Activate;
|
|
28
28
|
const Boost = protocol.Boost;
|
|
29
|
-
const Cant = protocol.Cant;
|
|
30
29
|
const Damage = protocol.Damage;
|
|
31
30
|
const End = protocol.End;
|
|
31
|
+
const Fail = protocol.Fail;
|
|
32
32
|
const Heal = protocol.Heal;
|
|
33
33
|
const Start = protocol.Start;
|
|
34
34
|
|
|
@@ -140,11 +140,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
140
140
|
const p1 = battle.side(.P1);
|
|
141
141
|
const p2 = battle.side(.P2);
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
const p1_slot = findFirstAlive(p1);
|
|
144
144
|
assert(!showdown or p1_slot == 1);
|
|
145
145
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
const p2_slot = findFirstAlive(p2);
|
|
148
148
|
assert(!showdown or p2_slot == 1);
|
|
149
149
|
if (p2_slot == 0) return Result.Win;
|
|
150
150
|
|
|
@@ -252,15 +252,15 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
252
252
|
statusModify(incoming.status, &active.stats);
|
|
253
253
|
|
|
254
254
|
if (kind == .Drag) {
|
|
255
|
-
try options.log.drag(battle.active(player), incoming);
|
|
255
|
+
try options.log.drag(.{ battle.active(player), incoming });
|
|
256
256
|
} else {
|
|
257
|
-
try options.log.switched(battle.active(player), incoming);
|
|
257
|
+
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
258
|
}
|
|
259
259
|
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
260
|
|
|
261
261
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
262
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
263
|
-
try options.log.damage(battle.active(player), incoming, .Spikes);
|
|
263
|
+
try options.log.damage(.{ battle.active(player), incoming, .Spikes });
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
@@ -403,7 +403,9 @@ fn executeMove(
|
|
|
403
403
|
|
|
404
404
|
assert(choice.type == .Move);
|
|
405
405
|
var mslot: u4 = @intCast(choice.data);
|
|
406
|
+
_ = &mslot;
|
|
406
407
|
var move = Move.Pound; // TODO
|
|
408
|
+
_ = &move;
|
|
407
409
|
|
|
408
410
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
409
411
|
|
|
@@ -433,7 +435,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
433
435
|
if (volatiles.Recharging) {
|
|
434
436
|
volatiles.Recharging = false;
|
|
435
437
|
resetCant(volatiles);
|
|
436
|
-
try log.cant(ident, .Recharge);
|
|
438
|
+
try log.cant(.{ ident, .Recharge });
|
|
437
439
|
return false;
|
|
438
440
|
}
|
|
439
441
|
|
|
@@ -452,12 +454,12 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
452
454
|
}
|
|
453
455
|
|
|
454
456
|
if (duration == 0) {
|
|
455
|
-
try log.curestatus(ident, before, .Message);
|
|
457
|
+
try log.curestatus(.{ ident, before, .Message });
|
|
456
458
|
stored.status = 0; // clears EXT if present
|
|
457
459
|
resetCant(volatiles);
|
|
458
460
|
volatiles.Nightmare = false;
|
|
459
461
|
} else {
|
|
460
|
-
try log.cant(ident, .Sleep);
|
|
462
|
+
try log.cant(.{ ident, .Sleep });
|
|
461
463
|
if (move == .Snore or move == .SleepTalk) break :slp;
|
|
462
464
|
resetCant(volatiles);
|
|
463
465
|
return false;
|
|
@@ -466,7 +468,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
466
468
|
|
|
467
469
|
if (Status.is(stored.status, .FRZ) and !(move == .FlameWheel or move == .SacredFire)) {
|
|
468
470
|
resetCant(volatiles);
|
|
469
|
-
try log.cant(ident, .Freeze);
|
|
471
|
+
try log.cant(.{ ident, .Freeze });
|
|
470
472
|
return false;
|
|
471
473
|
}
|
|
472
474
|
|
|
@@ -476,7 +478,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
476
478
|
// if (!showdown) volatiles.Flinch = false;
|
|
477
479
|
volatiles.Flinch = false;
|
|
478
480
|
resetCant(volatiles);
|
|
479
|
-
try log.cant(ident, .Flinch);
|
|
481
|
+
try log.cant(.{ ident, .Flinch });
|
|
480
482
|
return false;
|
|
481
483
|
}
|
|
482
484
|
|
|
@@ -490,7 +492,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
490
492
|
|
|
491
493
|
if (volatiles.disable.duration == 0) {
|
|
492
494
|
volatiles.disable.move = 0;
|
|
493
|
-
try log.end(ident, .Disable);
|
|
495
|
+
try log.end(.{ ident, .Disable });
|
|
494
496
|
}
|
|
495
497
|
}
|
|
496
498
|
|
|
@@ -506,9 +508,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
506
508
|
|
|
507
509
|
if (volatiles.confusion == 0) {
|
|
508
510
|
volatiles.Confusion = false;
|
|
509
|
-
try log.end(ident, .Confusion);
|
|
511
|
+
try log.end(.{ ident, .Confusion });
|
|
510
512
|
} else {
|
|
511
|
-
try log.activate(ident, .Confusion);
|
|
513
|
+
try log.activate(.{ ident, .Confusion });
|
|
512
514
|
|
|
513
515
|
if (try Rolls.confused(battle, player, options)) {
|
|
514
516
|
volatiles.BeatUp = false;
|
|
@@ -523,11 +525,11 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
523
525
|
}
|
|
524
526
|
|
|
525
527
|
if (volatiles.Attract) {
|
|
526
|
-
try log.activate(ident, .Attract);
|
|
528
|
+
try log.activate(.{ ident, .Attract });
|
|
527
529
|
|
|
528
530
|
if (try Rolls.attract(battle, player, options)) {
|
|
529
531
|
resetCant(volatiles);
|
|
530
|
-
try log.cant(ident, .Attract);
|
|
532
|
+
try log.cant(.{ ident, .Attract });
|
|
531
533
|
return false;
|
|
532
534
|
}
|
|
533
535
|
}
|
|
@@ -538,14 +540,14 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
538
540
|
if (m != .None and m == move) {
|
|
539
541
|
side.active.volatiles.Charging = false;
|
|
540
542
|
resetCant(volatiles);
|
|
541
|
-
try options.log.
|
|
543
|
+
try options.log.cant(.{ ident, .Disable, move });
|
|
542
544
|
return false;
|
|
543
545
|
}
|
|
544
546
|
}
|
|
545
547
|
|
|
546
548
|
if (Status.is(stored.status, .PAR) and try Rolls.paralyzed(battle, player, options)) {
|
|
547
549
|
resetCant(volatiles);
|
|
548
|
-
try log.cant(ident, .Paralysis);
|
|
550
|
+
try log.cant(.{ ident, .Paralysis });
|
|
549
551
|
return false;
|
|
550
552
|
}
|
|
551
553
|
|
|
@@ -565,11 +567,11 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
565
567
|
|
|
566
568
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
567
569
|
var side = battle.side(player);
|
|
568
|
-
|
|
570
|
+
const volatiles = &side.active.volatiles;
|
|
569
571
|
|
|
570
572
|
// FIXME: pass in target to avoid lookup?
|
|
571
573
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
572
|
-
try options.log.
|
|
574
|
+
try options.log.move(.{ battle.active(player), state.move, battle.active(target), state.from });
|
|
573
575
|
|
|
574
576
|
const charging = false; // TODO charging uses doUse
|
|
575
577
|
const skip_pp = charging or state.move == .Struggle or
|
|
@@ -849,13 +851,13 @@ fn checkFaint(
|
|
|
849
851
|
if (foe_fainted) try faint(battle, player.foe(), !more, options);
|
|
850
852
|
|
|
851
853
|
if (player_out and foe_out) {
|
|
852
|
-
try options.log.tie();
|
|
854
|
+
try options.log.tie(.{});
|
|
853
855
|
return Result.Tie;
|
|
854
856
|
} else if (player_out) {
|
|
855
|
-
try options.log.win(player.foe());
|
|
857
|
+
try options.log.win(.{player.foe()});
|
|
856
858
|
return if (player == .P1) Result.Lose else Result.Win;
|
|
857
859
|
} else if (foe_out) {
|
|
858
|
-
try options.log.win(player);
|
|
860
|
+
try options.log.win(.{player});
|
|
859
861
|
return if (player == .P1) Result.Win else Result.Lose;
|
|
860
862
|
}
|
|
861
863
|
|
|
@@ -885,19 +887,19 @@ fn faint(battle: anytype, player: Player, done: bool, options: anytype) !void {
|
|
|
885
887
|
side.stored().stats.spe;
|
|
886
888
|
}
|
|
887
889
|
|
|
888
|
-
try options.log.faint(battle.active(player), done);
|
|
890
|
+
try options.log.faint(.{ battle.active(player), done });
|
|
889
891
|
options.calc.capped(player);
|
|
890
892
|
}
|
|
891
893
|
|
|
892
894
|
// TODO: see if we can move *before* applyDamage and include failuretext logic
|
|
893
895
|
pub fn reportOutcome(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
894
896
|
const foe_ident = battle.active(player.foe());
|
|
895
|
-
if (state.crit) try options.log.crit(foe_ident);
|
|
897
|
+
if (state.crit) try options.log.crit(.{foe_ident});
|
|
896
898
|
if (!state.immune()) {
|
|
897
899
|
if (state.effectiveness > Effectiveness.neutral) {
|
|
898
|
-
try options.log.supereffective(foe_ident);
|
|
900
|
+
try options.log.supereffective(.{foe_ident});
|
|
899
901
|
} else if (state.effectiveness < Effectiveness.neutral) {
|
|
900
|
-
try options.log.resisted(foe_ident);
|
|
902
|
+
try options.log.resisted(.{foe_ident});
|
|
901
903
|
}
|
|
902
904
|
}
|
|
903
905
|
}
|
|
@@ -930,8 +932,8 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
930
932
|
}
|
|
931
933
|
|
|
932
934
|
stored.hp -|= damage;
|
|
933
|
-
// Pokémon Showdown uses
|
|
934
|
-
try options.log.damage(ident, stored, if (brn) Damage.Burn else Damage.Poison);
|
|
935
|
+
// Pokémon Showdown uses damage [of] here but its not relevant in Generation II
|
|
936
|
+
try options.log.damage(.{ ident, stored, if (brn) Damage.Burn else Damage.Poison });
|
|
935
937
|
if (stored.hp == 0) return true;
|
|
936
938
|
}
|
|
937
939
|
|
|
@@ -947,27 +949,27 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
947
949
|
|
|
948
950
|
const damage = @max(stored.stats.hp / 8, 1);
|
|
949
951
|
stored.hp -|= damage;
|
|
950
|
-
// As above, Pokémon Showdown uses
|
|
951
|
-
try options.log.damage(ident, stored, .LeechSeed);
|
|
952
|
+
// As above, Pokémon Showdown uses damage [of] but its not relevant
|
|
953
|
+
try options.log.damage(.{ ident, stored, .LeechSeed });
|
|
952
954
|
|
|
953
955
|
const before = foe_stored.hp;
|
|
954
956
|
foe_stored.hp = @min(foe_stored.hp + damage, foe_stored.stats.hp);
|
|
955
957
|
// 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,
|
|
958
|
+
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored, .Silent });
|
|
957
959
|
if (stored.hp == 0) return true;
|
|
958
960
|
}
|
|
959
961
|
|
|
960
962
|
if (volatiles.Nightmare) {
|
|
961
963
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
962
964
|
// ibid
|
|
963
|
-
try options.log.damage(ident, stored, .Nightmare);
|
|
965
|
+
try options.log.damage(.{ ident, stored, .Nightmare });
|
|
964
966
|
if (stored.hp == 0) return true;
|
|
965
967
|
}
|
|
966
968
|
|
|
967
969
|
if (volatiles.Curse) {
|
|
968
970
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
969
971
|
// ibid
|
|
970
|
-
try options.log.damage(ident, stored, .Curse);
|
|
972
|
+
try options.log.damage(.{ ident, stored, .Curse });
|
|
971
973
|
if (stored.hp == 0) return true;
|
|
972
974
|
}
|
|
973
975
|
|
|
@@ -988,7 +990,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
988
990
|
future_sight.count -= 1;
|
|
989
991
|
assert(future_sight.count != 0);
|
|
990
992
|
if (future_sight.count == 1) {
|
|
991
|
-
try options.log.end(battle.active(player), .FutureSight);
|
|
993
|
+
try options.log.end(.{ battle.active(player), .FutureSight });
|
|
992
994
|
// TODO doMove
|
|
993
995
|
}
|
|
994
996
|
}
|
|
@@ -997,21 +999,24 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
997
999
|
|
|
998
1000
|
// Weather
|
|
999
1001
|
if (battle.field.weather != .None) {
|
|
1000
|
-
assert(battle.field.weather_duration > 0);
|
|
1001
1002
|
battle.field.weather_duration -= 1;
|
|
1002
1003
|
|
|
1003
1004
|
if (battle.field.weather_duration == 0) {
|
|
1004
1005
|
battle.field.weather = .None;
|
|
1005
|
-
try options.log.weather(battle.field.weather, .None);
|
|
1006
|
+
try options.log.weather(.{ battle.field.weather, protocol.Weather.None });
|
|
1006
1007
|
} else {
|
|
1007
|
-
try options.log.weather(battle.field.weather, .Upkeep);
|
|
1008
|
+
try options.log.weather(.{ battle.field.weather, protocol.Weather.Upkeep });
|
|
1008
1009
|
if (battle.field.weather == .Sandstorm) {
|
|
1009
1010
|
inline for (players) |player| {
|
|
1010
1011
|
var side = battle.side(player);
|
|
1011
1012
|
const active = side.active;
|
|
1012
1013
|
if (!(active.volatiles.Underground or active.types.sandstormImmune())) {
|
|
1013
1014
|
side.stored().hp -|= @max(side.stored().stats.hp / 8, 1);
|
|
1014
|
-
try options.log.damage(
|
|
1015
|
+
try options.log.damage(.{
|
|
1016
|
+
battle.active(player),
|
|
1017
|
+
side.stored(),
|
|
1018
|
+
.Sandstorm,
|
|
1019
|
+
});
|
|
1015
1020
|
}
|
|
1016
1021
|
}
|
|
1017
1022
|
if (try checkFaint(battle, .P1, options)) |r| return r;
|
|
@@ -1023,19 +1028,24 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1023
1028
|
inline for (players) |player| {
|
|
1024
1029
|
var side = battle.side(player);
|
|
1025
1030
|
var volatiles = &side.active.volatiles;
|
|
1031
|
+
const ident = battle.active(player);
|
|
1026
1032
|
|
|
1027
1033
|
if (volatiles.bind.duration > 0 and !volatiles.Substitute) {
|
|
1028
1034
|
volatiles.bind.duration -= 1;
|
|
1029
1035
|
if (volatiles.bind.duration == 0) {
|
|
1030
1036
|
const reason = @intFromEnum(End.Bind) + volatiles.bind.reason - 1;
|
|
1031
|
-
try options.log.end(
|
|
1037
|
+
try options.log.end(.{ ident, @as(End, @enumFromInt(reason)) });
|
|
1032
1038
|
} else {
|
|
1033
1039
|
var reason = @intFromEnum(Activate.Bind) + volatiles.bind.reason - 1;
|
|
1034
|
-
try options.log.activate(
|
|
1040
|
+
try options.log.activate(.{ ident, @as(Activate, @enumFromInt(reason)) });
|
|
1035
1041
|
|
|
1036
1042
|
reason = @intFromEnum(Damage.Bind) + volatiles.bind.reason - 1;
|
|
1037
1043
|
side.stored().hp -|= @max(side.stored().stats.hp / 16, 1);
|
|
1038
|
-
try options.log.damage(
|
|
1044
|
+
try options.log.damage(.{
|
|
1045
|
+
ident,
|
|
1046
|
+
side.stored(),
|
|
1047
|
+
@as(Damage, @enumFromInt(reason)),
|
|
1048
|
+
});
|
|
1039
1049
|
}
|
|
1040
1050
|
}
|
|
1041
1051
|
}
|
|
@@ -1047,12 +1057,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1047
1057
|
var volatiles = &side.active.volatiles;
|
|
1048
1058
|
|
|
1049
1059
|
if (volatiles.PerishSong) {
|
|
1050
|
-
assert(volatiles.perish_song > 0);
|
|
1051
1060
|
volatiles.perish_song -= 1;
|
|
1052
1061
|
|
|
1053
1062
|
assert(volatiles.perish_song < 3);
|
|
1054
1063
|
const reason = @intFromEnum(Start.PerishSong0) + volatiles.perish_song;
|
|
1055
|
-
try options.log.start(battle.active(player), @enumFromInt(reason));
|
|
1064
|
+
try options.log.start(.{ battle.active(player), @as(Start, @enumFromInt(reason)) });
|
|
1056
1065
|
|
|
1057
1066
|
if (volatiles.perish_song == 0) {
|
|
1058
1067
|
volatiles.PerishSong = false;
|
|
@@ -1070,14 +1079,14 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1070
1079
|
const before = stored.hp;
|
|
1071
1080
|
stored.hp = @min(stored.hp + @max(stored.stats.hp / 16, 1), stored.stats.hp);
|
|
1072
1081
|
if (stored.hp > before) {
|
|
1073
|
-
try options.log.heal(.{ battle.active(player), stored,
|
|
1082
|
+
try options.log.heal(.{ battle.active(player), stored, .Leftovers });
|
|
1074
1083
|
}
|
|
1075
1084
|
}
|
|
1076
1085
|
}
|
|
1077
1086
|
|
|
1078
1087
|
// Mystery Berry
|
|
1079
1088
|
inline for (players) |player| {
|
|
1080
|
-
|
|
1089
|
+
const stored = battle.side(player).stored();
|
|
1081
1090
|
|
|
1082
1091
|
if (stored.item == .MysteryBerry) {
|
|
1083
1092
|
// TODO stored and active moves, transform, etc
|
|
@@ -1092,7 +1101,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1092
1101
|
!side.active.volatiles.frozen and
|
|
1093
1102
|
try Rolls.defrost(battle, player, options))
|
|
1094
1103
|
{
|
|
1095
|
-
try options.log.curestatus(battle.active(player), side.stored().status, .Message);
|
|
1104
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1096
1105
|
side.stored().status = 0;
|
|
1097
1106
|
}
|
|
1098
1107
|
}
|
|
@@ -1133,7 +1142,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1133
1142
|
stored.hp < stored.stats.hp;
|
|
1134
1143
|
if (proc) {
|
|
1135
1144
|
assert(stored.hp != 0);
|
|
1136
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1145
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1137
1146
|
stored.item = .None;
|
|
1138
1147
|
|
|
1139
1148
|
const offset = num - @intFromEnum(Item.Berry);
|
|
@@ -1150,7 +1159,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1150
1159
|
|
|
1151
1160
|
if (proc) {
|
|
1152
1161
|
assert(Status.any(stored.status));
|
|
1153
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1162
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1154
1163
|
stored.item = .None;
|
|
1155
1164
|
|
|
1156
1165
|
// FIXME wtf?
|
|
@@ -1158,21 +1167,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1158
1167
|
// side.active.volatiles.Nightmare = false;
|
|
1159
1168
|
if (stored.item == .MiracleBerry) {
|
|
1160
1169
|
side.active.volatiles.Confusion = false;
|
|
1161
|
-
try options.log.end(ident, .Confusion);
|
|
1170
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1162
1171
|
}
|
|
1163
1172
|
stored.status = 0;
|
|
1164
1173
|
|
|
1165
1174
|
// FIXME recalc stats
|
|
1166
1175
|
|
|
1167
|
-
try options.log.curestatus(ident, stored.status, .Message);
|
|
1176
|
+
try options.log.curestatus(.{ ident, stored.status, .Message });
|
|
1168
1177
|
}
|
|
1169
1178
|
} else if (num == @intFromEnum(Item.BitterBerry)) {
|
|
1170
1179
|
if (side.active.volatiles.Confusion) {
|
|
1171
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1180
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1172
1181
|
stored.item = .None;
|
|
1173
1182
|
|
|
1174
1183
|
side.active.volatiles.Confusion = false;
|
|
1175
|
-
try options.log.end(ident, .Confusion);
|
|
1184
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1176
1185
|
}
|
|
1177
1186
|
}
|
|
1178
1187
|
}
|
|
@@ -1184,12 +1193,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1184
1193
|
var volatiles = &side.active.volatiles;
|
|
1185
1194
|
|
|
1186
1195
|
if (volatiles.Encore) {
|
|
1187
|
-
assert(volatiles.encore > 0);
|
|
1188
1196
|
volatiles.encore -= 1;
|
|
1189
1197
|
|
|
1190
1198
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
1191
1199
|
volatiles.Encore = false;
|
|
1192
|
-
try options.log.end(battle.active(player), .Encore);
|
|
1200
|
+
try options.log.end(.{ battle.active(player), .Encore });
|
|
1193
1201
|
}
|
|
1194
1202
|
}
|
|
1195
1203
|
}
|
|
@@ -1206,12 +1214,11 @@ fn updateSideCondition(
|
|
|
1206
1214
|
) !void {
|
|
1207
1215
|
var side = battle.side(player);
|
|
1208
1216
|
if (@field(side.conditions, key)) {
|
|
1209
|
-
assert(@field(side.conditions, value) > 0);
|
|
1210
1217
|
@field(side.conditions, value) -= 1;
|
|
1211
1218
|
|
|
1212
1219
|
if (@field(side.conditions, value) == 0) {
|
|
1213
1220
|
@field(side.conditions, key) = false;
|
|
1214
|
-
try options.log.sideend(player, @field(protocol.Side, key));
|
|
1221
|
+
try options.log.sideend(.{ player, @field(protocol.Side, key) });
|
|
1215
1222
|
}
|
|
1216
1223
|
}
|
|
1217
1224
|
}
|
|
@@ -1220,13 +1227,13 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
|
|
|
1220
1227
|
battle.turn += 1;
|
|
1221
1228
|
|
|
1222
1229
|
if (showdown and battle.turn >= 1000) {
|
|
1223
|
-
try options.log.tie();
|
|
1230
|
+
try options.log.tie(.{});
|
|
1224
1231
|
return Result.Tie;
|
|
1225
1232
|
} else if (battle.turn >= 65535) {
|
|
1226
1233
|
return Result.Error;
|
|
1227
1234
|
}
|
|
1228
1235
|
|
|
1229
|
-
try options.log.turn(battle.turn);
|
|
1236
|
+
try options.log.turn(.{battle.turn});
|
|
1230
1237
|
|
|
1231
1238
|
return Result.Default;
|
|
1232
1239
|
}
|
|
@@ -1242,7 +1249,7 @@ pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytyp
|
|
|
1242
1249
|
}
|
|
1243
1250
|
} else {
|
|
1244
1251
|
foe.active.volatiles.rage +|= 1;
|
|
1245
|
-
try options.log.activate(battle.active(player), .Rage);
|
|
1252
|
+
try options.log.activate(.{ battle.active(player), .Rage });
|
|
1246
1253
|
}
|
|
1247
1254
|
}
|
|
1248
1255
|
}
|
|
@@ -1265,7 +1272,7 @@ pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype)
|
|
|
1265
1272
|
|
|
1266
1273
|
if (foe.active.volatiles.DestinyBond) {
|
|
1267
1274
|
battle.side(player).stored().hp = 0;
|
|
1268
|
-
try options.log.activate(battle.active(player.foe()), .DestinyBond);
|
|
1275
|
+
try options.log.activate(.{ battle.active(player.foe()), .DestinyBond });
|
|
1269
1276
|
}
|
|
1270
1277
|
return true;
|
|
1271
1278
|
}
|
|
@@ -1278,14 +1285,14 @@ pub const Effects = struct {
|
|
|
1278
1285
|
const gender: u8 = @intFromEnum(side.stored().dvs.gender);
|
|
1279
1286
|
const foe_gender: u8 = @intFromEnum(foe.stored().dvs.gender);
|
|
1280
1287
|
if (gender + foe_gender != 1) {
|
|
1281
|
-
return options.log.immune(battle.active(player.foe()), .None);
|
|
1288
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1282
1289
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1283
|
-
try options.log.lastmiss();
|
|
1284
|
-
return options.log.miss(battle.active(player));
|
|
1290
|
+
try options.log.lastmiss(.{});
|
|
1291
|
+
return options.log.miss(.{battle.active(player)});
|
|
1285
1292
|
}
|
|
1286
1293
|
|
|
1287
1294
|
foe.active.volatiles.Attract = true;
|
|
1288
|
-
try options.log.start(battle.active(player.foe()), .Attract);
|
|
1295
|
+
try options.log.start(.{ battle.active(player.foe()), .Attract });
|
|
1289
1296
|
}
|
|
1290
1297
|
|
|
1291
1298
|
pub fn batonPass(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1297,14 +1304,32 @@ pub const Effects = struct {
|
|
|
1297
1304
|
}
|
|
1298
1305
|
|
|
1299
1306
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1307
|
+
assert(!state.miss);
|
|
1308
|
+
var side = battle.side(player);
|
|
1309
|
+
|
|
1310
|
+
const ident = battle.active(player);
|
|
1311
|
+
|
|
1312
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1313
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1314
|
+
return options.log.fail(.{ ident, .None });
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1318
|
+
if (side.stored().hp <= hp) {
|
|
1319
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1320
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1321
|
+
return options.log.fail(.{ ident, .None });
|
|
1322
|
+
} else {
|
|
1323
|
+
side.stored().hp -= hp;
|
|
1324
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1325
|
+
|
|
1326
|
+
const before = side.active.boosts.atk;
|
|
1327
|
+
var i: usize = 0;
|
|
1328
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1329
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1330
|
+
}
|
|
1331
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1332
|
+
}
|
|
1308
1333
|
}
|
|
1309
1334
|
|
|
1310
1335
|
pub const bide = struct {
|
|
@@ -1316,7 +1341,6 @@ pub const Effects = struct {
|
|
|
1316
1341
|
|
|
1317
1342
|
if (!volatiles.Bide) return;
|
|
1318
1343
|
|
|
1319
|
-
assert(volatiles.attacks > 0);
|
|
1320
1344
|
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1321
1345
|
// if (!extend) volatiles.attacks = 0;
|
|
1322
1346
|
// } else {
|
|
@@ -1324,10 +1348,10 @@ pub const Effects = struct {
|
|
|
1324
1348
|
// }
|
|
1325
1349
|
try options.chance.attacking(player, volatiles.attacks);
|
|
1326
1350
|
|
|
1327
|
-
if (volatiles.attacks != 0) return options.log.activate(ident, .Bide);
|
|
1351
|
+
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1328
1352
|
|
|
1329
1353
|
volatiles.Bide = false;
|
|
1330
|
-
try options.log.end(ident, .Bide);
|
|
1354
|
+
try options.log.end(.{ ident, .Bide });
|
|
1331
1355
|
|
|
1332
1356
|
state.damage = volatiles.bide *% 2;
|
|
1333
1357
|
volatiles.bide = 0;
|
|
@@ -1361,11 +1385,11 @@ pub const Effects = struct {
|
|
|
1361
1385
|
if (Status.any(foe_stored.status)) {
|
|
1362
1386
|
if (Status.is(foe_stored.status, .FRZ)) {
|
|
1363
1387
|
assert(Move.get(state.move).type == .Fire or state.move == .TriAttack);
|
|
1364
|
-
try options.log.curestatus(
|
|
1388
|
+
try options.log.curestatus(.{
|
|
1365
1389
|
battle.active(player.foe()),
|
|
1366
1390
|
foe_stored.status,
|
|
1367
1391
|
.Message,
|
|
1368
|
-
);
|
|
1392
|
+
});
|
|
1369
1393
|
foe_stored.status = 0;
|
|
1370
1394
|
}
|
|
1371
1395
|
return;
|
|
@@ -1380,7 +1404,8 @@ pub const Effects = struct {
|
|
|
1380
1404
|
|
|
1381
1405
|
foe_stored.status = Status.init(.BRN);
|
|
1382
1406
|
foe.active.stats.atk = @max(foe.active.stats.atk / 2, 1);
|
|
1383
|
-
|
|
1407
|
+
|
|
1408
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
1384
1409
|
|
|
1385
1410
|
// TODO: check for status berry
|
|
1386
1411
|
}
|
|
@@ -1400,7 +1425,7 @@ pub const Effects = struct {
|
|
|
1400
1425
|
for (side.active.moves) |m| {
|
|
1401
1426
|
if (convertible(side.active, m.id)) n += 1;
|
|
1402
1427
|
}
|
|
1403
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
1428
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1404
1429
|
|
|
1405
1430
|
side.active.types.type1 = try Rolls.conversion(battle, player, n, options);
|
|
1406
1431
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1410,9 +1435,9 @@ pub const Effects = struct {
|
|
|
1410
1435
|
var side = battle.side(player);
|
|
1411
1436
|
|
|
1412
1437
|
const last = battle.foe(player).lastMove(false);
|
|
1413
|
-
if (last == .None) return options.log.fail(battle.active(player), .None);
|
|
1438
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1414
1439
|
const move = Move.get(last);
|
|
1415
|
-
if (move.type == .@"???") return options.log.fail(battle.active(player), .None);
|
|
1440
|
+
if (move.type == .@"???") return options.log.fail(.{ battle.active(player), .None });
|
|
1416
1441
|
|
|
1417
1442
|
side.active.types.type1 = try Rolls.conversion2(battle, player, move.type, options);
|
|
1418
1443
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1454,41 +1479,41 @@ pub const Effects = struct {
|
|
|
1454
1479
|
|
|
1455
1480
|
if (side.active.types.includes(.Ghost)) {
|
|
1456
1481
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1457
|
-
try options.log.lastmiss();
|
|
1458
|
-
return options.log.miss(battle.active(player));
|
|
1482
|
+
try options.log.lastmiss(.{});
|
|
1483
|
+
return options.log.miss(.{battle.active(player)});
|
|
1459
1484
|
} else if (foe.active.volatiles.Substitute) {
|
|
1460
|
-
return options.log.
|
|
1485
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1461
1486
|
} else if (foe.active.volatiles.Curse) {
|
|
1462
|
-
return options.log.fail(foe_ident, .None);
|
|
1487
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1463
1488
|
}
|
|
1464
1489
|
|
|
1465
1490
|
foe.active.volatiles.Curse = true;
|
|
1466
|
-
try options.log.start(foe_ident, .Curse);
|
|
1491
|
+
try options.log.start(.{ foe_ident, .Curse });
|
|
1467
1492
|
|
|
1468
1493
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1469
|
-
try options.log.damage(ident, side.stored(), .Curse);
|
|
1494
|
+
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1470
1495
|
} else {
|
|
1471
|
-
|
|
1472
|
-
|
|
1496
|
+
const stats = &side.active.stats;
|
|
1497
|
+
const boosts = &side.active.boosts;
|
|
1473
1498
|
|
|
1474
1499
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1475
1500
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
1476
|
-
if (max_atk and max_def) return options.log.fail(ident, .None);
|
|
1501
|
+
if (max_atk and max_def) return options.log.fail(.{ ident, .None });
|
|
1477
1502
|
|
|
1478
1503
|
if (!(boosts.spe == -6 or stats.spe == 1)) {
|
|
1479
1504
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
1480
1505
|
try unboostStat(battle, side, stats, boosts, "spe", 1);
|
|
1481
|
-
try options.log.boost(ident, .Speed, @as(i8, -1));
|
|
1506
|
+
try options.log.boost(.{ ident, .Speed, @as(i8, -1) });
|
|
1482
1507
|
}
|
|
1483
1508
|
if (!max_atk) {
|
|
1484
1509
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
1485
1510
|
try boostStat(battle, side, stats, boosts, "atk", 1);
|
|
1486
|
-
try options.log.boost(ident, .Attack, @as(i8, 1));
|
|
1511
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 1) });
|
|
1487
1512
|
}
|
|
1488
1513
|
if (!max_def) {
|
|
1489
1514
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
1490
1515
|
try boostStat(battle, side, stats, boosts, "def", 1);
|
|
1491
|
-
try options.log.boost(ident, .Defense, @as(i8, 1));
|
|
1516
|
+
try options.log.boost(.{ ident, .Defense, @as(i8, 1) });
|
|
1492
1517
|
}
|
|
1493
1518
|
}
|
|
1494
1519
|
}
|
|
@@ -1500,14 +1525,13 @@ pub const Effects = struct {
|
|
|
1500
1525
|
pub fn defrost(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1501
1526
|
var side = battle.side(player);
|
|
1502
1527
|
assert(Status.is(side.stored().status, .FRZ));
|
|
1503
|
-
|
|
1504
|
-
try options.log.curestatus(battle.active(player), side.stored().status, .Message);
|
|
1528
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1505
1529
|
side.stored().status = 0;
|
|
1506
1530
|
}
|
|
1507
1531
|
|
|
1508
1532
|
pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1509
1533
|
battle.side(player).active.volatiles.DestinyBond = true;
|
|
1510
|
-
try options.log.singlemove(battle.active(player), Move.DestinyBond);
|
|
1534
|
+
try options.log.singlemove(.{ battle.active(player), Move.DestinyBond });
|
|
1511
1535
|
}
|
|
1512
1536
|
|
|
1513
1537
|
pub fn disable(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -1515,14 +1539,10 @@ pub const Effects = struct {
|
|
|
1515
1539
|
var volatiles = &foe.active.volatiles;
|
|
1516
1540
|
const foe_ident = battle.active(player.foe());
|
|
1517
1541
|
|
|
1518
|
-
if (volatiles.disable.move != 0) {
|
|
1519
|
-
return options.log.fail(foe_ident, .None);
|
|
1520
|
-
}
|
|
1542
|
+
if (volatiles.disable.move != 0) return options.log.fail(.{ foe_ident, .None });
|
|
1521
1543
|
|
|
1522
1544
|
const last = battle.foe(player).lastMove(false);
|
|
1523
|
-
if (last == .None or last == .Struggle) {
|
|
1524
|
-
return options.log.fail(foe_ident, .None);
|
|
1525
|
-
}
|
|
1545
|
+
if (last == .None or last == .Struggle) return options.log.fail(.{ foe_ident, .None });
|
|
1526
1546
|
|
|
1527
1547
|
var slot: u3 = 0;
|
|
1528
1548
|
for (foe.active.moves) |m| {
|
|
@@ -1531,12 +1551,12 @@ pub const Effects = struct {
|
|
|
1531
1551
|
if (m.id == last) break;
|
|
1532
1552
|
}
|
|
1533
1553
|
const move = foe.active.move(slot);
|
|
1534
|
-
if (move.pp == 0) return options.log.fail(foe_ident, .None);
|
|
1554
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1535
1555
|
|
|
1536
1556
|
volatiles.disable.move = slot;
|
|
1537
1557
|
volatiles.disable.duration = Rolls.disableDuration(battle, player, options);
|
|
1538
1558
|
|
|
1539
|
-
try options.log.
|
|
1559
|
+
try options.log.start(.{ foe_ident, .Disable, move.id });
|
|
1540
1560
|
}
|
|
1541
1561
|
|
|
1542
1562
|
pub const double = struct {
|
|
@@ -1555,13 +1575,14 @@ pub const Effects = struct {
|
|
|
1555
1575
|
|
|
1556
1576
|
pub fn drainHP(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1557
1577
|
var stored = battle.side(player).stored();
|
|
1578
|
+
const ident = battle.active(player);
|
|
1558
1579
|
|
|
1559
1580
|
assert(state.damage != 0);
|
|
1560
1581
|
|
|
1561
1582
|
if (stored.hp == stored.stats.hp) return;
|
|
1562
1583
|
stored.hp = @min(stored.stats.hp, stored.hp + @max(state.damage / 2, 1));
|
|
1563
1584
|
|
|
1564
|
-
try options.log.
|
|
1585
|
+
try options.log.heal(.{ ident, stored, .Drain, battle.active(player.foe()) });
|
|
1565
1586
|
}
|
|
1566
1587
|
|
|
1567
1588
|
pub fn encore(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1573,7 +1594,7 @@ pub const Effects = struct {
|
|
|
1573
1594
|
const failed = volatiles.Encore or
|
|
1574
1595
|
(last == .None or last == .Struggle or
|
|
1575
1596
|
last == .Encore or .last == .MirrorMove);
|
|
1576
|
-
if (failed) return options.log.fail(foe_ident, .None);
|
|
1597
|
+
if (failed) return options.log.fail(.{ foe_ident, .None });
|
|
1577
1598
|
|
|
1578
1599
|
var slot: u3 = 0;
|
|
1579
1600
|
for (foe.active.moves) |m| {
|
|
@@ -1582,7 +1603,7 @@ pub const Effects = struct {
|
|
|
1582
1603
|
if (m.id == last) break;
|
|
1583
1604
|
}
|
|
1584
1605
|
const move = foe.active.move(slot);
|
|
1585
|
-
if (move.pp == 0) return options.log.fail(foe_ident, .None);
|
|
1606
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1586
1607
|
|
|
1587
1608
|
volatiles.Encore = true;
|
|
1588
1609
|
volatiles.encore = Rolls.encoreDuration(battle, player, options);
|
|
@@ -1590,11 +1611,19 @@ pub const Effects = struct {
|
|
|
1590
1611
|
// TODO ???
|
|
1591
1612
|
_ = state;
|
|
1592
1613
|
|
|
1593
|
-
try options.log.
|
|
1614
|
+
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1594
1615
|
}
|
|
1595
1616
|
|
|
1596
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1597
|
-
|
|
1617
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1618
|
+
var side = battle.side(player);
|
|
1619
|
+
var stored = side.stored();
|
|
1620
|
+
|
|
1621
|
+
stored.hp = 0;
|
|
1622
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1623
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1624
|
+
if (!showdown) stored.status = 0;
|
|
1625
|
+
side.active.volatiles.LeechSeed = false;
|
|
1626
|
+
side.active.volatiles.DestinyBond = false;
|
|
1598
1627
|
}
|
|
1599
1628
|
|
|
1600
1629
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1644,12 +1673,11 @@ pub const Effects = struct {
|
|
|
1644
1673
|
var side = battle.side(player);
|
|
1645
1674
|
|
|
1646
1675
|
if (side.active.volatiles.FocusEnergy) {
|
|
1647
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
1648
|
-
return;
|
|
1676
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
1649
1677
|
}
|
|
1650
1678
|
|
|
1651
1679
|
side.active.volatiles.FocusEnergy = true;
|
|
1652
|
-
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1680
|
+
try options.log.start(.{ battle.active(player), .FocusEnergy });
|
|
1653
1681
|
}
|
|
1654
1682
|
|
|
1655
1683
|
var SLOTS: [5]u4 = .{ 0, 0, 0, 0, 0 };
|
|
@@ -1659,7 +1687,7 @@ pub const Effects = struct {
|
|
|
1659
1687
|
|
|
1660
1688
|
const foe = battle.foe(player);
|
|
1661
1689
|
|
|
1662
|
-
if (state.first) return options.log.fail(battle.active(player), .None);
|
|
1690
|
+
if (state.first) return options.log.fail(.{ battle.active(player), .None });
|
|
1663
1691
|
|
|
1664
1692
|
var n: u4 = 0;
|
|
1665
1693
|
var i: u4 = 0;
|
|
@@ -1673,7 +1701,7 @@ pub const Effects = struct {
|
|
|
1673
1701
|
}
|
|
1674
1702
|
}
|
|
1675
1703
|
|
|
1676
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
1704
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1677
1705
|
|
|
1678
1706
|
const slot = try Rolls.forceSwitch(battle, player, SLOTS[0..i], n, options);
|
|
1679
1707
|
try switchIn(battle, player.foe(), slot, .Drag, options);
|
|
@@ -1683,14 +1711,14 @@ pub const Effects = struct {
|
|
|
1683
1711
|
var foe = battle.foe(player);
|
|
1684
1712
|
|
|
1685
1713
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1686
|
-
try options.log.lastmiss();
|
|
1687
|
-
return options.log.miss(battle.active(player));
|
|
1714
|
+
try options.log.lastmiss(.{});
|
|
1715
|
+
return options.log.miss(.{battle.active(player)});
|
|
1688
1716
|
} else if (foe.active.volatiles.Foresight) {
|
|
1689
|
-
return options.log.fail(battle.active(player), .None);
|
|
1717
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
1690
1718
|
}
|
|
1691
1719
|
|
|
1692
1720
|
foe.active.volatiles.Foresight = true;
|
|
1693
|
-
try options.log.start(battle.active(player.foe()), .FocusEnergy);
|
|
1721
|
+
try options.log.start(.{ battle.active(player.foe()), .FocusEnergy });
|
|
1694
1722
|
}
|
|
1695
1723
|
|
|
1696
1724
|
pub fn freezeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1713,7 +1741,7 @@ pub const Effects = struct {
|
|
|
1713
1741
|
if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1714
1742
|
|
|
1715
1743
|
foe_stored.status = Status.init(.FRZ);
|
|
1716
|
-
try options.log.status(foe_ident, foe_stored.status, .None);
|
|
1744
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
1717
1745
|
|
|
1718
1746
|
// TODO: check for status berry
|
|
1719
1747
|
|
|
@@ -1721,8 +1749,26 @@ pub const Effects = struct {
|
|
|
1721
1749
|
foe.active.volatiles.frozen = true;
|
|
1722
1750
|
}
|
|
1723
1751
|
|
|
1724
|
-
|
|
1725
|
-
|
|
1752
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1753
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1754
|
+
var side = battle.side(player);
|
|
1755
|
+
var volatiles = &side.active.volatiles;
|
|
1756
|
+
|
|
1757
|
+
if (state.miss) {
|
|
1758
|
+
volatiles.fury_cutter = 0;
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1763
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1764
|
+
volatiles.fury_cutter += 1;
|
|
1765
|
+
|
|
1766
|
+
if (showdown) {
|
|
1767
|
+
state.bp *= 2;
|
|
1768
|
+
assert(state.bp <= 160);
|
|
1769
|
+
} else {
|
|
1770
|
+
state.damage *|= 2;
|
|
1771
|
+
}
|
|
1726
1772
|
}
|
|
1727
1773
|
|
|
1728
1774
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1748,7 +1794,7 @@ pub const Effects = struct {
|
|
|
1748
1794
|
inline for (STATS) |s| @field(foe.active.stats, s) = @field(stats, s);
|
|
1749
1795
|
statusModify(foe.stored().status, &foe.active.stats);
|
|
1750
1796
|
|
|
1751
|
-
try options.log.clearallboost();
|
|
1797
|
+
try options.log.clearallboost(.{});
|
|
1752
1798
|
}
|
|
1753
1799
|
|
|
1754
1800
|
pub fn heal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1756,7 +1802,7 @@ pub const Effects = struct {
|
|
|
1756
1802
|
var stored = side.stored();
|
|
1757
1803
|
const ident = battle.active(player);
|
|
1758
1804
|
|
|
1759
|
-
if (stored.stats.hp == stored.hp) return options.log.fail(ident, .None);
|
|
1805
|
+
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident, .None });
|
|
1760
1806
|
|
|
1761
1807
|
const rest = state.move == .Rest;
|
|
1762
1808
|
if (rest) {
|
|
@@ -1765,7 +1811,7 @@ pub const Effects = struct {
|
|
|
1765
1811
|
side.active.volatiles.Toxic = false;
|
|
1766
1812
|
stored.status = Status.slf(2);
|
|
1767
1813
|
recomputeStats(battle, side);
|
|
1768
|
-
try options.log.
|
|
1814
|
+
try options.log.status(.{ ident, stored.status, .From, Move.Rest });
|
|
1769
1815
|
stored.hp = stored.stats.hp;
|
|
1770
1816
|
} else {
|
|
1771
1817
|
stored.hp = @min(stored.stats.hp, stored.hp + (stored.stats.hp / 2));
|
|
@@ -1777,7 +1823,7 @@ pub const Effects = struct {
|
|
|
1777
1823
|
var side = battle.side(player);
|
|
1778
1824
|
side.active.volatiles.Nightmare = false;
|
|
1779
1825
|
|
|
1780
|
-
try options.log.cureteam(battle.active(player));
|
|
1826
|
+
try options.log.cureteam(.{battle.active(player)});
|
|
1781
1827
|
for (&side.pokemon) |*p| p.status = 0;
|
|
1782
1828
|
|
|
1783
1829
|
// We technically only need to recompute stats if the active Pokémon was burned/paralyzed,
|
|
@@ -1793,7 +1839,7 @@ pub const Effects = struct {
|
|
|
1793
1839
|
|
|
1794
1840
|
pub fn hyperBeam(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1795
1841
|
battle.side(player).active.volatiles.Recharging = true;
|
|
1796
|
-
try options.log.mustrecharge(battle.active(player));
|
|
1842
|
+
try options.log.mustrecharge(.{battle.active(player)});
|
|
1797
1843
|
}
|
|
1798
1844
|
|
|
1799
1845
|
pub fn leechSeed(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1803,12 +1849,12 @@ pub const Effects = struct {
|
|
|
1803
1849
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
1804
1850
|
const invulnerable = foe.active.volatiles.Underground or foe.active.volatiles.Flying;
|
|
1805
1851
|
if (!invulnerable and foe.active.types.includes(.Grass)) {
|
|
1806
|
-
return options.log.immune(battle.active(player.foe()), .None);
|
|
1852
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1807
1853
|
}
|
|
1808
1854
|
try checkHit(battle, player, state, options);
|
|
1809
1855
|
if (state.miss) {
|
|
1810
|
-
try options.log.lastmiss();
|
|
1811
|
-
return options.log.miss(battle.active(player));
|
|
1856
|
+
try options.log.lastmiss(.{});
|
|
1857
|
+
return options.log.miss(.{battle.active(player)});
|
|
1812
1858
|
}
|
|
1813
1859
|
if (foe.active.volatiles.LeechSeed) return;
|
|
1814
1860
|
} else {
|
|
@@ -1816,14 +1862,14 @@ pub const Effects = struct {
|
|
|
1816
1862
|
const miss =
|
|
1817
1863
|
state.miss or foe.active.types.includes(.Grass) or foe.active.volatiles.LeechSeed;
|
|
1818
1864
|
if (miss) {
|
|
1819
|
-
try options.log.lastmiss();
|
|
1820
|
-
return options.log.miss(battle.active(player));
|
|
1865
|
+
try options.log.lastmiss(.{});
|
|
1866
|
+
return options.log.miss(.{battle.active(player)});
|
|
1821
1867
|
}
|
|
1822
1868
|
}
|
|
1823
1869
|
|
|
1824
1870
|
foe.active.volatiles.LeechSeed = true;
|
|
1825
1871
|
|
|
1826
|
-
try options.log.start(battle.active(player.foe()), .LeechSeed);
|
|
1872
|
+
try options.log.start(.{ battle.active(player.foe()), .LeechSeed });
|
|
1827
1873
|
}
|
|
1828
1874
|
|
|
1829
1875
|
pub fn lockOn(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1831,20 +1877,20 @@ pub const Effects = struct {
|
|
|
1831
1877
|
const foe_ident = battle.active(player.foe());
|
|
1832
1878
|
|
|
1833
1879
|
if (foe.active.volatiles.Substitute) {
|
|
1834
|
-
return options.log.
|
|
1880
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1835
1881
|
} else if (state.miss) {
|
|
1836
|
-
return options.log.fail(foe_ident, .None);
|
|
1882
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1837
1883
|
}
|
|
1838
1884
|
|
|
1839
1885
|
foe.active.volatiles.LockOn = true;
|
|
1840
|
-
try options.log.activate(battle.active(player), .LockOn);
|
|
1886
|
+
try options.log.activate(.{ battle.active(player), .LockOn });
|
|
1841
1887
|
}
|
|
1842
1888
|
|
|
1843
1889
|
const MAGNITUDE_POWER = [_]u8{ 10, 30, 50, 70, 90, 110, 150 };
|
|
1844
1890
|
|
|
1845
1891
|
pub fn magnitude(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1846
1892
|
const num = try Rolls.magnitude(battle, player, options);
|
|
1847
|
-
try options.log.
|
|
1893
|
+
try options.log.activate(.{ battle.active(player), .Magnitude, num });
|
|
1848
1894
|
state.bp = MAGNITUDE_POWER[num - 4];
|
|
1849
1895
|
}
|
|
1850
1896
|
|
|
@@ -1852,11 +1898,11 @@ pub const Effects = struct {
|
|
|
1852
1898
|
var foe = battle.foe(player);
|
|
1853
1899
|
|
|
1854
1900
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1855
|
-
if (!showdown) return options.log.fail(battle.active(player.foe()), .None);
|
|
1856
|
-
try options.log.lastmiss();
|
|
1857
|
-
return options.log.miss(battle.active(player));
|
|
1901
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1902
|
+
try options.log.lastmiss(.{});
|
|
1903
|
+
return options.log.miss(.{battle.active(player)});
|
|
1858
1904
|
} else if (foe.active.volatiles.Trapped) {
|
|
1859
|
-
return options.log.fail(battle.active(player.foe()), .None);
|
|
1905
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1860
1906
|
}
|
|
1861
1907
|
|
|
1862
1908
|
foe.active.volatiles.Trapped = true;
|
|
@@ -1883,7 +1929,29 @@ pub const Effects = struct {
|
|
|
1883
1929
|
}
|
|
1884
1930
|
|
|
1885
1931
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1886
|
-
|
|
1932
|
+
var side = battle.side(player);
|
|
1933
|
+
|
|
1934
|
+
if (state.miss) {
|
|
1935
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1936
|
+
try options.log.lastmiss(.{});
|
|
1937
|
+
return options.log.miss(.{battle.active(player)});
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
const move = battle.foe(player).lastMove(false);
|
|
1941
|
+
if (move == .None or move == .Struggle) {
|
|
1942
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
var slot: u4 = 0;
|
|
1946
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1947
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1948
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1949
|
+
}
|
|
1950
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1951
|
+
|
|
1952
|
+
side.active.move(slot).id = move;
|
|
1953
|
+
side.active.move(slot).pp = 5;
|
|
1954
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1887
1955
|
}
|
|
1888
1956
|
|
|
1889
1957
|
pub fn mirrorMove(
|
|
@@ -1900,11 +1968,11 @@ pub const Effects = struct {
|
|
|
1900
1968
|
|
|
1901
1969
|
const last = foe.lastMove(false);
|
|
1902
1970
|
|
|
1903
|
-
if (last == .None) return options.log.fail(battle.active(player), .None);
|
|
1971
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1904
1972
|
const knows = knows: for (side.active.moves) |m| {
|
|
1905
1973
|
if (last == m.id) break :knows true;
|
|
1906
1974
|
} else false;
|
|
1907
|
-
if (knows) return options.log.fail(battle.active(player), .None);
|
|
1975
|
+
if (knows) return options.log.fail(.{ battle.active(player), .None });
|
|
1908
1976
|
|
|
1909
1977
|
// if (wPlayerCharging) ???
|
|
1910
1978
|
// wPlayerCharging = 1
|
|
@@ -1918,17 +1986,17 @@ pub const Effects = struct {
|
|
|
1918
1986
|
pub fn mist(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1919
1987
|
var side = battle.side(player);
|
|
1920
1988
|
|
|
1921
|
-
if (side.active.volatiles.Mist) return options.log.fail(battle.active(player), .None);
|
|
1989
|
+
if (side.active.volatiles.Mist) return options.log.fail(.{ battle.active(player), .None });
|
|
1922
1990
|
side.active.volatiles.Mist = true;
|
|
1923
1991
|
|
|
1924
|
-
try options.log.start(battle.active(player), .Mist);
|
|
1992
|
+
try options.log.start(.{ battle.active(player), .Mist });
|
|
1925
1993
|
}
|
|
1926
1994
|
|
|
1927
1995
|
pub fn weatherHeal(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1928
1996
|
var stored = battle.side(player).stored();
|
|
1929
1997
|
const ident = battle.active(player);
|
|
1930
1998
|
|
|
1931
|
-
if (stored.hp == stored.stats.hp) return options.log.fail(ident, .None);
|
|
1999
|
+
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident, .None });
|
|
1932
2000
|
|
|
1933
2001
|
stored.hp = switch (battle.field.weather) {
|
|
1934
2002
|
.Sun => stored.stats.hp,
|
|
@@ -1936,7 +2004,7 @@ pub const Effects = struct {
|
|
|
1936
2004
|
else => @min(stored.stats.hp, stored.hp + (stored.stats.hp / 4)),
|
|
1937
2005
|
};
|
|
1938
2006
|
|
|
1939
|
-
try options.log.heal(.{ ident, stored,
|
|
2007
|
+
try options.log.heal(.{ ident, stored, .None });
|
|
1940
2008
|
}
|
|
1941
2009
|
|
|
1942
2010
|
pub fn multiHit(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1949,17 +2017,17 @@ pub const Effects = struct {
|
|
|
1949
2017
|
const foe_ident = battle.active(player.foe());
|
|
1950
2018
|
|
|
1951
2019
|
if (foe_volatiles.Flying or foe_volatiles.Underground) {
|
|
1952
|
-
if (!showdown) return options.log.fail(battle.active(player.foe()), .None);
|
|
1953
|
-
try options.log.lastmiss();
|
|
1954
|
-
return options.log.miss(battle.active(player));
|
|
2020
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2021
|
+
try options.log.lastmiss(.{});
|
|
2022
|
+
return options.log.miss(.{battle.active(player)});
|
|
1955
2023
|
} else if (foe_volatiles.Substitute) {
|
|
1956
|
-
return options.log.
|
|
2024
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1957
2025
|
} else if (!Status.is(foe.stored().status, .SLP) or foe_volatiles.Nightmare) {
|
|
1958
|
-
return options.log.fail(foe_ident, .None);
|
|
2026
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1959
2027
|
}
|
|
1960
2028
|
|
|
1961
2029
|
foe_volatiles.Nightmare = true;
|
|
1962
|
-
try options.log.start(battle.active(player.foe()), .Nightmare);
|
|
2030
|
+
try options.log.start(.{ battle.active(player.foe()), .Nightmare });
|
|
1963
2031
|
}
|
|
1964
2032
|
|
|
1965
2033
|
pub const ohko = struct {
|
|
@@ -1983,9 +2051,9 @@ pub const Effects = struct {
|
|
|
1983
2051
|
const foe_ident = battle.active(player.foe());
|
|
1984
2052
|
|
|
1985
2053
|
state.damage = 0;
|
|
1986
|
-
if (state.immune()) return options.log.immune(foe_ident, .None);
|
|
2054
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
1987
2055
|
const delta = side.stored().level -| foe.stored().level;
|
|
1988
|
-
if (delta == 0) return options.log.immune(foe_ident, .OHKO);
|
|
2056
|
+
if (delta == 0) return options.log.immune(.{ foe_ident, .OHKO });
|
|
1989
2057
|
|
|
1990
2058
|
state.accuracy = 2 * delta +| Gen12.percent(30);
|
|
1991
2059
|
try checkHit(battle, player, state, options);
|
|
@@ -2001,37 +2069,39 @@ pub const Effects = struct {
|
|
|
2001
2069
|
const ident = battle.active(player);
|
|
2002
2070
|
const foe_ident = battle.active(player.foe());
|
|
2003
2071
|
|
|
2004
|
-
if (state.miss) return options.log.fail(foe_ident, .None);
|
|
2072
|
+
if (state.miss) return options.log.fail(.{ foe_ident, .None });
|
|
2005
2073
|
if (foe.active.volatiles.Substitute) {
|
|
2006
|
-
return options.log.
|
|
2074
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2007
2075
|
}
|
|
2008
2076
|
|
|
2009
2077
|
const avg = @max((foe.stored().hp + side.stored().hp) / 2, 1);
|
|
2010
2078
|
foe.stored().hp -= avg;
|
|
2011
2079
|
side.stored().hp = avg;
|
|
2012
2080
|
|
|
2013
|
-
try options.log.sethp(foe_ident, foe.stored(), .Silent);
|
|
2014
|
-
try options.log.sethp(ident, side.stored(), .None);
|
|
2081
|
+
try options.log.sethp(.{ foe_ident, foe.stored(), .Silent });
|
|
2082
|
+
try options.log.sethp(.{ ident, side.stored(), .None });
|
|
2015
2083
|
}
|
|
2016
2084
|
|
|
2017
2085
|
pub fn paralyze(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2018
2086
|
var foe = battle.foe(player);
|
|
2019
2087
|
const foe_ident = battle.active(player.foe());
|
|
2020
2088
|
|
|
2021
|
-
if (Status.is(foe.stored().status, .PAR))
|
|
2022
|
-
|
|
2023
|
-
|
|
2089
|
+
if (Status.is(foe.stored().status, .PAR)) {
|
|
2090
|
+
return options.log.fail(.{ foe_ident, .Paralysis });
|
|
2091
|
+
}
|
|
2092
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
2093
|
+
if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident, .None });
|
|
2024
2094
|
if (state.miss) {
|
|
2025
|
-
try options.log.lastmiss();
|
|
2026
|
-
return options.log.miss(battle.active(player));
|
|
2095
|
+
try options.log.lastmiss(.{});
|
|
2096
|
+
return options.log.miss(.{battle.active(player)});
|
|
2027
2097
|
} else if (foe.active.volatiles.Substitute) {
|
|
2028
|
-
return options.log.
|
|
2098
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2029
2099
|
}
|
|
2030
2100
|
|
|
2031
2101
|
foe.stored().status = Status.init(.PAR);
|
|
2032
2102
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2033
2103
|
|
|
2034
|
-
try options.log.status(foe_ident, foe.stored().status, .None);
|
|
2104
|
+
try options.log.status(.{ foe_ident, foe.stored().status, .None });
|
|
2035
2105
|
}
|
|
2036
2106
|
|
|
2037
2107
|
pub fn paralyzeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2046,14 +2116,15 @@ pub const Effects = struct {
|
|
|
2046
2116
|
|
|
2047
2117
|
foe_stored.status = Status.init(.PAR);
|
|
2048
2118
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2049
|
-
|
|
2119
|
+
|
|
2120
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2050
2121
|
|
|
2051
2122
|
// TODO: check for status berry
|
|
2052
2123
|
}
|
|
2053
2124
|
|
|
2054
2125
|
pub fn payDay(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2055
2126
|
if (!showdown or !battle.foe(player).active.volatiles.Substitute) {
|
|
2056
|
-
try options.log.fieldactivate(); // FIXME .PayDay
|
|
2127
|
+
try options.log.fieldactivate(.{}); // FIXME .PayDay
|
|
2057
2128
|
}
|
|
2058
2129
|
}
|
|
2059
2130
|
|
|
@@ -2062,17 +2133,17 @@ pub const Effects = struct {
|
|
|
2062
2133
|
var foe = battle.foe(player);
|
|
2063
2134
|
|
|
2064
2135
|
if (foe.active.volatiles.PerishSong) {
|
|
2065
|
-
return options.log.fail(battle.active(player.foe()), .None);
|
|
2136
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2066
2137
|
} else if (!side.active.volatiles.PerishSong) {
|
|
2067
2138
|
side.active.volatiles.PerishSong = true;
|
|
2068
2139
|
side.active.volatiles.perish_song = 4;
|
|
2069
|
-
try options.log.start(battle.active(player), .PerishSong3Silent);
|
|
2140
|
+
try options.log.start(.{ battle.active(player), .PerishSong3Silent });
|
|
2070
2141
|
}
|
|
2071
2142
|
foe.active.volatiles.PerishSong = true;
|
|
2072
2143
|
foe.active.volatiles.perish_song = 4;
|
|
2073
|
-
try options.log.start(battle.active(player.foe()), .PerishSong3Silent);
|
|
2144
|
+
try options.log.start(.{ battle.active(player.foe()), .PerishSong3Silent });
|
|
2074
2145
|
|
|
2075
|
-
try options.log.fieldactivate(); // FIXME .PerishSong
|
|
2146
|
+
try options.log.fieldactivate(.{}); // FIXME .PerishSong
|
|
2076
2147
|
}
|
|
2077
2148
|
|
|
2078
2149
|
pub fn poison(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2082,24 +2153,24 @@ pub const Effects = struct {
|
|
|
2082
2153
|
|
|
2083
2154
|
const toxic = state.move == .Toxic;
|
|
2084
2155
|
if (state.immune() or foe.active.types.includes(.Poison)) {
|
|
2085
|
-
return options.log.immune(foe_ident, .None);
|
|
2156
|
+
return options.log.immune(.{ foe_ident, .None });
|
|
2086
2157
|
} else if (Status.any(foe_stored.status)) {
|
|
2087
2158
|
// Pokémon Showdown considers Toxic to be a status even in Generation II and so
|
|
2088
2159
|
// will not include a fail reason for Toxic vs. Poison or vice-versa...
|
|
2089
|
-
return options.log.fail(foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2160
|
+
return options.log.fail(.{ foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2090
2161
|
if (!showdown)
|
|
2091
|
-
.Poison
|
|
2162
|
+
Fail.Poison
|
|
2092
2163
|
else if (toxic == (foe_stored.status == Status.TOX))
|
|
2093
|
-
if (toxic) .Toxic else .Poison
|
|
2164
|
+
if (toxic) Fail.Toxic else Fail.Poison
|
|
2094
2165
|
else
|
|
2095
|
-
.None
|
|
2166
|
+
Fail.None
|
|
2096
2167
|
else
|
|
2097
|
-
.None);
|
|
2168
|
+
Fail.None });
|
|
2098
2169
|
} else if (foe.active.volatiles.Substitute) {
|
|
2099
|
-
return options.log.
|
|
2170
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2100
2171
|
} else if (state.miss) {
|
|
2101
|
-
try options.log.lastmiss();
|
|
2102
|
-
return options.log.miss(battle.active(player));
|
|
2172
|
+
try options.log.lastmiss(.{});
|
|
2173
|
+
return options.log.miss(.{battle.active(player)});
|
|
2103
2174
|
}
|
|
2104
2175
|
|
|
2105
2176
|
foe_stored.status = Status.init(.PSN);
|
|
@@ -2109,7 +2180,7 @@ pub const Effects = struct {
|
|
|
2109
2180
|
foe.active.volatiles.toxic = 0;
|
|
2110
2181
|
}
|
|
2111
2182
|
|
|
2112
|
-
try options.log.status(foe_ident, foe_stored.status, .None);
|
|
2183
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2113
2184
|
|
|
2114
2185
|
// TODO: check for status berry
|
|
2115
2186
|
}
|
|
@@ -2125,7 +2196,8 @@ pub const Effects = struct {
|
|
|
2125
2196
|
if (foe.conditions.Safeguard) return;
|
|
2126
2197
|
|
|
2127
2198
|
foe_stored.status = Status.init(.PSN);
|
|
2128
|
-
|
|
2199
|
+
|
|
2200
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2129
2201
|
|
|
2130
2202
|
// TODO: check for status berry
|
|
2131
2203
|
}
|
|
@@ -2140,9 +2212,7 @@ pub const Effects = struct {
|
|
|
2140
2212
|
|
|
2141
2213
|
const failed = !state.first or volatiles.Substitute or
|
|
2142
2214
|
!try Rolls.protect(battle, player, volatiles.protect, options);
|
|
2143
|
-
if (failed) {
|
|
2144
|
-
return options.log.fail(battle.active(player), .None);
|
|
2145
|
-
}
|
|
2215
|
+
if (failed) return options.log.fail(.{ battle.active(player), .None });
|
|
2146
2216
|
|
|
2147
2217
|
volatiles.protect = @max(8, volatiles.protect + 1);
|
|
2148
2218
|
if (state.move == .Endure) {
|
|
@@ -2150,7 +2220,7 @@ pub const Effects = struct {
|
|
|
2150
2220
|
} else {
|
|
2151
2221
|
volatiles.Protect = true;
|
|
2152
2222
|
}
|
|
2153
|
-
try options.log.singleturn(battle.active(player), state.move);
|
|
2223
|
+
try options.log.singleturn(.{ battle.active(player), state.move });
|
|
2154
2224
|
}
|
|
2155
2225
|
|
|
2156
2226
|
pub fn psychUp(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2159,11 +2229,11 @@ pub const Effects = struct {
|
|
|
2159
2229
|
|
|
2160
2230
|
// Pokémon Showdown should fail when the enemy has no boosts/unboosts but doesn't
|
|
2161
2231
|
if (!showdown and @as(u32, @bitCast(foe.active.boosts)) == 0) {
|
|
2162
|
-
return options.log.fail(battle.active(player), .None);
|
|
2232
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2163
2233
|
}
|
|
2164
2234
|
|
|
2165
2235
|
side.active.boosts = foe.active.boosts;
|
|
2166
|
-
try options.log.copyboost(battle.active(player), battle.active(player.foe()));
|
|
2236
|
+
try options.log.copyboost(.{ battle.active(player), battle.active(player.foe()) });
|
|
2167
2237
|
}
|
|
2168
2238
|
|
|
2169
2239
|
pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -2174,15 +2244,14 @@ pub const Effects = struct {
|
|
|
2174
2244
|
pub const rage = struct {
|
|
2175
2245
|
pub fn start(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2176
2246
|
battle.side(player).active.volatiles.Rage = true;
|
|
2177
|
-
try options.log.singlemove(battle.active(player), Move.Rage);
|
|
2247
|
+
try options.log.singlemove(.{ battle.active(player), Move.Rage });
|
|
2178
2248
|
}
|
|
2179
2249
|
|
|
2180
2250
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2181
|
-
|
|
2182
|
-
var volatiles = &side.active.volatiles;
|
|
2251
|
+
const side = battle.side(player);
|
|
2183
2252
|
|
|
2184
|
-
assert(volatiles.Rage);
|
|
2185
|
-
state.damage *|= (volatiles.rage +| 1);
|
|
2253
|
+
assert(side.active.volatiles.Rage);
|
|
2254
|
+
state.damage *|= (side.active.volatiles.rage +| 1);
|
|
2186
2255
|
}
|
|
2187
2256
|
};
|
|
2188
2257
|
|
|
@@ -2194,18 +2263,18 @@ pub const Effects = struct {
|
|
|
2194
2263
|
var side = battle.side(player);
|
|
2195
2264
|
|
|
2196
2265
|
if (side.active.volatiles.LeechSeed) {
|
|
2197
|
-
try options.log.end(battle.active(player), .LeechSeedFrom);
|
|
2266
|
+
try options.log.end(.{ battle.active(player), .LeechSeedFrom });
|
|
2198
2267
|
side.active.volatiles.LeechSeed = false;
|
|
2199
2268
|
}
|
|
2200
2269
|
|
|
2201
2270
|
if (side.conditions.Spikes) {
|
|
2202
|
-
try options.log.
|
|
2271
|
+
try options.log.sideend(.{ player, protocol.Side.Spikes, battle.active(player) });
|
|
2203
2272
|
side.conditions.Spikes = false;
|
|
2204
2273
|
}
|
|
2205
2274
|
|
|
2206
2275
|
if (side.active.volatiles.bind.duration > 0) {
|
|
2207
2276
|
const reason = @intFromEnum(End.Bind) + side.active.volatiles.bind.reason - 1;
|
|
2208
|
-
try options.log.end(battle.active(player), @enumFromInt(reason));
|
|
2277
|
+
try options.log.end(.{ battle.active(player), @as(End, @enumFromInt(reason)) });
|
|
2209
2278
|
side.active.volatiles.bind = .{};
|
|
2210
2279
|
}
|
|
2211
2280
|
}
|
|
@@ -2213,18 +2282,14 @@ pub const Effects = struct {
|
|
|
2213
2282
|
pub fn recoil(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2214
2283
|
var side = battle.side(player);
|
|
2215
2284
|
var stored = side.stored();
|
|
2285
|
+
const ident = battle.active(player);
|
|
2216
2286
|
|
|
2217
2287
|
assert(state.damage > 0);
|
|
2218
2288
|
|
|
2219
2289
|
const damage: i16 = @intCast(@max(state.damage / 4, 1));
|
|
2220
2290
|
stored.hp = @intCast(@max(@as(i16, @intCast(stored.hp)) - damage, 0));
|
|
2221
2291
|
|
|
2222
|
-
try options.log.
|
|
2223
|
-
battle.active(player),
|
|
2224
|
-
stored,
|
|
2225
|
-
.RecoilOf,
|
|
2226
|
-
battle.active(player.foe()),
|
|
2227
|
-
);
|
|
2292
|
+
try options.log.damage(.{ ident, stored, .RecoilOf, battle.active(player.foe()) });
|
|
2228
2293
|
}
|
|
2229
2294
|
|
|
2230
2295
|
pub fn reversal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2287,7 +2352,7 @@ pub const Effects = struct {
|
|
|
2287
2352
|
|
|
2288
2353
|
pub fn sandstorm(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2289
2354
|
if (battle.field.weather != .Sandstorm) return weather(battle, .Sandstorm, options);
|
|
2290
|
-
return options.log.fail(battle.active(player), .None);
|
|
2355
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2291
2356
|
}
|
|
2292
2357
|
|
|
2293
2358
|
pub fn safeguard(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2311,11 +2376,13 @@ pub const Effects = struct {
|
|
|
2311
2376
|
) !void {
|
|
2312
2377
|
var side = battle.side(player);
|
|
2313
2378
|
|
|
2314
|
-
if (@field(side.conditions, key))
|
|
2379
|
+
if (@field(side.conditions, key)) {
|
|
2380
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2381
|
+
}
|
|
2315
2382
|
|
|
2316
2383
|
@field(side.conditions, key) = true;
|
|
2317
2384
|
@field(side.conditions, value) = 5;
|
|
2318
|
-
try options.log.sidestart(player, @field(protocol.Side, key));
|
|
2385
|
+
try options.log.sidestart(.{ player, @field(protocol.Side, key) });
|
|
2319
2386
|
}
|
|
2320
2387
|
|
|
2321
2388
|
pub fn sketch(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2326,9 +2393,9 @@ pub const Effects = struct {
|
|
|
2326
2393
|
|
|
2327
2394
|
if (showdown) {
|
|
2328
2395
|
// yes, actually "Splash". '|nothing' gets blindly mapped to '|-activate||move:Splash'
|
|
2329
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2396
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2330
2397
|
} else {
|
|
2331
|
-
try options.log.fail(battle.active(player), .None);
|
|
2398
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2332
2399
|
}
|
|
2333
2400
|
}
|
|
2334
2401
|
|
|
@@ -2338,14 +2405,14 @@ pub const Effects = struct {
|
|
|
2338
2405
|
const foe_ident = battle.active(player.foe());
|
|
2339
2406
|
|
|
2340
2407
|
if (Status.any(foe_stored.status)) {
|
|
2341
|
-
return options.log.fail(
|
|
2408
|
+
return options.log.fail(.{
|
|
2342
2409
|
foe_ident,
|
|
2343
|
-
if (Status.is(foe_stored.status, .SLP)) .Sleep else .None,
|
|
2344
|
-
);
|
|
2410
|
+
if (Status.is(foe_stored.status, .SLP)) Fail.Sleep else Fail.None,
|
|
2411
|
+
});
|
|
2345
2412
|
}
|
|
2346
2413
|
|
|
2347
2414
|
if (foe.active.volatiles.Substitute) {
|
|
2348
|
-
return options.log.
|
|
2415
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2349
2416
|
}
|
|
2350
2417
|
|
|
2351
2418
|
// Sleep Clause Mod
|
|
@@ -2356,22 +2423,21 @@ pub const Effects = struct {
|
|
|
2356
2423
|
}
|
|
2357
2424
|
|
|
2358
2425
|
foe_stored.status = Status.slp(Rolls.sleepDuration(battle, player, options));
|
|
2359
|
-
try options.log.
|
|
2426
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .From, state.move });
|
|
2360
2427
|
}
|
|
2361
2428
|
|
|
2362
2429
|
pub fn sleepTalk(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2363
2430
|
const side = battle.side(player);
|
|
2364
2431
|
|
|
2365
2432
|
if (!Status.is(side.stored().status, .SLP)) {
|
|
2366
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
2367
|
-
return;
|
|
2433
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2368
2434
|
}
|
|
2369
2435
|
|
|
2370
2436
|
var n: u3 = 0;
|
|
2371
2437
|
for (side.active.moves, 0..) |m, i| {
|
|
2372
2438
|
if (state.mslot - 1 != i and Move.get(m.id).extra.sleep_talk) n += 1;
|
|
2373
2439
|
}
|
|
2374
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
2440
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
2375
2441
|
|
|
2376
2442
|
_ = try Rolls.sleepTalk(battle, player, n, state.mslot, options); // TODO
|
|
2377
2443
|
}
|
|
@@ -2380,55 +2446,75 @@ pub const Effects = struct {
|
|
|
2380
2446
|
if (Status.is(battle.side(player).stored().status, .SLP)) return true;
|
|
2381
2447
|
state.damage = 0;
|
|
2382
2448
|
state.miss = true; // TODO is this necessary?
|
|
2383
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
2449
|
+
if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2384
2450
|
return false;
|
|
2385
2451
|
}
|
|
2386
2452
|
|
|
2387
2453
|
pub fn spikes(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2388
2454
|
var foe = battle.foe(player);
|
|
2389
|
-
if (foe.conditions.Spikes) return options.log.fail(battle.active(player), .None);
|
|
2455
|
+
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player), .None });
|
|
2390
2456
|
|
|
2391
2457
|
foe.conditions.Spikes = true;
|
|
2392
|
-
try options.log.sidestart(player.foe(), .Spikes);
|
|
2458
|
+
try options.log.sidestart(.{ player.foe(), protocol.Side.Spikes });
|
|
2393
2459
|
}
|
|
2394
2460
|
|
|
2395
2461
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2396
|
-
|
|
2397
|
-
// var foe = battle.foe(player);
|
|
2462
|
+
var foe = battle.foe(player);
|
|
2398
2463
|
|
|
2399
|
-
|
|
2464
|
+
const last = foe.lastMove(false);
|
|
2465
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2466
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2467
|
+
}
|
|
2400
2468
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
//
|
|
2406
|
-
|
|
2469
|
+
var slot: u4 = 0;
|
|
2470
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2471
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2472
|
+
}
|
|
2473
|
+
// If the move was called via Metronome it might not exist
|
|
2474
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2475
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2479
|
+
const pp = @min(
|
|
2480
|
+
foe.active.move(slot).pp,
|
|
2481
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2482
|
+
);
|
|
2483
|
+
foe.active.move(slot).pp -= pp;
|
|
2484
|
+
if (!foe.active.volatiles.Transform) {
|
|
2485
|
+
var move_slot = foe.stored().move(slot);
|
|
2486
|
+
assert(move_slot.pp > 0);
|
|
2487
|
+
|
|
2488
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2489
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2407
2493
|
}
|
|
2408
2494
|
|
|
2409
2495
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2410
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2496
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2411
2497
|
}
|
|
2412
2498
|
|
|
2413
2499
|
pub fn substitute(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2414
2500
|
var side = battle.side(player);
|
|
2415
2501
|
if (side.active.volatiles.Substitute) {
|
|
2416
|
-
return options.log.fail(battle.active(player), .Substitute);
|
|
2502
|
+
return options.log.fail(.{ battle.active(player), .Substitute });
|
|
2417
2503
|
}
|
|
2418
2504
|
|
|
2419
2505
|
assert(side.stored().stats.hp <= 1023);
|
|
2420
2506
|
const hp: u8 = @intCast(side.stored().stats.hp / 4);
|
|
2421
2507
|
|
|
2422
2508
|
if (hp == 0 or side.stored().hp <= hp) {
|
|
2423
|
-
return options.log.fail(battle.active(player), .Weak);
|
|
2509
|
+
return options.log.fail(.{ battle.active(player), .Weak });
|
|
2424
2510
|
}
|
|
2425
2511
|
|
|
2426
2512
|
side.stored().hp -= hp;
|
|
2427
2513
|
side.active.volatiles.substitute = hp;
|
|
2428
2514
|
side.active.volatiles.Substitute = true;
|
|
2429
2515
|
side.active.volatiles.bind = .{};
|
|
2430
|
-
try options.log.start(battle.active(player), .Substitute);
|
|
2431
|
-
try options.log.damage(battle.active(player), side.stored(), .None);
|
|
2516
|
+
try options.log.start(.{ battle.active(player), .Substitute });
|
|
2517
|
+
try options.log.damage(.{ battle.active(player), side.stored(), .None });
|
|
2432
2518
|
}
|
|
2433
2519
|
|
|
2434
2520
|
pub fn sunnyDay(battle: anytype, _: Player, _: *State, options: anytype) !void {
|
|
@@ -2438,14 +2524,14 @@ pub const Effects = struct {
|
|
|
2438
2524
|
fn weather(battle: anytype, w: Weather, options: anytype) !void {
|
|
2439
2525
|
battle.field.weather = w;
|
|
2440
2526
|
battle.field.weather_duration = 5;
|
|
2441
|
-
try options.log.weather(w, .None);
|
|
2527
|
+
try options.log.weather(.{ w, protocol.Weather.None });
|
|
2442
2528
|
}
|
|
2443
2529
|
|
|
2444
2530
|
pub fn teleport(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2445
2531
|
if (showdown) return;
|
|
2446
2532
|
|
|
2447
|
-
try options.log.fail(battle.active(player), .None);
|
|
2448
|
-
try options.log.laststill();
|
|
2533
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2534
|
+
try options.log.laststill(.{});
|
|
2449
2535
|
}
|
|
2450
2536
|
|
|
2451
2537
|
pub fn thief(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2460,11 +2546,11 @@ pub const Effects = struct {
|
|
|
2460
2546
|
side.stored().item = foe.stored().item;
|
|
2461
2547
|
foe.stored().item = .None;
|
|
2462
2548
|
|
|
2463
|
-
try options.log.item(
|
|
2549
|
+
try options.log.item(.{
|
|
2464
2550
|
battle.active(player),
|
|
2465
2551
|
side.stored().item,
|
|
2466
2552
|
battle.active(player.foe()),
|
|
2467
|
-
);
|
|
2553
|
+
});
|
|
2468
2554
|
}
|
|
2469
2555
|
|
|
2470
2556
|
pub fn thrashing(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2486,11 +2572,11 @@ pub const Effects = struct {
|
|
|
2486
2572
|
side.active.volatiles.dirty = false;
|
|
2487
2573
|
|
|
2488
2574
|
if (foe.active.volatiles.Transform) {
|
|
2489
|
-
return options.log.fail(battle.active(player), .None);
|
|
2575
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2490
2576
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2491
|
-
if (!showdown) return options.log.fail(battle.active(player), .None);
|
|
2492
|
-
try options.log.lastmiss();
|
|
2493
|
-
return options.log.miss(battle.active(player));
|
|
2577
|
+
if (!showdown) return options.log.fail(.{ battle.active(player), .None });
|
|
2578
|
+
try options.log.lastmiss(.{});
|
|
2579
|
+
return options.log.miss(.{battle.active(player)});
|
|
2494
2580
|
}
|
|
2495
2581
|
|
|
2496
2582
|
side.active.volatiles.Transform = true;
|
|
@@ -2512,7 +2598,7 @@ pub const Effects = struct {
|
|
|
2512
2598
|
side.active.moves[i].pp = if (m.id != .None) if (m.id == .Sketch) 1 else 5 else 0;
|
|
2513
2599
|
}
|
|
2514
2600
|
|
|
2515
|
-
try options.log.transform(battle.active(player), foe_ident);
|
|
2601
|
+
try options.log.transform(.{ battle.active(player), foe_ident });
|
|
2516
2602
|
}
|
|
2517
2603
|
|
|
2518
2604
|
pub fn triAttack(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2537,17 +2623,17 @@ pub const Effects = struct {
|
|
|
2537
2623
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2538
2624
|
var side = battle.side(player);
|
|
2539
2625
|
|
|
2540
|
-
|
|
2541
|
-
|
|
2626
|
+
const stats = &side.active.stats;
|
|
2627
|
+
const boosts = &side.active.boosts;
|
|
2542
2628
|
|
|
2543
2629
|
if (!state.proc) return;
|
|
2544
2630
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
2545
2631
|
assert(@field(boosts, s) >= -6 and @field(boosts, s) <= 6);
|
|
2546
2632
|
if (@field(boosts, s) == 6 or @field(stats, s) == MAX_STAT_VALUE) {
|
|
2547
|
-
try options.log.boost(battle.active(player), r, 0);
|
|
2633
|
+
try options.log.boost(.{ battle.active(player), r, 0 });
|
|
2548
2634
|
} else {
|
|
2549
2635
|
try boostStat(battle, side, stats, boosts, s, 1);
|
|
2550
|
-
try options.log.boost(battle.active(player), r, 1);
|
|
2636
|
+
try options.log.boost(.{ battle.active(player), r, 1 });
|
|
2551
2637
|
}
|
|
2552
2638
|
}
|
|
2553
2639
|
}
|
|
@@ -2557,51 +2643,61 @@ pub const Effects = struct {
|
|
|
2557
2643
|
var side = battle.side(player);
|
|
2558
2644
|
const ident = battle.active(player);
|
|
2559
2645
|
|
|
2560
|
-
if (state.miss) return log.fail(ident, .None);
|
|
2646
|
+
if (state.miss) return log.fail(.{ ident, .None });
|
|
2561
2647
|
|
|
2562
|
-
|
|
2648
|
+
const stats = &side.active.stats;
|
|
2563
2649
|
var boosts = &side.active.boosts;
|
|
2564
2650
|
|
|
2565
2651
|
const move = Move.get(state.move);
|
|
2566
2652
|
switch (move.effect) {
|
|
2567
2653
|
.AttackUp1, .AttackUp2, .Rage, .Swagger => {
|
|
2568
2654
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2569
|
-
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE)
|
|
2655
|
+
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE) {
|
|
2656
|
+
return log.fail(.{ ident, .None });
|
|
2657
|
+
}
|
|
2570
2658
|
const n: u2 = if (move.effect == .AttackUp2 or move.effect == .Swagger) 2 else 1;
|
|
2571
2659
|
try boostStat(battle, side, stats, boosts, "atk", n);
|
|
2572
2660
|
const reason = if (showdown and move.effect == .Rage) Boost.Rage else Boost.Attack;
|
|
2573
|
-
try log.boost(ident, reason, n);
|
|
2661
|
+
try log.boost(.{ ident, reason, n });
|
|
2574
2662
|
},
|
|
2575
2663
|
.DefenseUp1, .DefenseUp2, .SkullBash => {
|
|
2576
2664
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2577
|
-
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE)
|
|
2665
|
+
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE) {
|
|
2666
|
+
return log.fail(.{ ident, .None });
|
|
2667
|
+
}
|
|
2578
2668
|
const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
|
|
2579
2669
|
try boostStat(battle, side, stats, boosts, "def", n);
|
|
2580
|
-
try log.boost(ident, .Defense, n);
|
|
2670
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2581
2671
|
},
|
|
2582
2672
|
.SpeedUp2 => {
|
|
2583
2673
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2584
|
-
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE)
|
|
2674
|
+
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE) {
|
|
2675
|
+
return log.fail(.{ ident, .None });
|
|
2676
|
+
}
|
|
2585
2677
|
try boostStat(battle, side, stats, boosts, "spe", 2);
|
|
2586
|
-
try log.boost(ident, .Speed, 2);
|
|
2678
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2587
2679
|
},
|
|
2588
2680
|
.SpAtkUp1 => {
|
|
2589
2681
|
assert(boosts.spa >= -6 and boosts.spa <= 6);
|
|
2590
|
-
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE)
|
|
2682
|
+
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE) {
|
|
2683
|
+
return log.fail(.{ ident, .None });
|
|
2684
|
+
}
|
|
2591
2685
|
try boostStat(battle, side, stats, boosts, "spa", 1);
|
|
2592
|
-
try log.boost(ident, .SpecialAttack, 1);
|
|
2686
|
+
try log.boost(.{ ident, .SpecialAttack, 1 });
|
|
2593
2687
|
},
|
|
2594
2688
|
.SpDefUp2 => {
|
|
2595
2689
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2596
|
-
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE)
|
|
2690
|
+
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE) {
|
|
2691
|
+
return log.fail(.{ ident, .None });
|
|
2692
|
+
}
|
|
2597
2693
|
try boostStat(battle, side, stats, boosts, "spd", 2);
|
|
2598
|
-
try log.boost(ident, .SpecialDefense, 2);
|
|
2694
|
+
try log.boost(.{ ident, .SpecialDefense, 2 });
|
|
2599
2695
|
},
|
|
2600
2696
|
.EvasionUp1 => {
|
|
2601
2697
|
assert(boosts.evasion >= -6 and boosts.evasion <= 6);
|
|
2602
|
-
if (boosts.evasion == 6) return log.fail(ident, .None);
|
|
2698
|
+
if (boosts.evasion == 6) return log.fail(.{ ident, .None });
|
|
2603
2699
|
boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
|
|
2604
|
-
try log.boost(ident, .Evasion, 1);
|
|
2700
|
+
try log.boost(.{ ident, .Evasion, 1 });
|
|
2605
2701
|
if (state.move == .Minimize) side.active.volatiles.minimized = true;
|
|
2606
2702
|
},
|
|
2607
2703
|
else => unreachable,
|
|
@@ -2614,79 +2710,83 @@ pub const Effects = struct {
|
|
|
2614
2710
|
const foe_ident = battle.active(player.foe());
|
|
2615
2711
|
|
|
2616
2712
|
if (foe.active.volatiles.Mist) {
|
|
2617
|
-
try log.activate(foe_ident, .Mist);
|
|
2618
|
-
return log.fail(foe_ident, .None);
|
|
2713
|
+
try log.activate(.{ foe_ident, .Mist });
|
|
2714
|
+
return log.fail(.{ foe_ident, .None });
|
|
2619
2715
|
}
|
|
2620
2716
|
|
|
2621
2717
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2622
2718
|
|
|
2623
|
-
|
|
2719
|
+
const stats = &foe.active.stats;
|
|
2624
2720
|
var boosts = &foe.active.boosts;
|
|
2625
2721
|
|
|
2626
2722
|
const move = Move.get(state.move);
|
|
2723
|
+
const none: Fail = .None;
|
|
2627
2724
|
switch (move.effect) {
|
|
2628
2725
|
.AttackDown1, .AttackDown2, .AttackDownChance => {
|
|
2629
2726
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2630
2727
|
if (fail or boosts.atk == -6) {
|
|
2631
|
-
return if (move.effect != .AttackDownChance) try log.fail(foe_ident,
|
|
2728
|
+
return if (move.effect != .AttackDownChance) try log.fail(.{ foe_ident, none });
|
|
2632
2729
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2633
|
-
try options.log.lastmiss();
|
|
2634
|
-
return options.log.miss(battle.active(player));
|
|
2730
|
+
try options.log.lastmiss(.{});
|
|
2731
|
+
return options.log.miss(.{battle.active(player)});
|
|
2635
2732
|
} else if (stats.atk == 1) {
|
|
2636
|
-
return if (move.effect != .AttackDownChance) try log.fail(foe_ident,
|
|
2733
|
+
return if (move.effect != .AttackDownChance) try log.fail(.{ foe_ident, none });
|
|
2637
2734
|
}
|
|
2638
2735
|
const n: u2 = if (move.effect == .AttackDown2) 2 else 1;
|
|
2639
2736
|
try unboostStat(battle, foe, stats, boosts, "atk", n);
|
|
2640
|
-
try log.boost(foe_ident, .Attack, -@as(i8, n));
|
|
2737
|
+
try log.boost(.{ foe_ident, .Attack, -@as(i8, n) });
|
|
2641
2738
|
},
|
|
2642
2739
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2643
2740
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2644
2741
|
if (fail or boosts.def == -6) {
|
|
2645
|
-
|
|
2742
|
+
if (move.effect != .DefenseDownChance) try log.fail(.{ foe_ident, none });
|
|
2743
|
+
return;
|
|
2646
2744
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2647
|
-
try options.log.lastmiss();
|
|
2648
|
-
return options.log.miss(battle.active(player));
|
|
2745
|
+
try options.log.lastmiss(.{});
|
|
2746
|
+
return options.log.miss(.{battle.active(player)});
|
|
2649
2747
|
} else if (stats.def == 1) {
|
|
2650
|
-
|
|
2748
|
+
if (move.effect != .DefenseDownChance) try log.fail(.{ foe_ident, none });
|
|
2749
|
+
return;
|
|
2651
2750
|
}
|
|
2652
2751
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2653
2752
|
try unboostStat(battle, foe, stats, boosts, "def", n);
|
|
2654
|
-
try log.boost(foe_ident, .Defense, -@as(i8, n));
|
|
2753
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2655
2754
|
},
|
|
2656
2755
|
.SpeedDown1, .SpeedDown2, .SpeedDownChance => {
|
|
2657
2756
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2658
2757
|
if (fail or boosts.spe == -6) {
|
|
2659
|
-
return if (move.effect != .SpeedDownChance) try log.fail(foe_ident,
|
|
2758
|
+
return if (move.effect != .SpeedDownChance) try log.fail(.{ foe_ident, none });
|
|
2660
2759
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2661
|
-
try options.log.lastmiss();
|
|
2662
|
-
return options.log.miss(battle.active(player));
|
|
2760
|
+
try options.log.lastmiss(.{});
|
|
2761
|
+
return options.log.miss(.{battle.active(player)});
|
|
2663
2762
|
} else if (stats.spe == 1) {
|
|
2664
|
-
return if (move.effect != .SpeedDownChance) try log.fail(foe_ident,
|
|
2763
|
+
return if (move.effect != .SpeedDownChance) try log.fail(.{ foe_ident, none });
|
|
2665
2764
|
}
|
|
2666
2765
|
const n: u2 = if (move.effect == .SpeedDown2) 2 else 1;
|
|
2667
2766
|
try unboostStat(battle, foe, stats, boosts, "spe", n);
|
|
2668
|
-
try log.boost(foe_ident, .Speed, -@as(i8, n));
|
|
2767
|
+
try log.boost(.{ foe_ident, .Speed, -@as(i8, n) });
|
|
2669
2768
|
},
|
|
2670
2769
|
.SpDefDownChance => {
|
|
2671
2770
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2672
2771
|
if (fail or boosts.spd == -6) return;
|
|
2673
2772
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2674
|
-
try options.log.lastmiss();
|
|
2675
|
-
return options.log.miss(battle.active(player));
|
|
2773
|
+
try options.log.lastmiss(.{});
|
|
2774
|
+
return options.log.miss(.{battle.active(player)});
|
|
2676
2775
|
} else if (stats.spd == 1) return;
|
|
2677
2776
|
try unboostStat(battle, foe, stats, boosts, "spd", 1);
|
|
2678
|
-
try log.boost(foe_ident, .SpecialDefense, -1);
|
|
2777
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2679
2778
|
},
|
|
2680
2779
|
.AccuracyDown1, .AccuracyDownChance => {
|
|
2681
2780
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
2682
2781
|
if (fail or boosts.accuracy == -6) {
|
|
2683
|
-
|
|
2782
|
+
if (move.effect != .AccuracyDownChance) try log.fail(.{ foe_ident, none });
|
|
2783
|
+
return;
|
|
2684
2784
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2685
|
-
try options.log.lastmiss();
|
|
2686
|
-
return options.log.miss(battle.active(player));
|
|
2785
|
+
try options.log.lastmiss(.{});
|
|
2786
|
+
return options.log.miss(.{battle.active(player)});
|
|
2687
2787
|
}
|
|
2688
2788
|
boosts.accuracy = @intCast(@max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2689
|
-
try log.boost(foe_ident, .Accuracy, -1);
|
|
2789
|
+
try log.boost(.{ foe_ident, .Accuracy, -1 });
|
|
2690
2790
|
},
|
|
2691
2791
|
else => unreachable,
|
|
2692
2792
|
}
|
|
@@ -2700,9 +2800,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2700
2800
|
}
|
|
2701
2801
|
|
|
2702
2802
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2703
|
-
|
|
2803
|
+
const stats = unmodifiedStats(battle, side);
|
|
2704
2804
|
inline for (STATS) |s| {
|
|
2705
|
-
|
|
2805
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2706
2806
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2707
2807
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2708
2808
|
}
|
|
@@ -2725,7 +2825,7 @@ fn boostStat(
|
|
|
2725
2825
|
n: u2,
|
|
2726
2826
|
) !void {
|
|
2727
2827
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2728
|
-
|
|
2828
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2729
2829
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2730
2830
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2731
2831
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2748,7 +2848,7 @@ fn unboostStat(
|
|
|
2748
2848
|
n: u2,
|
|
2749
2849
|
) !void {
|
|
2750
2850
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2751
|
-
|
|
2851
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2752
2852
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2753
2853
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2754
2854
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2995,13 +3095,12 @@ pub const Rolls = struct {
|
|
|
2995
3095
|
return hits;
|
|
2996
3096
|
}
|
|
2997
3097
|
|
|
2998
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
2999
|
-
|
|
3098
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3099
|
+
assert(!showdown);
|
|
3100
|
+
const pp: u3 = if (options.calc.overridden(player, .spite)) |val|
|
|
3000
3101
|
val + 1
|
|
3001
|
-
else if (showdown)
|
|
3002
|
-
battle.rng.range(u8, 2, 6)
|
|
3003
3102
|
else
|
|
3004
|
-
(battle.rng.next() & 3) + 2;
|
|
3103
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3005
3104
|
|
|
3006
3105
|
assert(pp >= 2 and pp <= 5);
|
|
3007
3106
|
try options.chance.spite(player, pp);
|
|
@@ -3226,7 +3325,7 @@ pub const Rolls = struct {
|
|
|
3226
3325
|
|
|
3227
3326
|
const move: Move = overridden orelse move: {
|
|
3228
3327
|
if (showdown) {
|
|
3229
|
-
var r = battle.rng.range(u8, 0, @intCast(Move.METRONOME.len - n));
|
|
3328
|
+
var r = battle.rng.range(u8, 0, @as(u8, @intCast(Move.METRONOME.len - n)));
|
|
3230
3329
|
for (moves) |m| {
|
|
3231
3330
|
if (m.id == .None) break;
|
|
3232
3331
|
if (@intFromEnum(m.id) > @intFromEnum(Move.METRONOME[r])) break;
|
|
@@ -3449,7 +3548,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3449
3548
|
.Move => {
|
|
3450
3549
|
const side = battle.side(player);
|
|
3451
3550
|
|
|
3452
|
-
|
|
3551
|
+
const active = &side.active;
|
|
3453
3552
|
const stored = side.stored();
|
|
3454
3553
|
|
|
3455
3554
|
// While players are not given any input options on the cartridge in these cases,
|