@pkmn/engine 0.1.0-dev.30c63015 → 0.1.0-dev.32adfe77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -3
- package/README.md +32 -9
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +3 -2
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +1 -1
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js.map +1 -1
- package/build.zig +60 -56
- package/package.json +29 -21
- package/src/bin/install-pkmn-engine +14 -14
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/protocol.zig +211 -209
- package/src/lib/common/rational.zig +7 -3
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/gen1/calc.zig +5 -5
- package/src/lib/gen1/chance.zig +21 -21
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +18 -18
- package/src/lib/gen1/helpers.zig +7 -7
- package/src/lib/gen1/mechanics.zig +198 -187
- package/src/lib/gen2/chance.zig +27 -27
- package/src/lib/gen2/data.zig +9 -9
- package/src/lib/gen2/generated.zig +1 -3
- package/src/lib/gen2/mechanics.zig +171 -198
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +26 -3
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
|
@@ -26,15 +26,10 @@ const Result = common.Result;
|
|
|
26
26
|
|
|
27
27
|
const Activate = protocol.Activate;
|
|
28
28
|
const Boost = protocol.Boost;
|
|
29
|
-
const Cant = protocol.Cant;
|
|
30
|
-
const CureStatus = protocol.CureStatus;
|
|
31
29
|
const Damage = protocol.Damage;
|
|
32
30
|
const End = protocol.End;
|
|
33
|
-
const EndItem = protocol.EndItem;
|
|
34
31
|
const Fail = protocol.Fail;
|
|
35
32
|
const Heal = protocol.Heal;
|
|
36
|
-
const Immune = protocol.Immune;
|
|
37
|
-
const SetHP = protocol.SetHP;
|
|
38
33
|
const Start = protocol.Start;
|
|
39
34
|
|
|
40
35
|
const Gen12 = rng.Gen12;
|
|
@@ -145,11 +140,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
145
140
|
const p1 = battle.side(.P1);
|
|
146
141
|
const p2 = battle.side(.P2);
|
|
147
142
|
|
|
148
|
-
|
|
143
|
+
const p1_slot = findFirstAlive(p1);
|
|
149
144
|
assert(!showdown or p1_slot == 1);
|
|
150
145
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
151
146
|
|
|
152
|
-
|
|
147
|
+
const p2_slot = findFirstAlive(p2);
|
|
153
148
|
assert(!showdown or p2_slot == 1);
|
|
154
149
|
if (p2_slot == 0) return Result.Win;
|
|
155
150
|
|
|
@@ -265,7 +260,7 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
265
260
|
|
|
266
261
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
267
262
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
268
|
-
try options.log.damage(.{ battle.active(player), incoming,
|
|
263
|
+
try options.log.damage(.{ battle.active(player), incoming, .Spikes });
|
|
269
264
|
}
|
|
270
265
|
}
|
|
271
266
|
|
|
@@ -408,7 +403,9 @@ fn executeMove(
|
|
|
408
403
|
|
|
409
404
|
assert(choice.type == .Move);
|
|
410
405
|
var mslot: u4 = @intCast(choice.data);
|
|
406
|
+
_ = &mslot;
|
|
411
407
|
var move = Move.Pound; // TODO
|
|
408
|
+
_ = &move;
|
|
412
409
|
|
|
413
410
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
414
411
|
|
|
@@ -438,7 +435,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
438
435
|
if (volatiles.Recharging) {
|
|
439
436
|
volatiles.Recharging = false;
|
|
440
437
|
resetCant(volatiles);
|
|
441
|
-
try log.cant(.{ ident,
|
|
438
|
+
try log.cant(.{ ident, .Recharge });
|
|
442
439
|
return false;
|
|
443
440
|
}
|
|
444
441
|
|
|
@@ -457,12 +454,12 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
457
454
|
}
|
|
458
455
|
|
|
459
456
|
if (duration == 0) {
|
|
460
|
-
try log.curestatus(.{ ident, before,
|
|
457
|
+
try log.curestatus(.{ ident, before, .Message });
|
|
461
458
|
stored.status = 0; // clears EXT if present
|
|
462
459
|
resetCant(volatiles);
|
|
463
460
|
volatiles.Nightmare = false;
|
|
464
461
|
} else {
|
|
465
|
-
try log.cant(.{ ident,
|
|
462
|
+
try log.cant(.{ ident, .Sleep });
|
|
466
463
|
if (move == .Snore or move == .SleepTalk) break :slp;
|
|
467
464
|
resetCant(volatiles);
|
|
468
465
|
return false;
|
|
@@ -471,7 +468,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
471
468
|
|
|
472
469
|
if (Status.is(stored.status, .FRZ) and !(move == .FlameWheel or move == .SacredFire)) {
|
|
473
470
|
resetCant(volatiles);
|
|
474
|
-
try log.cant(.{ ident,
|
|
471
|
+
try log.cant(.{ ident, .Freeze });
|
|
475
472
|
return false;
|
|
476
473
|
}
|
|
477
474
|
|
|
@@ -481,7 +478,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
481
478
|
// if (!showdown) volatiles.Flinch = false;
|
|
482
479
|
volatiles.Flinch = false;
|
|
483
480
|
resetCant(volatiles);
|
|
484
|
-
try log.cant(.{ ident,
|
|
481
|
+
try log.cant(.{ ident, .Flinch });
|
|
485
482
|
return false;
|
|
486
483
|
}
|
|
487
484
|
|
|
@@ -495,7 +492,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
495
492
|
|
|
496
493
|
if (volatiles.disable.duration == 0) {
|
|
497
494
|
volatiles.disable.move = 0;
|
|
498
|
-
try log.end(.{ ident,
|
|
495
|
+
try log.end(.{ ident, .Disable });
|
|
499
496
|
}
|
|
500
497
|
}
|
|
501
498
|
|
|
@@ -511,9 +508,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
511
508
|
|
|
512
509
|
if (volatiles.confusion == 0) {
|
|
513
510
|
volatiles.Confusion = false;
|
|
514
|
-
try log.end(.{ ident,
|
|
511
|
+
try log.end(.{ ident, .Confusion });
|
|
515
512
|
} else {
|
|
516
|
-
try log.activate(ident, .Confusion);
|
|
513
|
+
try log.activate(.{ ident, .Confusion });
|
|
517
514
|
|
|
518
515
|
if (try Rolls.confused(battle, player, options)) {
|
|
519
516
|
volatiles.BeatUp = false;
|
|
@@ -528,11 +525,11 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
528
525
|
}
|
|
529
526
|
|
|
530
527
|
if (volatiles.Attract) {
|
|
531
|
-
try log.activate(ident, .Attract);
|
|
528
|
+
try log.activate(.{ ident, .Attract });
|
|
532
529
|
|
|
533
530
|
if (try Rolls.attract(battle, player, options)) {
|
|
534
531
|
resetCant(volatiles);
|
|
535
|
-
try log.cant(.{ ident,
|
|
532
|
+
try log.cant(.{ ident, .Attract });
|
|
536
533
|
return false;
|
|
537
534
|
}
|
|
538
535
|
}
|
|
@@ -543,14 +540,14 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
543
540
|
if (m != .None and m == move) {
|
|
544
541
|
side.active.volatiles.Charging = false;
|
|
545
542
|
resetCant(volatiles);
|
|
546
|
-
try options.log.cant(.{ ident,
|
|
543
|
+
try options.log.cant(.{ ident, .Disable, move });
|
|
547
544
|
return false;
|
|
548
545
|
}
|
|
549
546
|
}
|
|
550
547
|
|
|
551
548
|
if (Status.is(stored.status, .PAR) and try Rolls.paralyzed(battle, player, options)) {
|
|
552
549
|
resetCant(volatiles);
|
|
553
|
-
try log.cant(.{ ident,
|
|
550
|
+
try log.cant(.{ ident, .Paralysis });
|
|
554
551
|
return false;
|
|
555
552
|
}
|
|
556
553
|
|
|
@@ -570,7 +567,7 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
570
567
|
|
|
571
568
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
572
569
|
var side = battle.side(player);
|
|
573
|
-
|
|
570
|
+
const volatiles = &side.active.volatiles;
|
|
574
571
|
|
|
575
572
|
// FIXME: pass in target to avoid lookup?
|
|
576
573
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
@@ -953,26 +950,26 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
953
950
|
const damage = @max(stored.stats.hp / 8, 1);
|
|
954
951
|
stored.hp -|= damage;
|
|
955
952
|
// As above, Pokémon Showdown uses damage [of] but its not relevant
|
|
956
|
-
try options.log.damage(.{ ident, stored,
|
|
953
|
+
try options.log.damage(.{ ident, stored, .LeechSeed });
|
|
957
954
|
|
|
958
955
|
const before = foe_stored.hp;
|
|
959
956
|
foe_stored.hp = @min(foe_stored.hp + damage, foe_stored.stats.hp);
|
|
960
957
|
// Pokémon Showdown uses the less specific heal here instead of drain... because reasons?
|
|
961
|
-
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 });
|
|
962
959
|
if (stored.hp == 0) return true;
|
|
963
960
|
}
|
|
964
961
|
|
|
965
962
|
if (volatiles.Nightmare) {
|
|
966
963
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
967
964
|
// ibid
|
|
968
|
-
try options.log.damage(.{ ident, stored,
|
|
965
|
+
try options.log.damage(.{ ident, stored, .Nightmare });
|
|
969
966
|
if (stored.hp == 0) return true;
|
|
970
967
|
}
|
|
971
968
|
|
|
972
969
|
if (volatiles.Curse) {
|
|
973
970
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
974
971
|
// ibid
|
|
975
|
-
try options.log.damage(.{ ident, stored,
|
|
972
|
+
try options.log.damage(.{ ident, stored, .Curse });
|
|
976
973
|
if (stored.hp == 0) return true;
|
|
977
974
|
}
|
|
978
975
|
|
|
@@ -993,7 +990,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
993
990
|
future_sight.count -= 1;
|
|
994
991
|
assert(future_sight.count != 0);
|
|
995
992
|
if (future_sight.count == 1) {
|
|
996
|
-
try options.log.end(.{ battle.active(player),
|
|
993
|
+
try options.log.end(.{ battle.active(player), .FutureSight });
|
|
997
994
|
// TODO doMove
|
|
998
995
|
}
|
|
999
996
|
}
|
|
@@ -1002,7 +999,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1002
999
|
|
|
1003
1000
|
// Weather
|
|
1004
1001
|
if (battle.field.weather != .None) {
|
|
1005
|
-
assert(battle.field.weather_duration > 0);
|
|
1006
1002
|
battle.field.weather_duration -= 1;
|
|
1007
1003
|
|
|
1008
1004
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1019,7 +1015,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1019
1015
|
try options.log.damage(.{
|
|
1020
1016
|
battle.active(player),
|
|
1021
1017
|
side.stored(),
|
|
1022
|
-
|
|
1018
|
+
.Sandstorm,
|
|
1023
1019
|
});
|
|
1024
1020
|
}
|
|
1025
1021
|
}
|
|
@@ -1032,20 +1028,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1032
1028
|
inline for (players) |player| {
|
|
1033
1029
|
var side = battle.side(player);
|
|
1034
1030
|
var volatiles = &side.active.volatiles;
|
|
1031
|
+
const ident = battle.active(player);
|
|
1035
1032
|
|
|
1036
1033
|
if (volatiles.bind.duration > 0 and !volatiles.Substitute) {
|
|
1037
1034
|
volatiles.bind.duration -= 1;
|
|
1038
1035
|
if (volatiles.bind.duration == 0) {
|
|
1039
1036
|
const reason = @intFromEnum(End.Bind) + volatiles.bind.reason - 1;
|
|
1040
|
-
try options.log.end(.{
|
|
1037
|
+
try options.log.end(.{ ident, @as(End, @enumFromInt(reason)) });
|
|
1041
1038
|
} else {
|
|
1042
1039
|
var reason = @intFromEnum(Activate.Bind) + volatiles.bind.reason - 1;
|
|
1043
|
-
try options.log.activate(
|
|
1040
|
+
try options.log.activate(.{ ident, @as(Activate, @enumFromInt(reason)) });
|
|
1044
1041
|
|
|
1045
1042
|
reason = @intFromEnum(Damage.Bind) + volatiles.bind.reason - 1;
|
|
1046
1043
|
side.stored().hp -|= @max(side.stored().stats.hp / 16, 1);
|
|
1047
1044
|
try options.log.damage(.{
|
|
1048
|
-
|
|
1045
|
+
ident,
|
|
1049
1046
|
side.stored(),
|
|
1050
1047
|
@as(Damage, @enumFromInt(reason)),
|
|
1051
1048
|
});
|
|
@@ -1060,12 +1057,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1060
1057
|
var volatiles = &side.active.volatiles;
|
|
1061
1058
|
|
|
1062
1059
|
if (volatiles.PerishSong) {
|
|
1063
|
-
assert(volatiles.perish_song > 0);
|
|
1064
1060
|
volatiles.perish_song -= 1;
|
|
1065
1061
|
|
|
1066
1062
|
assert(volatiles.perish_song < 3);
|
|
1067
1063
|
const reason = @intFromEnum(Start.PerishSong0) + volatiles.perish_song;
|
|
1068
|
-
try options.log.start(battle.active(player), @enumFromInt(reason));
|
|
1064
|
+
try options.log.start(.{ battle.active(player), @as(Start, @enumFromInt(reason)) });
|
|
1069
1065
|
|
|
1070
1066
|
if (volatiles.perish_song == 0) {
|
|
1071
1067
|
volatiles.PerishSong = false;
|
|
@@ -1083,14 +1079,14 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1083
1079
|
const before = stored.hp;
|
|
1084
1080
|
stored.hp = @min(stored.hp + @max(stored.stats.hp / 16, 1), stored.stats.hp);
|
|
1085
1081
|
if (stored.hp > before) {
|
|
1086
|
-
try options.log.heal(.{ battle.active(player), stored,
|
|
1082
|
+
try options.log.heal(.{ battle.active(player), stored, .Leftovers });
|
|
1087
1083
|
}
|
|
1088
1084
|
}
|
|
1089
1085
|
}
|
|
1090
1086
|
|
|
1091
1087
|
// Mystery Berry
|
|
1092
1088
|
inline for (players) |player| {
|
|
1093
|
-
|
|
1089
|
+
const stored = battle.side(player).stored();
|
|
1094
1090
|
|
|
1095
1091
|
if (stored.item == .MysteryBerry) {
|
|
1096
1092
|
// TODO stored and active moves, transform, etc
|
|
@@ -1105,8 +1101,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1105
1101
|
!side.active.volatiles.frozen and
|
|
1106
1102
|
try Rolls.defrost(battle, player, options))
|
|
1107
1103
|
{
|
|
1108
|
-
|
|
1109
|
-
try options.log.curestatus(.{ battle.active(player), side.stored().status, reason });
|
|
1104
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1110
1105
|
side.stored().status = 0;
|
|
1111
1106
|
}
|
|
1112
1107
|
}
|
|
@@ -1147,7 +1142,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1147
1142
|
stored.hp < stored.stats.hp;
|
|
1148
1143
|
if (proc) {
|
|
1149
1144
|
assert(stored.hp != 0);
|
|
1150
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1145
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1151
1146
|
stored.item = .None;
|
|
1152
1147
|
|
|
1153
1148
|
const offset = num - @intFromEnum(Item.Berry);
|
|
@@ -1164,7 +1159,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1164
1159
|
|
|
1165
1160
|
if (proc) {
|
|
1166
1161
|
assert(Status.any(stored.status));
|
|
1167
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1162
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1168
1163
|
stored.item = .None;
|
|
1169
1164
|
|
|
1170
1165
|
// FIXME wtf?
|
|
@@ -1172,21 +1167,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1172
1167
|
// side.active.volatiles.Nightmare = false;
|
|
1173
1168
|
if (stored.item == .MiracleBerry) {
|
|
1174
1169
|
side.active.volatiles.Confusion = false;
|
|
1175
|
-
try options.log.end(.{ ident,
|
|
1170
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1176
1171
|
}
|
|
1177
1172
|
stored.status = 0;
|
|
1178
1173
|
|
|
1179
1174
|
// FIXME recalc stats
|
|
1180
1175
|
|
|
1181
|
-
try options.log.curestatus(.{ ident, stored.status,
|
|
1176
|
+
try options.log.curestatus(.{ ident, stored.status, .Message });
|
|
1182
1177
|
}
|
|
1183
1178
|
} else if (num == @intFromEnum(Item.BitterBerry)) {
|
|
1184
1179
|
if (side.active.volatiles.Confusion) {
|
|
1185
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1180
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1186
1181
|
stored.item = .None;
|
|
1187
1182
|
|
|
1188
1183
|
side.active.volatiles.Confusion = false;
|
|
1189
|
-
try options.log.end(.{ ident,
|
|
1184
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1190
1185
|
}
|
|
1191
1186
|
}
|
|
1192
1187
|
}
|
|
@@ -1198,12 +1193,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1198
1193
|
var volatiles = &side.active.volatiles;
|
|
1199
1194
|
|
|
1200
1195
|
if (volatiles.Encore) {
|
|
1201
|
-
assert(volatiles.encore > 0);
|
|
1202
1196
|
volatiles.encore -= 1;
|
|
1203
1197
|
|
|
1204
1198
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
1205
1199
|
volatiles.Encore = false;
|
|
1206
|
-
try options.log.end(.{ battle.active(player),
|
|
1200
|
+
try options.log.end(.{ battle.active(player), .Encore });
|
|
1207
1201
|
}
|
|
1208
1202
|
}
|
|
1209
1203
|
}
|
|
@@ -1220,7 +1214,6 @@ fn updateSideCondition(
|
|
|
1220
1214
|
) !void {
|
|
1221
1215
|
var side = battle.side(player);
|
|
1222
1216
|
if (@field(side.conditions, key)) {
|
|
1223
|
-
assert(@field(side.conditions, value) > 0);
|
|
1224
1217
|
@field(side.conditions, value) -= 1;
|
|
1225
1218
|
|
|
1226
1219
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1256,7 +1249,7 @@ pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytyp
|
|
|
1256
1249
|
}
|
|
1257
1250
|
} else {
|
|
1258
1251
|
foe.active.volatiles.rage +|= 1;
|
|
1259
|
-
try options.log.activate(battle.active(player), .Rage);
|
|
1252
|
+
try options.log.activate(.{ battle.active(player), .Rage });
|
|
1260
1253
|
}
|
|
1261
1254
|
}
|
|
1262
1255
|
}
|
|
@@ -1279,7 +1272,7 @@ pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype)
|
|
|
1279
1272
|
|
|
1280
1273
|
if (foe.active.volatiles.DestinyBond) {
|
|
1281
1274
|
battle.side(player).stored().hp = 0;
|
|
1282
|
-
try options.log.activate(battle.active(player.foe()), .DestinyBond);
|
|
1275
|
+
try options.log.activate(.{ battle.active(player.foe()), .DestinyBond });
|
|
1283
1276
|
}
|
|
1284
1277
|
return true;
|
|
1285
1278
|
}
|
|
@@ -1292,14 +1285,14 @@ pub const Effects = struct {
|
|
|
1292
1285
|
const gender: u8 = @intFromEnum(side.stored().dvs.gender);
|
|
1293
1286
|
const foe_gender: u8 = @intFromEnum(foe.stored().dvs.gender);
|
|
1294
1287
|
if (gender + foe_gender != 1) {
|
|
1295
|
-
return options.log.immune(.{ battle.active(player.foe()),
|
|
1288
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1296
1289
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1297
1290
|
try options.log.lastmiss(.{});
|
|
1298
1291
|
return options.log.miss(.{battle.active(player)});
|
|
1299
1292
|
}
|
|
1300
1293
|
|
|
1301
1294
|
foe.active.volatiles.Attract = true;
|
|
1302
|
-
try options.log.start(battle.active(player.foe()), .Attract);
|
|
1295
|
+
try options.log.start(.{ battle.active(player.foe()), .Attract });
|
|
1303
1296
|
}
|
|
1304
1297
|
|
|
1305
1298
|
pub fn batonPass(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1330,7 +1323,6 @@ pub const Effects = struct {
|
|
|
1330
1323
|
|
|
1331
1324
|
if (!volatiles.Bide) return;
|
|
1332
1325
|
|
|
1333
|
-
assert(volatiles.attacks > 0);
|
|
1334
1326
|
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1335
1327
|
// if (!extend) volatiles.attacks = 0;
|
|
1336
1328
|
// } else {
|
|
@@ -1338,10 +1330,10 @@ pub const Effects = struct {
|
|
|
1338
1330
|
// }
|
|
1339
1331
|
try options.chance.attacking(player, volatiles.attacks);
|
|
1340
1332
|
|
|
1341
|
-
if (volatiles.attacks != 0) return options.log.activate(ident, .Bide);
|
|
1333
|
+
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1342
1334
|
|
|
1343
1335
|
volatiles.Bide = false;
|
|
1344
|
-
try options.log.end(.{ ident,
|
|
1336
|
+
try options.log.end(.{ ident, .Bide });
|
|
1345
1337
|
|
|
1346
1338
|
state.damage = volatiles.bide *% 2;
|
|
1347
1339
|
volatiles.bide = 0;
|
|
@@ -1378,7 +1370,7 @@ pub const Effects = struct {
|
|
|
1378
1370
|
try options.log.curestatus(.{
|
|
1379
1371
|
battle.active(player.foe()),
|
|
1380
1372
|
foe_stored.status,
|
|
1381
|
-
|
|
1373
|
+
.Message,
|
|
1382
1374
|
});
|
|
1383
1375
|
foe_stored.status = 0;
|
|
1384
1376
|
}
|
|
@@ -1395,8 +1387,7 @@ pub const Effects = struct {
|
|
|
1395
1387
|
foe_stored.status = Status.init(.BRN);
|
|
1396
1388
|
foe.active.stats.atk = @max(foe.active.stats.atk / 2, 1);
|
|
1397
1389
|
|
|
1398
|
-
|
|
1399
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
|
|
1390
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
1400
1391
|
|
|
1401
1392
|
// TODO: check for status berry
|
|
1402
1393
|
}
|
|
@@ -1416,7 +1407,7 @@ pub const Effects = struct {
|
|
|
1416
1407
|
for (side.active.moves) |m| {
|
|
1417
1408
|
if (convertible(side.active, m.id)) n += 1;
|
|
1418
1409
|
}
|
|
1419
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
1410
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1420
1411
|
|
|
1421
1412
|
side.active.types.type1 = try Rolls.conversion(battle, player, n, options);
|
|
1422
1413
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1426,9 +1417,9 @@ pub const Effects = struct {
|
|
|
1426
1417
|
var side = battle.side(player);
|
|
1427
1418
|
|
|
1428
1419
|
const last = battle.foe(player).lastMove(false);
|
|
1429
|
-
if (last == .None) return options.log.fail(.{ battle.active(player),
|
|
1420
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1430
1421
|
const move = Move.get(last);
|
|
1431
|
-
if (move.type == .@"???") return options.log.fail(.{ battle.active(player),
|
|
1422
|
+
if (move.type == .@"???") return options.log.fail(.{ battle.active(player), .None });
|
|
1432
1423
|
|
|
1433
1424
|
side.active.types.type1 = try Rolls.conversion2(battle, player, move.type, options);
|
|
1434
1425
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1473,38 +1464,38 @@ pub const Effects = struct {
|
|
|
1473
1464
|
try options.log.lastmiss(.{});
|
|
1474
1465
|
return options.log.miss(.{battle.active(player)});
|
|
1475
1466
|
} else if (foe.active.volatiles.Substitute) {
|
|
1476
|
-
return options.log.
|
|
1467
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1477
1468
|
} else if (foe.active.volatiles.Curse) {
|
|
1478
|
-
return options.log.fail(.{ foe_ident,
|
|
1469
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1479
1470
|
}
|
|
1480
1471
|
|
|
1481
1472
|
foe.active.volatiles.Curse = true;
|
|
1482
|
-
try options.log.start(foe_ident, .Curse);
|
|
1473
|
+
try options.log.start(.{ foe_ident, .Curse });
|
|
1483
1474
|
|
|
1484
1475
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1485
|
-
try options.log.damage(.{ ident, side.stored(),
|
|
1476
|
+
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1486
1477
|
} else {
|
|
1487
|
-
|
|
1488
|
-
|
|
1478
|
+
const stats = &side.active.stats;
|
|
1479
|
+
const boosts = &side.active.boosts;
|
|
1489
1480
|
|
|
1490
1481
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1491
1482
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
1492
|
-
if (max_atk and max_def) return options.log.fail(.{ ident,
|
|
1483
|
+
if (max_atk and max_def) return options.log.fail(.{ ident, .None });
|
|
1493
1484
|
|
|
1494
1485
|
if (!(boosts.spe == -6 or stats.spe == 1)) {
|
|
1495
1486
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
1496
1487
|
try unboostStat(battle, side, stats, boosts, "spe", 1);
|
|
1497
|
-
try options.log.boost(.{ ident,
|
|
1488
|
+
try options.log.boost(.{ ident, .Speed, @as(i8, -1) });
|
|
1498
1489
|
}
|
|
1499
1490
|
if (!max_atk) {
|
|
1500
1491
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
1501
1492
|
try boostStat(battle, side, stats, boosts, "atk", 1);
|
|
1502
|
-
try options.log.boost(.{ ident,
|
|
1493
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 1) });
|
|
1503
1494
|
}
|
|
1504
1495
|
if (!max_def) {
|
|
1505
1496
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
1506
1497
|
try boostStat(battle, side, stats, boosts, "def", 1);
|
|
1507
|
-
try options.log.boost(.{ ident,
|
|
1498
|
+
try options.log.boost(.{ ident, .Defense, @as(i8, 1) });
|
|
1508
1499
|
}
|
|
1509
1500
|
}
|
|
1510
1501
|
}
|
|
@@ -1516,9 +1507,7 @@ pub const Effects = struct {
|
|
|
1516
1507
|
pub fn defrost(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1517
1508
|
var side = battle.side(player);
|
|
1518
1509
|
assert(Status.is(side.stored().status, .FRZ));
|
|
1519
|
-
|
|
1520
|
-
const reason: CureStatus = .Message;
|
|
1521
|
-
try options.log.curestatus(.{ battle.active(player), side.stored().status, reason });
|
|
1510
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1522
1511
|
side.stored().status = 0;
|
|
1523
1512
|
}
|
|
1524
1513
|
|
|
@@ -1532,14 +1521,10 @@ pub const Effects = struct {
|
|
|
1532
1521
|
var volatiles = &foe.active.volatiles;
|
|
1533
1522
|
const foe_ident = battle.active(player.foe());
|
|
1534
1523
|
|
|
1535
|
-
if (volatiles.disable.move != 0) {
|
|
1536
|
-
return options.log.fail(.{ foe_ident, Fail.None });
|
|
1537
|
-
}
|
|
1524
|
+
if (volatiles.disable.move != 0) return options.log.fail(.{ foe_ident, .None });
|
|
1538
1525
|
|
|
1539
1526
|
const last = battle.foe(player).lastMove(false);
|
|
1540
|
-
if (last == .None or last == .Struggle) {
|
|
1541
|
-
return options.log.fail(.{ foe_ident, Fail.None });
|
|
1542
|
-
}
|
|
1527
|
+
if (last == .None or last == .Struggle) return options.log.fail(.{ foe_ident, .None });
|
|
1543
1528
|
|
|
1544
1529
|
var slot: u3 = 0;
|
|
1545
1530
|
for (foe.active.moves) |m| {
|
|
@@ -1548,12 +1533,12 @@ pub const Effects = struct {
|
|
|
1548
1533
|
if (m.id == last) break;
|
|
1549
1534
|
}
|
|
1550
1535
|
const move = foe.active.move(slot);
|
|
1551
|
-
if (move.pp == 0) return options.log.fail(.{ foe_ident,
|
|
1536
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1552
1537
|
|
|
1553
1538
|
volatiles.disable.move = slot;
|
|
1554
1539
|
volatiles.disable.duration = Rolls.disableDuration(battle, player, options);
|
|
1555
1540
|
|
|
1556
|
-
try options.log.
|
|
1541
|
+
try options.log.start(.{ foe_ident, .Disable, move.id });
|
|
1557
1542
|
}
|
|
1558
1543
|
|
|
1559
1544
|
pub const double = struct {
|
|
@@ -1579,7 +1564,7 @@ pub const Effects = struct {
|
|
|
1579
1564
|
if (stored.hp == stored.stats.hp) return;
|
|
1580
1565
|
stored.hp = @min(stored.stats.hp, stored.hp + @max(state.damage / 2, 1));
|
|
1581
1566
|
|
|
1582
|
-
try options.log.heal(.{ ident, stored,
|
|
1567
|
+
try options.log.heal(.{ ident, stored, .Drain, battle.active(player.foe()) });
|
|
1583
1568
|
}
|
|
1584
1569
|
|
|
1585
1570
|
pub fn encore(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1591,7 +1576,7 @@ pub const Effects = struct {
|
|
|
1591
1576
|
const failed = volatiles.Encore or
|
|
1592
1577
|
(last == .None or last == .Struggle or
|
|
1593
1578
|
last == .Encore or .last == .MirrorMove);
|
|
1594
|
-
if (failed) return options.log.fail(.{ foe_ident,
|
|
1579
|
+
if (failed) return options.log.fail(.{ foe_ident, .None });
|
|
1595
1580
|
|
|
1596
1581
|
var slot: u3 = 0;
|
|
1597
1582
|
for (foe.active.moves) |m| {
|
|
@@ -1600,7 +1585,7 @@ pub const Effects = struct {
|
|
|
1600
1585
|
if (m.id == last) break;
|
|
1601
1586
|
}
|
|
1602
1587
|
const move = foe.active.move(slot);
|
|
1603
|
-
if (move.pp == 0) return options.log.fail(.{ foe_ident,
|
|
1588
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1604
1589
|
|
|
1605
1590
|
volatiles.Encore = true;
|
|
1606
1591
|
volatiles.encore = Rolls.encoreDuration(battle, player, options);
|
|
@@ -1608,7 +1593,7 @@ pub const Effects = struct {
|
|
|
1608
1593
|
// TODO ???
|
|
1609
1594
|
_ = state;
|
|
1610
1595
|
|
|
1611
|
-
try options.log.
|
|
1596
|
+
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1612
1597
|
}
|
|
1613
1598
|
|
|
1614
1599
|
pub fn explode(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1662,12 +1647,11 @@ pub const Effects = struct {
|
|
|
1662
1647
|
var side = battle.side(player);
|
|
1663
1648
|
|
|
1664
1649
|
if (side.active.volatiles.FocusEnergy) {
|
|
1665
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
1666
|
-
return;
|
|
1650
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
1667
1651
|
}
|
|
1668
1652
|
|
|
1669
1653
|
side.active.volatiles.FocusEnergy = true;
|
|
1670
|
-
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1654
|
+
try options.log.start(.{ battle.active(player), .FocusEnergy });
|
|
1671
1655
|
}
|
|
1672
1656
|
|
|
1673
1657
|
var SLOTS: [5]u4 = .{ 0, 0, 0, 0, 0 };
|
|
@@ -1677,7 +1661,7 @@ pub const Effects = struct {
|
|
|
1677
1661
|
|
|
1678
1662
|
const foe = battle.foe(player);
|
|
1679
1663
|
|
|
1680
|
-
if (state.first) return options.log.fail(.{ battle.active(player),
|
|
1664
|
+
if (state.first) return options.log.fail(.{ battle.active(player), .None });
|
|
1681
1665
|
|
|
1682
1666
|
var n: u4 = 0;
|
|
1683
1667
|
var i: u4 = 0;
|
|
@@ -1691,7 +1675,7 @@ pub const Effects = struct {
|
|
|
1691
1675
|
}
|
|
1692
1676
|
}
|
|
1693
1677
|
|
|
1694
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
1678
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1695
1679
|
|
|
1696
1680
|
const slot = try Rolls.forceSwitch(battle, player, SLOTS[0..i], n, options);
|
|
1697
1681
|
try switchIn(battle, player.foe(), slot, .Drag, options);
|
|
@@ -1704,11 +1688,11 @@ pub const Effects = struct {
|
|
|
1704
1688
|
try options.log.lastmiss(.{});
|
|
1705
1689
|
return options.log.miss(.{battle.active(player)});
|
|
1706
1690
|
} else if (foe.active.volatiles.Foresight) {
|
|
1707
|
-
return options.log.fail(.{ battle.active(player),
|
|
1691
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
1708
1692
|
}
|
|
1709
1693
|
|
|
1710
1694
|
foe.active.volatiles.Foresight = true;
|
|
1711
|
-
try options.log.start(battle.active(player.foe()), .FocusEnergy);
|
|
1695
|
+
try options.log.start(.{ battle.active(player.foe()), .FocusEnergy });
|
|
1712
1696
|
}
|
|
1713
1697
|
|
|
1714
1698
|
pub fn freezeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1731,7 +1715,7 @@ pub const Effects = struct {
|
|
|
1731
1715
|
if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1732
1716
|
|
|
1733
1717
|
foe_stored.status = Status.init(.FRZ);
|
|
1734
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
1718
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
1735
1719
|
|
|
1736
1720
|
// TODO: check for status berry
|
|
1737
1721
|
|
|
@@ -1774,7 +1758,7 @@ pub const Effects = struct {
|
|
|
1774
1758
|
var stored = side.stored();
|
|
1775
1759
|
const ident = battle.active(player);
|
|
1776
1760
|
|
|
1777
|
-
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident,
|
|
1761
|
+
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident, .None });
|
|
1778
1762
|
|
|
1779
1763
|
const rest = state.move == .Rest;
|
|
1780
1764
|
if (rest) {
|
|
@@ -1783,7 +1767,7 @@ pub const Effects = struct {
|
|
|
1783
1767
|
side.active.volatiles.Toxic = false;
|
|
1784
1768
|
stored.status = Status.slf(2);
|
|
1785
1769
|
recomputeStats(battle, side);
|
|
1786
|
-
try options.log.status(.{ ident, stored.status,
|
|
1770
|
+
try options.log.status(.{ ident, stored.status, .From, Move.Rest });
|
|
1787
1771
|
stored.hp = stored.stats.hp;
|
|
1788
1772
|
} else {
|
|
1789
1773
|
stored.hp = @min(stored.stats.hp, stored.hp + (stored.stats.hp / 2));
|
|
@@ -1821,7 +1805,7 @@ pub const Effects = struct {
|
|
|
1821
1805
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
1822
1806
|
const invulnerable = foe.active.volatiles.Underground or foe.active.volatiles.Flying;
|
|
1823
1807
|
if (!invulnerable and foe.active.types.includes(.Grass)) {
|
|
1824
|
-
return options.log.immune(.{ battle.active(player.foe()),
|
|
1808
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1825
1809
|
}
|
|
1826
1810
|
try checkHit(battle, player, state, options);
|
|
1827
1811
|
if (state.miss) {
|
|
@@ -1841,7 +1825,7 @@ pub const Effects = struct {
|
|
|
1841
1825
|
|
|
1842
1826
|
foe.active.volatiles.LeechSeed = true;
|
|
1843
1827
|
|
|
1844
|
-
try options.log.start(battle.active(player.foe()), .LeechSeed);
|
|
1828
|
+
try options.log.start(.{ battle.active(player.foe()), .LeechSeed });
|
|
1845
1829
|
}
|
|
1846
1830
|
|
|
1847
1831
|
pub fn lockOn(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1849,20 +1833,20 @@ pub const Effects = struct {
|
|
|
1849
1833
|
const foe_ident = battle.active(player.foe());
|
|
1850
1834
|
|
|
1851
1835
|
if (foe.active.volatiles.Substitute) {
|
|
1852
|
-
return options.log.
|
|
1836
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1853
1837
|
} else if (state.miss) {
|
|
1854
|
-
return options.log.fail(.{ foe_ident,
|
|
1838
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1855
1839
|
}
|
|
1856
1840
|
|
|
1857
1841
|
foe.active.volatiles.LockOn = true;
|
|
1858
|
-
try options.log.activate(battle.active(player), .LockOn);
|
|
1842
|
+
try options.log.activate(.{ battle.active(player), .LockOn });
|
|
1859
1843
|
}
|
|
1860
1844
|
|
|
1861
1845
|
const MAGNITUDE_POWER = [_]u8{ 10, 30, 50, 70, 90, 110, 150 };
|
|
1862
1846
|
|
|
1863
1847
|
pub fn magnitude(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1864
1848
|
const num = try Rolls.magnitude(battle, player, options);
|
|
1865
|
-
try options.log.
|
|
1849
|
+
try options.log.activate(.{ battle.active(player), .Magnitude, num });
|
|
1866
1850
|
state.bp = MAGNITUDE_POWER[num - 4];
|
|
1867
1851
|
}
|
|
1868
1852
|
|
|
@@ -1870,11 +1854,11 @@ pub const Effects = struct {
|
|
|
1870
1854
|
var foe = battle.foe(player);
|
|
1871
1855
|
|
|
1872
1856
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1873
|
-
if (!showdown) return options.log.fail(.{ battle.active(player.foe()),
|
|
1857
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1874
1858
|
try options.log.lastmiss(.{});
|
|
1875
1859
|
return options.log.miss(.{battle.active(player)});
|
|
1876
1860
|
} else if (foe.active.volatiles.Trapped) {
|
|
1877
|
-
return options.log.fail(.{ battle.active(player.foe()),
|
|
1861
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1878
1862
|
}
|
|
1879
1863
|
|
|
1880
1864
|
foe.active.volatiles.Trapped = true;
|
|
@@ -1918,11 +1902,11 @@ pub const Effects = struct {
|
|
|
1918
1902
|
|
|
1919
1903
|
const last = foe.lastMove(false);
|
|
1920
1904
|
|
|
1921
|
-
if (last == .None) return options.log.fail(.{ battle.active(player),
|
|
1905
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1922
1906
|
const knows = knows: for (side.active.moves) |m| {
|
|
1923
1907
|
if (last == m.id) break :knows true;
|
|
1924
1908
|
} else false;
|
|
1925
|
-
if (knows) return options.log.fail(.{ battle.active(player),
|
|
1909
|
+
if (knows) return options.log.fail(.{ battle.active(player), .None });
|
|
1926
1910
|
|
|
1927
1911
|
// if (wPlayerCharging) ???
|
|
1928
1912
|
// wPlayerCharging = 1
|
|
@@ -1936,19 +1920,17 @@ pub const Effects = struct {
|
|
|
1936
1920
|
pub fn mist(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1937
1921
|
var side = battle.side(player);
|
|
1938
1922
|
|
|
1939
|
-
if (side.active.volatiles.Mist) {
|
|
1940
|
-
return options.log.fail(.{ battle.active(player), Fail.None });
|
|
1941
|
-
}
|
|
1923
|
+
if (side.active.volatiles.Mist) return options.log.fail(.{ battle.active(player), .None });
|
|
1942
1924
|
side.active.volatiles.Mist = true;
|
|
1943
1925
|
|
|
1944
|
-
try options.log.start(battle.active(player), .Mist);
|
|
1926
|
+
try options.log.start(.{ battle.active(player), .Mist });
|
|
1945
1927
|
}
|
|
1946
1928
|
|
|
1947
1929
|
pub fn weatherHeal(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1948
1930
|
var stored = battle.side(player).stored();
|
|
1949
1931
|
const ident = battle.active(player);
|
|
1950
1932
|
|
|
1951
|
-
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident,
|
|
1933
|
+
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident, .None });
|
|
1952
1934
|
|
|
1953
1935
|
stored.hp = switch (battle.field.weather) {
|
|
1954
1936
|
.Sun => stored.stats.hp,
|
|
@@ -1956,7 +1938,7 @@ pub const Effects = struct {
|
|
|
1956
1938
|
else => @min(stored.stats.hp, stored.hp + (stored.stats.hp / 4)),
|
|
1957
1939
|
};
|
|
1958
1940
|
|
|
1959
|
-
try options.log.heal(.{ ident, stored,
|
|
1941
|
+
try options.log.heal(.{ ident, stored, .None });
|
|
1960
1942
|
}
|
|
1961
1943
|
|
|
1962
1944
|
pub fn multiHit(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1969,17 +1951,17 @@ pub const Effects = struct {
|
|
|
1969
1951
|
const foe_ident = battle.active(player.foe());
|
|
1970
1952
|
|
|
1971
1953
|
if (foe_volatiles.Flying or foe_volatiles.Underground) {
|
|
1972
|
-
if (!showdown) return options.log.fail(.{ battle.active(player.foe()),
|
|
1954
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1973
1955
|
try options.log.lastmiss(.{});
|
|
1974
1956
|
return options.log.miss(.{battle.active(player)});
|
|
1975
1957
|
} else if (foe_volatiles.Substitute) {
|
|
1976
|
-
return options.log.
|
|
1958
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1977
1959
|
} else if (!Status.is(foe.stored().status, .SLP) or foe_volatiles.Nightmare) {
|
|
1978
|
-
return options.log.fail(.{ foe_ident,
|
|
1960
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1979
1961
|
}
|
|
1980
1962
|
|
|
1981
1963
|
foe_volatiles.Nightmare = true;
|
|
1982
|
-
try options.log.start(battle.active(player.foe()), .Nightmare);
|
|
1964
|
+
try options.log.start(.{ battle.active(player.foe()), .Nightmare });
|
|
1983
1965
|
}
|
|
1984
1966
|
|
|
1985
1967
|
pub const ohko = struct {
|
|
@@ -2003,9 +1985,9 @@ pub const Effects = struct {
|
|
|
2003
1985
|
const foe_ident = battle.active(player.foe());
|
|
2004
1986
|
|
|
2005
1987
|
state.damage = 0;
|
|
2006
|
-
if (state.immune()) return options.log.immune(.{ foe_ident,
|
|
1988
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
2007
1989
|
const delta = side.stored().level -| foe.stored().level;
|
|
2008
|
-
if (delta == 0) return options.log.immune(.{ foe_ident,
|
|
1990
|
+
if (delta == 0) return options.log.immune(.{ foe_ident, .OHKO });
|
|
2009
1991
|
|
|
2010
1992
|
state.accuracy = 2 * delta +| Gen12.percent(30);
|
|
2011
1993
|
try checkHit(battle, player, state, options);
|
|
@@ -2021,17 +2003,17 @@ pub const Effects = struct {
|
|
|
2021
2003
|
const ident = battle.active(player);
|
|
2022
2004
|
const foe_ident = battle.active(player.foe());
|
|
2023
2005
|
|
|
2024
|
-
if (state.miss) return options.log.fail(.{ foe_ident,
|
|
2006
|
+
if (state.miss) return options.log.fail(.{ foe_ident, .None });
|
|
2025
2007
|
if (foe.active.volatiles.Substitute) {
|
|
2026
|
-
return options.log.
|
|
2008
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2027
2009
|
}
|
|
2028
2010
|
|
|
2029
2011
|
const avg = @max((foe.stored().hp + side.stored().hp) / 2, 1);
|
|
2030
2012
|
foe.stored().hp -= avg;
|
|
2031
2013
|
side.stored().hp = avg;
|
|
2032
2014
|
|
|
2033
|
-
try options.log.sethp(.{ foe_ident, foe.stored(),
|
|
2034
|
-
try options.log.sethp(.{ ident, side.stored(),
|
|
2015
|
+
try options.log.sethp(.{ foe_ident, foe.stored(), .Silent });
|
|
2016
|
+
try options.log.sethp(.{ ident, side.stored(), .None });
|
|
2035
2017
|
}
|
|
2036
2018
|
|
|
2037
2019
|
pub fn paralyze(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2039,21 +2021,21 @@ pub const Effects = struct {
|
|
|
2039
2021
|
const foe_ident = battle.active(player.foe());
|
|
2040
2022
|
|
|
2041
2023
|
if (Status.is(foe.stored().status, .PAR)) {
|
|
2042
|
-
return options.log.fail(.{ foe_ident,
|
|
2024
|
+
return options.log.fail(.{ foe_ident, .Paralysis });
|
|
2043
2025
|
}
|
|
2044
|
-
if (state.immune()) return options.log.immune(.{ foe_ident,
|
|
2045
|
-
if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident,
|
|
2026
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
2027
|
+
if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident, .None });
|
|
2046
2028
|
if (state.miss) {
|
|
2047
2029
|
try options.log.lastmiss(.{});
|
|
2048
2030
|
return options.log.miss(.{battle.active(player)});
|
|
2049
2031
|
} else if (foe.active.volatiles.Substitute) {
|
|
2050
|
-
return options.log.
|
|
2032
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2051
2033
|
}
|
|
2052
2034
|
|
|
2053
2035
|
foe.stored().status = Status.init(.PAR);
|
|
2054
2036
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2055
2037
|
|
|
2056
|
-
try options.log.status(.{ foe_ident, foe.stored().status,
|
|
2038
|
+
try options.log.status(.{ foe_ident, foe.stored().status, .None });
|
|
2057
2039
|
}
|
|
2058
2040
|
|
|
2059
2041
|
pub fn paralyzeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2069,8 +2051,7 @@ pub const Effects = struct {
|
|
|
2069
2051
|
foe_stored.status = Status.init(.PAR);
|
|
2070
2052
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2071
2053
|
|
|
2072
|
-
|
|
2073
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
|
|
2054
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2074
2055
|
|
|
2075
2056
|
// TODO: check for status berry
|
|
2076
2057
|
}
|
|
@@ -2086,15 +2067,15 @@ pub const Effects = struct {
|
|
|
2086
2067
|
var foe = battle.foe(player);
|
|
2087
2068
|
|
|
2088
2069
|
if (foe.active.volatiles.PerishSong) {
|
|
2089
|
-
return options.log.fail(.{ battle.active(player.foe()),
|
|
2070
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2090
2071
|
} else if (!side.active.volatiles.PerishSong) {
|
|
2091
2072
|
side.active.volatiles.PerishSong = true;
|
|
2092
2073
|
side.active.volatiles.perish_song = 4;
|
|
2093
|
-
try options.log.start(battle.active(player), .PerishSong3Silent);
|
|
2074
|
+
try options.log.start(.{ battle.active(player), .PerishSong3Silent });
|
|
2094
2075
|
}
|
|
2095
2076
|
foe.active.volatiles.PerishSong = true;
|
|
2096
2077
|
foe.active.volatiles.perish_song = 4;
|
|
2097
|
-
try options.log.start(battle.active(player.foe()), .PerishSong3Silent);
|
|
2078
|
+
try options.log.start(.{ battle.active(player.foe()), .PerishSong3Silent });
|
|
2098
2079
|
|
|
2099
2080
|
try options.log.fieldactivate(.{}); // FIXME .PerishSong
|
|
2100
2081
|
}
|
|
@@ -2106,7 +2087,7 @@ pub const Effects = struct {
|
|
|
2106
2087
|
|
|
2107
2088
|
const toxic = state.move == .Toxic;
|
|
2108
2089
|
if (state.immune() or foe.active.types.includes(.Poison)) {
|
|
2109
|
-
return options.log.immune(.{ foe_ident,
|
|
2090
|
+
return options.log.immune(.{ foe_ident, .None });
|
|
2110
2091
|
} else if (Status.any(foe_stored.status)) {
|
|
2111
2092
|
// Pokémon Showdown considers Toxic to be a status even in Generation II and so
|
|
2112
2093
|
// will not include a fail reason for Toxic vs. Poison or vice-versa...
|
|
@@ -2120,7 +2101,7 @@ pub const Effects = struct {
|
|
|
2120
2101
|
else
|
|
2121
2102
|
Fail.None });
|
|
2122
2103
|
} else if (foe.active.volatiles.Substitute) {
|
|
2123
|
-
return options.log.
|
|
2104
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2124
2105
|
} else if (state.miss) {
|
|
2125
2106
|
try options.log.lastmiss(.{});
|
|
2126
2107
|
return options.log.miss(.{battle.active(player)});
|
|
@@ -2133,7 +2114,7 @@ pub const Effects = struct {
|
|
|
2133
2114
|
foe.active.volatiles.toxic = 0;
|
|
2134
2115
|
}
|
|
2135
2116
|
|
|
2136
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
2117
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2137
2118
|
|
|
2138
2119
|
// TODO: check for status berry
|
|
2139
2120
|
}
|
|
@@ -2149,8 +2130,8 @@ pub const Effects = struct {
|
|
|
2149
2130
|
if (foe.conditions.Safeguard) return;
|
|
2150
2131
|
|
|
2151
2132
|
foe_stored.status = Status.init(.PSN);
|
|
2152
|
-
|
|
2153
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status,
|
|
2133
|
+
|
|
2134
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2154
2135
|
|
|
2155
2136
|
// TODO: check for status berry
|
|
2156
2137
|
}
|
|
@@ -2165,9 +2146,7 @@ pub const Effects = struct {
|
|
|
2165
2146
|
|
|
2166
2147
|
const failed = !state.first or volatiles.Substitute or
|
|
2167
2148
|
!try Rolls.protect(battle, player, volatiles.protect, options);
|
|
2168
|
-
if (failed) {
|
|
2169
|
-
return options.log.fail(.{ battle.active(player), Fail.None });
|
|
2170
|
-
}
|
|
2149
|
+
if (failed) return options.log.fail(.{ battle.active(player), .None });
|
|
2171
2150
|
|
|
2172
2151
|
volatiles.protect = @max(8, volatiles.protect + 1);
|
|
2173
2152
|
if (state.move == .Endure) {
|
|
@@ -2184,7 +2163,7 @@ pub const Effects = struct {
|
|
|
2184
2163
|
|
|
2185
2164
|
// Pokémon Showdown should fail when the enemy has no boosts/unboosts but doesn't
|
|
2186
2165
|
if (!showdown and @as(u32, @bitCast(foe.active.boosts)) == 0) {
|
|
2187
|
-
return options.log.fail(.{ battle.active(player),
|
|
2166
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2188
2167
|
}
|
|
2189
2168
|
|
|
2190
2169
|
side.active.boosts = foe.active.boosts;
|
|
@@ -2203,11 +2182,10 @@ pub const Effects = struct {
|
|
|
2203
2182
|
}
|
|
2204
2183
|
|
|
2205
2184
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2206
|
-
|
|
2207
|
-
var volatiles = &side.active.volatiles;
|
|
2185
|
+
const side = battle.side(player);
|
|
2208
2186
|
|
|
2209
|
-
assert(volatiles.Rage);
|
|
2210
|
-
state.damage *|= (volatiles.rage +| 1);
|
|
2187
|
+
assert(side.active.volatiles.Rage);
|
|
2188
|
+
state.damage *|= (side.active.volatiles.rage +| 1);
|
|
2211
2189
|
}
|
|
2212
2190
|
};
|
|
2213
2191
|
|
|
@@ -2219,7 +2197,7 @@ pub const Effects = struct {
|
|
|
2219
2197
|
var side = battle.side(player);
|
|
2220
2198
|
|
|
2221
2199
|
if (side.active.volatiles.LeechSeed) {
|
|
2222
|
-
try options.log.end(.{ battle.active(player),
|
|
2200
|
+
try options.log.end(.{ battle.active(player), .LeechSeedFrom });
|
|
2223
2201
|
side.active.volatiles.LeechSeed = false;
|
|
2224
2202
|
}
|
|
2225
2203
|
|
|
@@ -2238,18 +2216,14 @@ pub const Effects = struct {
|
|
|
2238
2216
|
pub fn recoil(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2239
2217
|
var side = battle.side(player);
|
|
2240
2218
|
var stored = side.stored();
|
|
2219
|
+
const ident = battle.active(player);
|
|
2241
2220
|
|
|
2242
2221
|
assert(state.damage > 0);
|
|
2243
2222
|
|
|
2244
2223
|
const damage: i16 = @intCast(@max(state.damage / 4, 1));
|
|
2245
2224
|
stored.hp = @intCast(@max(@as(i16, @intCast(stored.hp)) - damage, 0));
|
|
2246
2225
|
|
|
2247
|
-
try options.log.damage(.{
|
|
2248
|
-
battle.active(player),
|
|
2249
|
-
stored,
|
|
2250
|
-
Damage.RecoilOf,
|
|
2251
|
-
battle.active(player.foe()),
|
|
2252
|
-
});
|
|
2226
|
+
try options.log.damage(.{ ident, stored, .RecoilOf, battle.active(player.foe()) });
|
|
2253
2227
|
}
|
|
2254
2228
|
|
|
2255
2229
|
pub fn reversal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2312,7 +2286,7 @@ pub const Effects = struct {
|
|
|
2312
2286
|
|
|
2313
2287
|
pub fn sandstorm(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2314
2288
|
if (battle.field.weather != .Sandstorm) return weather(battle, .Sandstorm, options);
|
|
2315
|
-
return options.log.fail(.{ battle.active(player),
|
|
2289
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2316
2290
|
}
|
|
2317
2291
|
|
|
2318
2292
|
pub fn safeguard(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2337,7 +2311,7 @@ pub const Effects = struct {
|
|
|
2337
2311
|
var side = battle.side(player);
|
|
2338
2312
|
|
|
2339
2313
|
if (@field(side.conditions, key)) {
|
|
2340
|
-
return options.log.fail(.{ battle.active(player),
|
|
2314
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2341
2315
|
}
|
|
2342
2316
|
|
|
2343
2317
|
@field(side.conditions, key) = true;
|
|
@@ -2353,9 +2327,9 @@ pub const Effects = struct {
|
|
|
2353
2327
|
|
|
2354
2328
|
if (showdown) {
|
|
2355
2329
|
// yes, actually "Splash". '|nothing' gets blindly mapped to '|-activate||move:Splash'
|
|
2356
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2330
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2357
2331
|
} else {
|
|
2358
|
-
try options.log.fail(.{ battle.active(player),
|
|
2332
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2359
2333
|
}
|
|
2360
2334
|
}
|
|
2361
2335
|
|
|
@@ -2372,7 +2346,7 @@ pub const Effects = struct {
|
|
|
2372
2346
|
}
|
|
2373
2347
|
|
|
2374
2348
|
if (foe.active.volatiles.Substitute) {
|
|
2375
|
-
return options.log.
|
|
2349
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2376
2350
|
}
|
|
2377
2351
|
|
|
2378
2352
|
// Sleep Clause Mod
|
|
@@ -2383,22 +2357,21 @@ pub const Effects = struct {
|
|
|
2383
2357
|
}
|
|
2384
2358
|
|
|
2385
2359
|
foe_stored.status = Status.slp(Rolls.sleepDuration(battle, player, options));
|
|
2386
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
2360
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .From, state.move });
|
|
2387
2361
|
}
|
|
2388
2362
|
|
|
2389
2363
|
pub fn sleepTalk(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2390
2364
|
const side = battle.side(player);
|
|
2391
2365
|
|
|
2392
2366
|
if (!Status.is(side.stored().status, .SLP)) {
|
|
2393
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
2394
|
-
return;
|
|
2367
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2395
2368
|
}
|
|
2396
2369
|
|
|
2397
2370
|
var n: u3 = 0;
|
|
2398
2371
|
for (side.active.moves, 0..) |m, i| {
|
|
2399
2372
|
if (state.mslot - 1 != i and Move.get(m.id).extra.sleep_talk) n += 1;
|
|
2400
2373
|
}
|
|
2401
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
2374
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
2402
2375
|
|
|
2403
2376
|
_ = try Rolls.sleepTalk(battle, player, n, state.mslot, options); // TODO
|
|
2404
2377
|
}
|
|
@@ -2407,13 +2380,13 @@ pub const Effects = struct {
|
|
|
2407
2380
|
if (Status.is(battle.side(player).stored().status, .SLP)) return true;
|
|
2408
2381
|
state.damage = 0;
|
|
2409
2382
|
state.miss = true; // TODO is this necessary?
|
|
2410
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
2383
|
+
if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2411
2384
|
return false;
|
|
2412
2385
|
}
|
|
2413
2386
|
|
|
2414
2387
|
pub fn spikes(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2415
2388
|
var foe = battle.foe(player);
|
|
2416
|
-
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player),
|
|
2389
|
+
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player), .None });
|
|
2417
2390
|
|
|
2418
2391
|
foe.conditions.Spikes = true;
|
|
2419
2392
|
try options.log.sidestart(.{ player.foe(), protocol.Side.Spikes });
|
|
@@ -2423,7 +2396,7 @@ pub const Effects = struct {
|
|
|
2423
2396
|
_ = .{ battle, player, state, options }; // TODO
|
|
2424
2397
|
// var foe = battle.foe(player);
|
|
2425
2398
|
|
|
2426
|
-
// if (state.miss) return options.log.fail(.{ battle.active(player),
|
|
2399
|
+
// if (state.miss) return options.log.fail(.{ battle.active(player), .None });
|
|
2427
2400
|
|
|
2428
2401
|
// const last = foe.lastMove(false);
|
|
2429
2402
|
// if (showdown) {
|
|
@@ -2434,28 +2407,28 @@ pub const Effects = struct {
|
|
|
2434
2407
|
}
|
|
2435
2408
|
|
|
2436
2409
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2437
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2410
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2438
2411
|
}
|
|
2439
2412
|
|
|
2440
2413
|
pub fn substitute(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2441
2414
|
var side = battle.side(player);
|
|
2442
2415
|
if (side.active.volatiles.Substitute) {
|
|
2443
|
-
return options.log.fail(.{ battle.active(player),
|
|
2416
|
+
return options.log.fail(.{ battle.active(player), .Substitute });
|
|
2444
2417
|
}
|
|
2445
2418
|
|
|
2446
2419
|
assert(side.stored().stats.hp <= 1023);
|
|
2447
2420
|
const hp: u8 = @intCast(side.stored().stats.hp / 4);
|
|
2448
2421
|
|
|
2449
2422
|
if (hp == 0 or side.stored().hp <= hp) {
|
|
2450
|
-
return options.log.fail(.{ battle.active(player),
|
|
2423
|
+
return options.log.fail(.{ battle.active(player), .Weak });
|
|
2451
2424
|
}
|
|
2452
2425
|
|
|
2453
2426
|
side.stored().hp -= hp;
|
|
2454
2427
|
side.active.volatiles.substitute = hp;
|
|
2455
2428
|
side.active.volatiles.Substitute = true;
|
|
2456
2429
|
side.active.volatiles.bind = .{};
|
|
2457
|
-
try options.log.start(battle.active(player), .Substitute);
|
|
2458
|
-
try options.log.damage(.{ battle.active(player), side.stored(),
|
|
2430
|
+
try options.log.start(.{ battle.active(player), .Substitute });
|
|
2431
|
+
try options.log.damage(.{ battle.active(player), side.stored(), .None });
|
|
2459
2432
|
}
|
|
2460
2433
|
|
|
2461
2434
|
pub fn sunnyDay(battle: anytype, _: Player, _: *State, options: anytype) !void {
|
|
@@ -2471,7 +2444,7 @@ pub const Effects = struct {
|
|
|
2471
2444
|
pub fn teleport(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2472
2445
|
if (showdown) return;
|
|
2473
2446
|
|
|
2474
|
-
try options.log.fail(.{ battle.active(player),
|
|
2447
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2475
2448
|
try options.log.laststill(.{});
|
|
2476
2449
|
}
|
|
2477
2450
|
|
|
@@ -2513,9 +2486,9 @@ pub const Effects = struct {
|
|
|
2513
2486
|
side.active.volatiles.dirty = false;
|
|
2514
2487
|
|
|
2515
2488
|
if (foe.active.volatiles.Transform) {
|
|
2516
|
-
return options.log.fail(.{ battle.active(player),
|
|
2489
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2517
2490
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2518
|
-
if (!showdown) return options.log.fail(.{ battle.active(player),
|
|
2491
|
+
if (!showdown) return options.log.fail(.{ battle.active(player), .None });
|
|
2519
2492
|
try options.log.lastmiss(.{});
|
|
2520
2493
|
return options.log.miss(.{battle.active(player)});
|
|
2521
2494
|
}
|
|
@@ -2564,8 +2537,8 @@ pub const Effects = struct {
|
|
|
2564
2537
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2565
2538
|
var side = battle.side(player);
|
|
2566
2539
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2540
|
+
const stats = &side.active.stats;
|
|
2541
|
+
const boosts = &side.active.boosts;
|
|
2569
2542
|
|
|
2570
2543
|
if (!state.proc) return;
|
|
2571
2544
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
@@ -2584,9 +2557,9 @@ pub const Effects = struct {
|
|
|
2584
2557
|
var side = battle.side(player);
|
|
2585
2558
|
const ident = battle.active(player);
|
|
2586
2559
|
|
|
2587
|
-
if (state.miss) return log.fail(.{ ident,
|
|
2560
|
+
if (state.miss) return log.fail(.{ ident, .None });
|
|
2588
2561
|
|
|
2589
|
-
|
|
2562
|
+
const stats = &side.active.stats;
|
|
2590
2563
|
var boosts = &side.active.boosts;
|
|
2591
2564
|
|
|
2592
2565
|
const move = Move.get(state.move);
|
|
@@ -2594,7 +2567,7 @@ pub const Effects = struct {
|
|
|
2594
2567
|
.AttackUp1, .AttackUp2, .Rage, .Swagger => {
|
|
2595
2568
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2596
2569
|
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE) {
|
|
2597
|
-
return log.fail(.{ ident,
|
|
2570
|
+
return log.fail(.{ ident, .None });
|
|
2598
2571
|
}
|
|
2599
2572
|
const n: u2 = if (move.effect == .AttackUp2 or move.effect == .Swagger) 2 else 1;
|
|
2600
2573
|
try boostStat(battle, side, stats, boosts, "atk", n);
|
|
@@ -2604,41 +2577,41 @@ pub const Effects = struct {
|
|
|
2604
2577
|
.DefenseUp1, .DefenseUp2, .SkullBash => {
|
|
2605
2578
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2606
2579
|
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE) {
|
|
2607
|
-
return log.fail(.{ ident,
|
|
2580
|
+
return log.fail(.{ ident, .None });
|
|
2608
2581
|
}
|
|
2609
2582
|
const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
|
|
2610
2583
|
try boostStat(battle, side, stats, boosts, "def", n);
|
|
2611
|
-
try log.boost(.{ ident,
|
|
2584
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2612
2585
|
},
|
|
2613
2586
|
.SpeedUp2 => {
|
|
2614
2587
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2615
2588
|
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE) {
|
|
2616
|
-
return log.fail(.{ ident,
|
|
2589
|
+
return log.fail(.{ ident, .None });
|
|
2617
2590
|
}
|
|
2618
2591
|
try boostStat(battle, side, stats, boosts, "spe", 2);
|
|
2619
|
-
try log.boost(.{ ident,
|
|
2592
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2620
2593
|
},
|
|
2621
2594
|
.SpAtkUp1 => {
|
|
2622
2595
|
assert(boosts.spa >= -6 and boosts.spa <= 6);
|
|
2623
2596
|
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE) {
|
|
2624
|
-
return log.fail(.{ ident,
|
|
2597
|
+
return log.fail(.{ ident, .None });
|
|
2625
2598
|
}
|
|
2626
2599
|
try boostStat(battle, side, stats, boosts, "spa", 1);
|
|
2627
|
-
try log.boost(.{ ident,
|
|
2600
|
+
try log.boost(.{ ident, .SpecialAttack, 1 });
|
|
2628
2601
|
},
|
|
2629
2602
|
.SpDefUp2 => {
|
|
2630
2603
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2631
2604
|
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE) {
|
|
2632
|
-
return log.fail(.{ ident,
|
|
2605
|
+
return log.fail(.{ ident, .None });
|
|
2633
2606
|
}
|
|
2634
2607
|
try boostStat(battle, side, stats, boosts, "spd", 2);
|
|
2635
|
-
try log.boost(.{ ident,
|
|
2608
|
+
try log.boost(.{ ident, .SpecialDefense, 2 });
|
|
2636
2609
|
},
|
|
2637
2610
|
.EvasionUp1 => {
|
|
2638
2611
|
assert(boosts.evasion >= -6 and boosts.evasion <= 6);
|
|
2639
|
-
if (boosts.evasion == 6) return log.fail(.{ ident,
|
|
2612
|
+
if (boosts.evasion == 6) return log.fail(.{ ident, .None });
|
|
2640
2613
|
boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
|
|
2641
|
-
try log.boost(.{ ident,
|
|
2614
|
+
try log.boost(.{ ident, .Evasion, 1 });
|
|
2642
2615
|
if (state.move == .Minimize) side.active.volatiles.minimized = true;
|
|
2643
2616
|
},
|
|
2644
2617
|
else => unreachable,
|
|
@@ -2651,13 +2624,13 @@ pub const Effects = struct {
|
|
|
2651
2624
|
const foe_ident = battle.active(player.foe());
|
|
2652
2625
|
|
|
2653
2626
|
if (foe.active.volatiles.Mist) {
|
|
2654
|
-
try log.activate(foe_ident, .Mist);
|
|
2655
|
-
return log.fail(.{ foe_ident,
|
|
2627
|
+
try log.activate(.{ foe_ident, .Mist });
|
|
2628
|
+
return log.fail(.{ foe_ident, .None });
|
|
2656
2629
|
}
|
|
2657
2630
|
|
|
2658
2631
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2659
2632
|
|
|
2660
|
-
|
|
2633
|
+
const stats = &foe.active.stats;
|
|
2661
2634
|
var boosts = &foe.active.boosts;
|
|
2662
2635
|
|
|
2663
2636
|
const move = Move.get(state.move);
|
|
@@ -2675,7 +2648,7 @@ pub const Effects = struct {
|
|
|
2675
2648
|
}
|
|
2676
2649
|
const n: u2 = if (move.effect == .AttackDown2) 2 else 1;
|
|
2677
2650
|
try unboostStat(battle, foe, stats, boosts, "atk", n);
|
|
2678
|
-
try log.boost(.{ foe_ident,
|
|
2651
|
+
try log.boost(.{ foe_ident, .Attack, -@as(i8, n) });
|
|
2679
2652
|
},
|
|
2680
2653
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2681
2654
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
@@ -2691,7 +2664,7 @@ pub const Effects = struct {
|
|
|
2691
2664
|
}
|
|
2692
2665
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2693
2666
|
try unboostStat(battle, foe, stats, boosts, "def", n);
|
|
2694
|
-
try log.boost(.{ foe_ident,
|
|
2667
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2695
2668
|
},
|
|
2696
2669
|
.SpeedDown1, .SpeedDown2, .SpeedDownChance => {
|
|
2697
2670
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
@@ -2705,7 +2678,7 @@ pub const Effects = struct {
|
|
|
2705
2678
|
}
|
|
2706
2679
|
const n: u2 = if (move.effect == .SpeedDown2) 2 else 1;
|
|
2707
2680
|
try unboostStat(battle, foe, stats, boosts, "spe", n);
|
|
2708
|
-
try log.boost(.{ foe_ident,
|
|
2681
|
+
try log.boost(.{ foe_ident, .Speed, -@as(i8, n) });
|
|
2709
2682
|
},
|
|
2710
2683
|
.SpDefDownChance => {
|
|
2711
2684
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
@@ -2715,7 +2688,7 @@ pub const Effects = struct {
|
|
|
2715
2688
|
return options.log.miss(.{battle.active(player)});
|
|
2716
2689
|
} else if (stats.spd == 1) return;
|
|
2717
2690
|
try unboostStat(battle, foe, stats, boosts, "spd", 1);
|
|
2718
|
-
try log.boost(.{ foe_ident,
|
|
2691
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2719
2692
|
},
|
|
2720
2693
|
.AccuracyDown1, .AccuracyDownChance => {
|
|
2721
2694
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
@@ -2727,7 +2700,7 @@ pub const Effects = struct {
|
|
|
2727
2700
|
return options.log.miss(.{battle.active(player)});
|
|
2728
2701
|
}
|
|
2729
2702
|
boosts.accuracy = @intCast(@max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2730
|
-
try log.boost(.{ foe_ident,
|
|
2703
|
+
try log.boost(.{ foe_ident, .Accuracy, -1 });
|
|
2731
2704
|
},
|
|
2732
2705
|
else => unreachable,
|
|
2733
2706
|
}
|
|
@@ -2741,9 +2714,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2741
2714
|
}
|
|
2742
2715
|
|
|
2743
2716
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2744
|
-
|
|
2717
|
+
const stats = unmodifiedStats(battle, side);
|
|
2745
2718
|
inline for (STATS) |s| {
|
|
2746
|
-
|
|
2719
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2747
2720
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2748
2721
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2749
2722
|
}
|
|
@@ -2766,7 +2739,7 @@ fn boostStat(
|
|
|
2766
2739
|
n: u2,
|
|
2767
2740
|
) !void {
|
|
2768
2741
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2769
|
-
|
|
2742
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2770
2743
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2771
2744
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2772
2745
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2789,7 +2762,7 @@ fn unboostStat(
|
|
|
2789
2762
|
n: u2,
|
|
2790
2763
|
) !void {
|
|
2791
2764
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2792
|
-
|
|
2765
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2793
2766
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2794
2767
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2795
2768
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -3490,7 +3463,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3490
3463
|
.Move => {
|
|
3491
3464
|
const side = battle.side(player);
|
|
3492
3465
|
|
|
3493
|
-
|
|
3466
|
+
const active = &side.active;
|
|
3494
3467
|
const stored = side.stored();
|
|
3495
3468
|
|
|
3496
3469
|
// While players are not given any input options on the cartridge in these cases,
|