@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.
@@ -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
- var p1_slot = findFirstAlive(p1);
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
- var p2_slot = findFirstAlive(p2);
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, Damage.Spikes });
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, Cant.Recharge });
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, CureStatus.Message });
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, Cant.Sleep });
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, Cant.Freeze });
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, Cant.Flinch });
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, End.Disable });
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, End.Confusion });
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, Cant.Attract });
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, Cant.Disable, move });
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, Cant.Paralysis });
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
- var volatiles = &side.active.volatiles;
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, Damage.LeechSeed });
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, Heal.Silent });
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, Damage.Nightmare });
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, Damage.Curse });
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), End.FutureSight });
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
- Damage.Sandstorm,
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(.{ battle.active(player), @as(End, @enumFromInt(reason)) });
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(battle.active(player), @enumFromInt(reason));
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
- battle.active(player),
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, Heal.Leftovers });
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
- var stored = battle.side(player).stored();
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
- const reason: CureStatus = .Message;
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, EndItem.Eat });
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, EndItem.Eat });
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, End.Confusion });
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, CureStatus.Message });
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, EndItem.Eat });
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, End.Confusion });
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), End.Encore });
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()), Immune.None });
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, End.Bide });
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
- CureStatus.Message,
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
- const reason: protocol.Status = .None;
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), Fail.None });
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), Fail.None });
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), Fail.None });
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.activateMove(foe_ident, .SubstituteBlock, state.move);
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, Fail.None });
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(), Damage.Curse });
1476
+ try options.log.damage(.{ ident, side.stored(), .Curse });
1486
1477
  } else {
1487
- var stats = &side.active.stats;
1488
- var boosts = &side.active.boosts;
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, Fail.None });
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, Boost.Speed, @as(i8, -1) });
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, Boost.Attack, @as(i8, 1) });
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, Boost.Defense, @as(i8, 1) });
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, Fail.None });
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.startEffect(foe_ident, .Disable, move.id);
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, Heal.Drain, battle.active(player.foe()) });
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, Fail.None });
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, Fail.None });
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.startEffect(foe_ident, .Encore, move.id);
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), Fail.None });
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), Fail.None });
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), Fail.None });
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), Fail.None });
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, protocol.Status.None });
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, Fail.None });
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, protocol.Status.From, Move.Rest });
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()), Immune.None });
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.activateMove(foe_ident, .SubstituteBlock, state.move);
1836
+ return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
1853
1837
  } else if (state.miss) {
1854
- return options.log.fail(.{ foe_ident, Fail.None });
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.magnitude(battle.active(player), num);
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()), Fail.None });
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()), Fail.None });
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), Fail.None });
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), Fail.None });
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, Fail.None });
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, Heal.None });
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()), Fail.None });
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.activateMove(foe_ident, .SubstituteBlock, state.move);
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, Fail.None });
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, Immune.None });
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, Immune.OHKO });
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, Fail.None });
2006
+ if (state.miss) return options.log.fail(.{ foe_ident, .None });
2025
2007
  if (foe.active.volatiles.Substitute) {
2026
- return options.log.activateMove(foe_ident, .SubstituteBlock, state.move);
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(), SetHP.Silent });
2034
- try options.log.sethp(.{ ident, side.stored(), SetHP.None });
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, Fail.Paralysis });
2024
+ return options.log.fail(.{ foe_ident, .Paralysis });
2043
2025
  }
2044
- if (state.immune()) return options.log.immune(.{ foe_ident, Immune.None });
2045
- if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident, Fail.None });
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.activateMove(foe_ident, .SubstituteBlock, state.move);
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, protocol.Status.None });
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
- const reason: protocol.Status = .None;
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()), Fail.None });
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, Immune.None });
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.activateMove(foe_ident, .SubstituteBlock, state.move);
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, protocol.Status.None });
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
- const reason: protocol.Status = .None;
2153
- try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
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), Fail.None });
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
- var side = battle.side(player);
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), End.LeechSeedFrom });
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), Fail.None });
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), Fail.None });
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), Fail.None });
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.activateMove(foe_ident, .SubstituteBlock, state.move);
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, protocol.Status.From, state.move });
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), Fail.None });
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), Fail.None });
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), Fail.None });
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), Fail.None });
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), Fail.None });
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), Fail.Substitute });
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), Fail.Weak });
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(), Damage.None });
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), Fail.None });
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), Fail.None });
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), Fail.None });
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
- var stats = &side.active.stats;
2568
- var boosts = &side.active.boosts;
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, Fail.None });
2560
+ if (state.miss) return log.fail(.{ ident, .None });
2588
2561
 
2589
- var stats = &side.active.stats;
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, Fail.None });
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, Fail.None });
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, Boost.Defense, n });
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, Fail.None });
2589
+ return log.fail(.{ ident, .None });
2617
2590
  }
2618
2591
  try boostStat(battle, side, stats, boosts, "spe", 2);
2619
- try log.boost(.{ ident, Boost.Speed, 2 });
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, Fail.None });
2597
+ return log.fail(.{ ident, .None });
2625
2598
  }
2626
2599
  try boostStat(battle, side, stats, boosts, "spa", 1);
2627
- try log.boost(.{ ident, Boost.SpecialAttack, 1 });
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, Fail.None });
2605
+ return log.fail(.{ ident, .None });
2633
2606
  }
2634
2607
  try boostStat(battle, side, stats, boosts, "spd", 2);
2635
- try log.boost(.{ ident, Boost.SpecialDefense, 2 });
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, Fail.None });
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, Boost.Evasion, 1 });
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, Fail.None });
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
- var stats = &foe.active.stats;
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, Boost.Attack, -@as(i8, n) });
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, Boost.Defense, -@as(i8, n) });
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, Boost.Speed, -@as(i8, n) });
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, Boost.SpecialDefense, -1 });
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, Boost.Accuracy, -1 });
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
- var stats = unmodifiedStats(battle, side);
2717
+ const stats = unmodifiedStats(battle, side);
2745
2718
  inline for (STATS) |s| {
2746
- var mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
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
- var mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
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
- var mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
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
- var active = &side.active;
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,