@pkmn/engine 0.1.0-dev.5ec64ea5 → 0.1.0-dev.6195e056

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.
@@ -347,7 +347,6 @@ fn doTurn(
347
347
  if (try checkFaint(battle, player, log)) |r| return r;
348
348
  } else if (foe_choice.type == .Pass) return null;
349
349
 
350
- residual = true;
351
350
  replace = battle.side(foe_player).stored().hp == 0;
352
351
  if (try executeMove(battle, foe_player, foe_choice, foe_from, &residual, log)) |r| return r;
353
352
  if (!replace) {
@@ -435,7 +434,7 @@ fn executeMove(
435
434
 
436
435
  var skip_can = false;
437
436
  var skip_pp = false;
438
- switch (try beforeMove(battle, player, from, residual, log)) {
437
+ switch (try beforeMove(battle, player, from, log)) {
439
438
  .done => return null,
440
439
  .skip_can => skip_can = true,
441
440
  .skip_pp => skip_pp = true,
@@ -443,22 +442,14 @@ fn executeMove(
443
442
  .err => return @as(?Result, Result.Error),
444
443
  }
445
444
 
446
- if (!skip_can and !try canMove(battle, player, mslot, auto, skip_pp, from, residual, log)) {
447
- return null;
448
- }
445
+ if (!skip_can and !try canMove(battle, player, mslot, auto, skip_pp, from, log)) return null;
449
446
 
450
447
  return doMove(battle, player, mslot, auto, residual, log);
451
448
  }
452
449
 
453
450
  const BeforeMove = union(enum) { done, skip_can, skip_pp, ok, err };
454
451
 
455
- fn beforeMove(
456
- battle: anytype,
457
- player: Player,
458
- from: ?Move,
459
- residual: *bool,
460
- log: anytype,
461
- ) !BeforeMove {
452
+ fn beforeMove(battle: anytype, player: Player, from: ?Move, log: anytype) !BeforeMove {
462
453
  var side = battle.side(player);
463
454
  const foe = battle.foe(player);
464
455
  var active = &side.active;
@@ -544,7 +535,7 @@ fn beforeMove(
544
535
 
545
536
  if (confused) {
546
537
  assert(!volatiles.MultiHit);
547
- if (!volatiles.Rage) volatiles.state = 0;
538
+ volatiles.state = 0;
548
539
  volatiles.Bide = false;
549
540
  volatiles.Thrashing = false;
550
541
  volatiles.MultiHit = false;
@@ -585,7 +576,7 @@ fn beforeMove(
585
576
  battle.rng.next() < Gen12.percent(25);
586
577
 
587
578
  if (paralyzed) {
588
- if (!volatiles.Rage) volatiles.state = 0;
579
+ volatiles.state = 0;
589
580
  volatiles.Bide = false;
590
581
  volatiles.Thrashing = false;
591
582
  volatiles.Charging = false;
@@ -620,18 +611,7 @@ fn beforeMove(
620
611
  return .done;
621
612
  }
622
613
 
623
- const sub = showdown and foe.active.volatiles.Substitute;
624
614
  _ = try applyDamage(battle, player.foe(), player.foe(), .None, log);
625
- if (foe.stored().hp > 0 and !sub) try buildRage(battle, player.foe(), log);
626
-
627
- // For reasons passing understanding, Pokémon Showdown still inflicts residual damage to
628
- // Bide's user even if the above damage has caused the foe to faint. It's simpler to always
629
- // run residual here regardless of whether the foe fainted and opt-out of the default flow
630
- if (showdown) {
631
- residual.* = false;
632
- try handleResidual(battle, player, log);
633
- }
634
-
635
615
  return .done;
636
616
  }
637
617
 
@@ -674,7 +654,6 @@ fn canMove(
674
654
  auto: bool,
675
655
  skip_pp: bool,
676
656
  from: ?Move,
677
- residual: *bool,
678
657
  log: anytype,
679
658
  ) !bool {
680
659
  var side = battle.side(player);
@@ -709,7 +688,7 @@ fn canMove(
709
688
  try log.move(player_ident, side.last_selected_move, battle.active(target), f);
710
689
 
711
690
  if (move.effect.onBegin()) {
712
- try onBegin(battle, player, move, mslot, residual, log);
691
+ try onBegin(battle, player, move, mslot, log);
713
692
  return false;
714
693
  }
715
694
 
@@ -788,21 +767,16 @@ fn doMove(
788
767
  (@enumToInt(move.type.effectiveness(foe.active.types.type1)) == 0 or
789
768
  @enumToInt(move.type.effectiveness(foe.active.types.type2)) == 0);
790
769
  if (immune and move.effect != .Binding) break :miss true;
791
- if (move.effect == .OHKO and side.active.stats.spe < foe.active.stats.spe) {
792
- battle.last_damage = 0;
793
- break :miss true;
794
- }
770
+ if (move.effect == .OHKO and side.active.stats.spe < foe.active.stats.spe) break :miss true;
795
771
  break :miss move.target != .Self and !moveHit(battle, player, move, &immune, &mist);
796
772
  };
797
773
  assert(!immune or miss or (showdown and move.effect == .Binding));
798
774
 
799
- var late = showdown;
800
775
  const skip = status or immune;
801
776
  if ((!showdown or (!skip or counter)) and !miss) blk: {
802
777
  if (showdown and move.effect.isMulti()) {
803
778
  Effects.multiHit(battle, player, move);
804
779
  hits = side.active.volatiles.attacks;
805
- late = false;
806
780
  }
807
781
 
808
782
  // Cartridge rolls for crit even for moves that can't crit (Counter/Metronome/status/OHKO)
@@ -836,7 +810,7 @@ fn doMove(
836
810
  }
837
811
 
838
812
  // Due to control flow shenanigans we need to clear last_damage for Pokémon Showdown
839
- if (showdown and skip) battle.last_damage = 0;
813
+ if (showdown and skip and move.effect != .Bide) battle.last_damage = 0;
840
814
 
841
815
  miss = if (showdown or skip)
842
816
  miss
@@ -883,9 +857,15 @@ fn doMove(
883
857
  if (showdown and side.stored().hp == 0) residual.* = false;
884
858
  } else if (move.effect == .Explode) {
885
859
  try Effects.explode(battle, player);
886
- try buildRage(battle, player.foe(), log);
887
- } else if (showdown and move.effect == .Disable) {
888
- try buildRage(battle, player.foe(), log);
860
+ if (foe.active.volatiles.Rage and foe.active.boosts.atk < 6) {
861
+ try Effects.boost(battle, player.foe(), Move.get(.Rage), log);
862
+ }
863
+ } else if (showdown) {
864
+ if (move.effect == .Disable) {
865
+ if (foe.active.volatiles.Rage and foe.active.boosts.atk < 6) {
866
+ try Effects.boost(battle, player.foe(), Move.get(.Rage), log);
867
+ }
868
+ }
889
869
  }
890
870
  return null;
891
871
  }
@@ -898,9 +878,6 @@ fn doMove(
898
878
  hits = side.active.volatiles.attacks;
899
879
  }
900
880
 
901
- // Pokémon Showdown only builds Rage for Disable/Explosion (hit/miss) when attacking into a sub
902
- const sub = showdown and foe.active.volatiles.Substitute;
903
-
904
881
  var nullified = false;
905
882
  var hit: u4 = 0;
906
883
  while (hit < hits) {
@@ -916,7 +893,9 @@ fn doMove(
916
893
  if (hit == 0 and ohko) try log.ohko();
917
894
  hit += 1;
918
895
  if (foe.stored().hp == 0) break;
919
- if (!late and !sub) try buildRage(battle, player.foe(), log);
896
+ if (foe.active.volatiles.Rage and foe.active.boosts.atk < 6) {
897
+ try Effects.boost(battle, player.foe(), Move.get(.Rage), log);
898
+ }
920
899
  // If the substitute breaks during a multi-hit attack, the attack ends
921
900
  if (nullified) break;
922
901
  }
@@ -953,9 +932,6 @@ fn doMove(
953
932
 
954
933
  if (foe.stored().hp == 0) return null;
955
934
 
956
- // Pokémon Showdown builds Rage at the wrong time for non-MultiHit move
957
- if (late and !sub and move.effect != .Disable) try buildRage(battle, player.foe(), log);
958
-
959
935
  if (!move.effect.isSpecial()) {
960
936
  // On the cartridge Rage is not considered to be "special" and thus gets executed for a
961
937
  // second time here (after being executed in the "always happens" block above) but that
@@ -967,9 +943,7 @@ fn doMove(
967
943
  if (move.effect == .Twineedle) {
968
944
  if (!showdown) try Effects.poison(battle, player, Move.get(.PoisonSting), log);
969
945
  } else if (move.effect == .Disable) {
970
- const result = try Effects.disable(battle, player, move, log);
971
- if (showdown) try buildRage(battle, player.foe(), log);
972
- return result;
946
+ return Effects.disable(battle, player, move, log);
973
947
  } else {
974
948
  try moveEffect(battle, player, move, log);
975
949
  }
@@ -1107,12 +1081,11 @@ fn randomizeDamage(battle: anytype) void {
1107
1081
 
1108
1082
  fn specialDamage(battle: anytype, player: Player, move: Move.Data, log: anytype) !?Result {
1109
1083
  const side = battle.side(player);
1110
- const foe = battle.foe(player);
1111
1084
 
1112
1085
  if (!try checkHit(battle, player, move, log)) return null;
1113
1086
 
1114
1087
  battle.last_damage = switch (side.last_selected_move) {
1115
- .SuperFang => @max(foe.stored().hp / 2, 1),
1088
+ .SuperFang => @max(battle.foe(player).stored().hp / 2, 1),
1116
1089
  .SeismicToss, .NightShade => side.stored().level,
1117
1090
  .SonicBoom => 20,
1118
1091
  .DragonRage => 40,
@@ -1135,10 +1108,7 @@ fn specialDamage(battle: anytype, player: Player, move: Move.Data, log: anytype)
1135
1108
 
1136
1109
  if (battle.last_damage == 0) return if (showdown) null else Result.Error;
1137
1110
 
1138
- const sub = showdown and foe.active.volatiles.Substitute;
1139
1111
  _ = try applyDamage(battle, player.foe(), player.foe(), .None, log);
1140
- if (battle.foe(player).stored().hp > 0 and !sub) try buildRage(battle, player.foe(), log);
1141
-
1142
1112
  return null;
1143
1113
  }
1144
1114
 
@@ -1246,7 +1216,7 @@ fn mirrorMove(
1246
1216
 
1247
1217
  incrementPP(side, mslot);
1248
1218
 
1249
- if (!try canMove(battle, player, mslot, auto, false, .MirrorMove, residual, log)) return null;
1219
+ if (!try canMove(battle, player, mslot, auto, false, .MirrorMove, log)) return null;
1250
1220
  return doMove(battle, player, mslot, auto, residual, log);
1251
1221
  }
1252
1222
 
@@ -1275,7 +1245,7 @@ fn metronome(
1275
1245
 
1276
1246
  incrementPP(side, mslot);
1277
1247
 
1278
- if (!try canMove(battle, player, mslot, auto, false, .Metronome, residual, log)) return null;
1248
+ if (!try canMove(battle, player, mslot, auto, false, .Metronome, log)) return null;
1279
1249
  return doMove(battle, player, mslot, auto, residual, log);
1280
1250
  }
1281
1251
 
@@ -1308,9 +1278,7 @@ fn moveHit(battle: anytype, player: Player, move: Move.Data, immune: *bool, mist
1308
1278
  if (move.effect == .Swift) return true;
1309
1279
  if (foe.active.volatiles.Invulnerable) break :miss true;
1310
1280
  }
1311
- if (move.effect == .DreamEater and (!Status.is(foe.stored().status, .SLP) or
1312
- (showdown and foe.active.volatiles.Substitute)))
1313
- {
1281
+ if (move.effect == .DreamEater and !Status.is(foe.stored().status, .SLP)) {
1314
1282
  immune.* = true;
1315
1283
  if (showdown) return false;
1316
1284
  break :miss true;
@@ -1461,15 +1429,6 @@ fn handleResidual(battle: anytype, player: Player, log: anytype) !void {
1461
1429
  }
1462
1430
 
1463
1431
  if (volatiles.LeechSeed) {
1464
- var foe = battle.foe(player);
1465
- var foe_stored = foe.stored();
1466
- const foe_ident = battle.active(player.foe());
1467
-
1468
- if (foe_stored.hp == 0) {
1469
- assert(showdown);
1470
- return;
1471
- }
1472
-
1473
1432
  var damage = @max(stored.stats.hp / 16, 1);
1474
1433
 
1475
1434
  // GLITCH: Leech Seed + Toxic glitch
@@ -1478,12 +1437,13 @@ fn handleResidual(battle: anytype, player: Player, log: anytype) !void {
1478
1437
  damage *= volatiles.toxic;
1479
1438
  }
1480
1439
 
1481
- // Pokémon Showdown erroneously updates last damage with uncapped Leech Seed damage
1482
- if (showdown) battle.last_damage = damage;
1483
-
1484
1440
  const amount = @min(damage, stored.hp);
1485
1441
  stored.hp -= amount;
1486
1442
 
1443
+ var foe = battle.foe(player);
1444
+ var foe_stored = foe.stored();
1445
+ const foe_ident = battle.active(player.foe());
1446
+
1487
1447
  // As above, Pokémon Showdown uses damageOf but its not relevant
1488
1448
  if (amount > 0) try log.damage(ident, stored, .LeechSeed);
1489
1449
 
@@ -1520,20 +1480,16 @@ fn endTurn(battle: anytype, log: anytype) @TypeOf(log).Error!Result {
1520
1480
 
1521
1481
  fn checkEBC(battle: anytype) bool {
1522
1482
  for (battle.sides, 0..) |side, i| {
1523
- const foe = battle.sides[~@intCast(u1, i)];
1524
-
1525
1483
  var foe_all_ghosts = true;
1526
1484
  var foe_all_transform = true;
1527
- for (foe.order, 0..) |id, j| {
1528
- if (id == 0) break;
1529
- const active = j == 0;
1530
- const pokemon = foe.pokemon[id - 1];
1531
1485
 
1532
- const ghost = pokemon.hp == 0 or
1533
- (if (active) foe.active.types else pokemon.types).includes(.Ghost);
1486
+ for (battle.sides[~@intCast(u1, i)].pokemon) |pokemon| {
1487
+ if (pokemon.species == .None) continue;
1488
+
1489
+ const ghost = pokemon.hp == 0 or pokemon.types.includes(.Ghost);
1534
1490
  foe_all_ghosts = foe_all_ghosts and ghost;
1535
1491
  foe_all_transform = foe_all_transform and pokemon.hp == 0 or transform: {
1536
- for (if (active) foe.active.moves else pokemon.moves) |m| {
1492
+ for (pokemon.moves) |m| {
1537
1493
  if (m.id == .None) break :transform true;
1538
1494
  if (m.id != .Transform) break :transform false;
1539
1495
  }
@@ -1541,14 +1497,10 @@ fn checkEBC(battle: anytype) bool {
1541
1497
  };
1542
1498
  }
1543
1499
 
1544
- for (side.order, 0..) |id, j| {
1545
- if (id == 0) break;
1546
- const active = j == 0;
1547
- const pokemon = side.pokemon[id - 1];
1548
-
1500
+ for (side.pokemon) |pokemon| {
1549
1501
  if (pokemon.hp == 0 or Status.is(pokemon.status, .FRZ)) continue;
1550
1502
  const transform = foe_all_transform and transform: {
1551
- for (if (active) side.active.moves else pokemon.moves) |m| {
1503
+ for (pokemon.moves) |m| {
1552
1504
  if (m.id == .None) break :transform true;
1553
1505
  if (m.id != .Transform) break :transform false;
1554
1506
  }
@@ -1556,7 +1508,7 @@ fn checkEBC(battle: anytype) bool {
1556
1508
  };
1557
1509
  if (transform) continue;
1558
1510
  const no_pp = foe_all_ghosts and no_pp: {
1559
- for (if (active) side.active.moves else pokemon.moves) |m| {
1511
+ for (pokemon.moves) |m| {
1560
1512
  if (m.pp != 0) break :no_pp false;
1561
1513
  }
1562
1514
  break :no_pp true;
@@ -1572,14 +1524,7 @@ fn checkEBC(battle: anytype) bool {
1572
1524
  return false;
1573
1525
  }
1574
1526
 
1575
- inline fn onBegin(
1576
- battle: anytype,
1577
- player: Player,
1578
- move: Move.Data,
1579
- mslot: u8,
1580
- residual: *bool,
1581
- log: anytype,
1582
- ) !void {
1527
+ inline fn onBegin(battle: anytype, player: Player, move: Move.Data, mslot: u8, log: anytype) !void {
1583
1528
  assert(move.effect.onBegin());
1584
1529
  return switch (move.effect) {
1585
1530
  .Confusion => Effects.confusion(battle, player, move, log),
@@ -1595,7 +1540,7 @@ inline fn onBegin(
1595
1540
  .Poison => Effects.poison(battle, player, move, log),
1596
1541
  .Reflect => Effects.reflect(battle, player, log),
1597
1542
  .Splash => Effects.splash(battle, player, log),
1598
- .Substitute => Effects.substitute(battle, player, residual, log),
1543
+ .Substitute => Effects.substitute(battle, player, log),
1599
1544
  .SwitchAndTeleport => Effects.switchAndTeleport(battle, player, move, log),
1600
1545
  .Transform => Effects.transform(battle, player, log),
1601
1546
  else => unreachable,
@@ -1808,11 +1753,6 @@ pub const Effects = struct {
1808
1753
  var side = battle.side(player);
1809
1754
  var stored = side.stored();
1810
1755
 
1811
- if (battle.last_damage == 0) {
1812
- assert(showdown);
1813
- return;
1814
- }
1815
-
1816
1756
  const drain = @max(battle.last_damage / 2, 1);
1817
1757
  battle.last_damage = drain;
1818
1758
 
@@ -2123,7 +2063,7 @@ pub const Effects = struct {
2123
2063
  return log.fail(foe_ident, if (Status.is(foe_stored.status, .PSN))
2124
2064
  if (!showdown)
2125
2065
  .Poison
2126
- else if (toxic == (foe_stored.status == Status.TOX))
2066
+ else if (toxic == foe.active.volatiles.Toxic)
2127
2067
  if (toxic) .Toxic else .Poison
2128
2068
  else
2129
2069
  .None
@@ -2231,7 +2171,7 @@ pub const Effects = struct {
2231
2171
  try log.activate(battle.active(player), .Splash);
2232
2172
  }
2233
2173
 
2234
- fn substitute(battle: anytype, player: Player, residual: *bool, log: anytype) !void {
2174
+ fn substitute(battle: anytype, player: Player, log: anytype) !void {
2235
2175
  var side = battle.side(player);
2236
2176
  if (side.active.volatiles.Substitute) {
2237
2177
  try log.fail(battle.active(player), .Substitute);
@@ -2256,18 +2196,14 @@ pub const Effects = struct {
2256
2196
  side.active.volatiles.substitute = hp + 1;
2257
2197
  side.active.volatiles.Substitute = true;
2258
2198
  try log.start(battle.active(player), .Substitute);
2259
- if (hp > 0) {
2260
- try log.damage(battle.active(player), side.stored(), .None);
2261
- if (showdown and side.stored().hp == 0) residual.* = false;
2262
- }
2199
+ if (hp > 0) try log.damage(battle.active(player), side.stored(), .None);
2263
2200
  }
2264
2201
 
2265
2202
  fn switchAndTeleport(battle: anytype, player: Player, move: Move.Data, log: anytype) !void {
2266
2203
  if (!showdown or battle.side(player).last_selected_move == .Teleport) return;
2267
2204
 
2268
- // Whirlwind/Roar should not roll to hit/reset damage but Pokémon Showdown does anyway
2205
+ // Whirlwind/Roar should not roll to hit but Pokémon Showdown does anyway
2269
2206
  _ = try checkHit(battle, player, move, log);
2270
- battle.last_damage = 0;
2271
2207
  }
2272
2208
 
2273
2209
  fn thrashing(battle: anytype, player: Player) void {
@@ -2352,8 +2288,6 @@ pub const Effects = struct {
2352
2288
  const stat = unmodifiedStats(battle, player).atk;
2353
2289
  stats.atk = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
2354
2290
  try log.boost(ident, reason, n);
2355
- // Pokémon Showdown doesn't re-apply status modifiers after Rage boosts
2356
- if (showdown and move.effect == .Rage) return;
2357
2291
  },
2358
2292
  .DefenseUp1, .DefenseUp2 => {
2359
2293
  assert(boosts.def >= -6 and boosts.def <= 6);
@@ -2548,7 +2482,7 @@ fn statusModify(status: u8, stats: *Stats(u16)) void {
2548
2482
  }
2549
2483
  }
2550
2484
 
2551
- inline fn isForced(active: ActivePokemon) bool {
2485
+ fn isForced(active: ActivePokemon) bool {
2552
2486
  return active.volatiles.Recharging or active.volatiles.Rage or
2553
2487
  active.volatiles.Thrashing or active.volatiles.Charging;
2554
2488
  }
@@ -2580,9 +2514,13 @@ fn clearVolatiles(battle: anytype, who: Player, log: anytype) !void {
2580
2514
  volatiles.LeechSeed = false;
2581
2515
  try log.end(ident, .LeechSeed);
2582
2516
  }
2583
- if (!showdown and volatiles.Toxic) {
2517
+ if (volatiles.Toxic) {
2584
2518
  volatiles.Toxic = false;
2585
- // volatiles.toxic is left unchanged, except on Pokémon Showdown which clears it (below)
2519
+ // volatiles.toxic is left unchanged, except on Pokémon Showdown which clears it
2520
+ if (showdown) {
2521
+ volatiles.toxic = 0;
2522
+ try log.end(ident, .Toxic);
2523
+ }
2586
2524
  }
2587
2525
  if (volatiles.LightScreen) {
2588
2526
  volatiles.LightScreen = false;
@@ -2592,39 +2530,21 @@ fn clearVolatiles(battle: anytype, who: Player, log: anytype) !void {
2592
2530
  volatiles.Reflect = false;
2593
2531
  try log.end(ident, .Reflect);
2594
2532
  }
2595
- if (showdown and volatiles.Toxic) {
2596
- volatiles.Toxic = false;
2597
- // Pokémon Showdown erroneously clears the toxic counter
2598
- volatiles.toxic = 0;
2599
- try log.end(ident, .Toxic);
2600
- }
2601
2533
  }
2602
2534
 
2603
2535
  fn disabled(side: *Side, ident: ID, log: anytype) !bool {
2604
2536
  if (side.active.volatiles.disabled_move != 0) {
2605
2537
  // A Pokémon that transforms after being disabled may end up with less move slots
2606
2538
  const m = side.active.moves[side.active.volatiles.disabled_move - 1].id;
2607
- // side.last_selected_move can be Struggle here on Pokemon Showdown we need an extra check
2608
- const last = if (showdown and m == .Bide and side.active.volatiles.Bide)
2609
- m
2610
- else
2611
- side.last_selected_move;
2612
- if (m != .None and m == last) {
2539
+ if (m != .None and m == side.last_selected_move) {
2613
2540
  side.active.volatiles.Charging = false;
2614
- try log.disabled(ident, last);
2541
+ try log.disabled(ident, side.last_selected_move);
2615
2542
  return true;
2616
2543
  }
2617
2544
  }
2618
2545
  return false;
2619
2546
  }
2620
2547
 
2621
- inline fn buildRage(battle: anytype, who: Player, log: anytype) !void {
2622
- const side = battle.side(who);
2623
- if (side.active.volatiles.Rage and side.active.boosts.atk < 6) {
2624
- try Effects.boost(battle, who, Move.get(.Rage), log);
2625
- }
2626
- }
2627
-
2628
2548
  fn handleThrashing(battle: anytype, active: *ActivePokemon) bool {
2629
2549
  var volatiles = &active.volatiles;
2630
2550
  assert(volatiles.Thrashing);
@@ -2773,11 +2693,9 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
2773
2693
  while (slot <= 4) : (slot += 1) {
2774
2694
  const m = active.moves[slot - 1];
2775
2695
  if (m.id == .None) break;
2776
- if (m.id == if (active.volatiles.Bide) .Bide else side.last_selected_move) {
2777
- // Pokémon Showdown displays Struggle if limited to Bide but unable to pick
2778
- const struggle =
2779
- m.id == .Bide and (m.pp == 0 or active.volatiles.disabled_move == slot);
2780
- const s = if (struggle) 0 else slot;
2696
+ if (m.id == side.last_selected_move) {
2697
+ // Pokémon Showdown displays Struggle if limited to Bide when out of PP
2698
+ const s = if (m.pp == 0 and m.id == .Bide) 0 else slot;
2781
2699
  out[n] = .{ .type = .Move, .data = s };
2782
2700
  n += 1;
2783
2701
  return n;
@@ -1,7 +0,0 @@
1
- import { Choice, Player, Result } from '.';
2
- export declare function check(showdown: boolean): void;
3
- export declare function supports(showdown: boolean, trace?: boolean): boolean;
4
- export declare function update(index: number, showdown: boolean, battle: ArrayBuffer, c1?: Choice, c2?: Choice, log?: ArrayBuffer): Result;
5
- export declare function choices(index: number, showdown: boolean, battle: ArrayBuffer, player: Player, choice: Choice['type'], buf: ArrayBuffer): Choice[];
6
- export declare function choose(index: number, showdown: boolean, battle: ArrayBuffer, player: Player, choice: Choice['type'], buf: ArrayBuffer, fn: (n: number) => number): Choice;
7
- export declare function size(index: number, type: 'options' | 'log'): number;
@@ -1,111 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.size = exports.choose = exports.choices = exports.update = exports.supports = exports.check = void 0;
27
- const path = __importStar(require("path"));
28
- const _1 = require(".");
29
- const ROOT = path.join(__dirname, '..', '..');
30
- let ADDON = undefined;
31
- function load() {
32
- if (ADDON)
33
- return ADDON;
34
- let pkmn = undefined;
35
- let showdown = undefined;
36
- try {
37
- pkmn = require(path.join(ROOT, 'build', 'lib', 'pkmn.node'));
38
- }
39
- catch { }
40
- try {
41
- showdown = require(path.join(ROOT, 'build', 'lib', 'pkmn-showdown.node'));
42
- }
43
- catch { }
44
- if (!pkmn && !showdown) {
45
- throw new Error('Could not find native addons - did you run `npx install-pkmn-engine`?');
46
- }
47
- return (ADDON = [pkmn, showdown]);
48
- }
49
- function check(showdown) {
50
- if (!load()[+showdown]) {
51
- const opts = ADDON[+!showdown].options.trace ? ['-Dtrace'] : [];
52
- if (showdown)
53
- opts.push('-Dshowdown');
54
- throw new Error(`@pkmn/engine has ${showdown ? 'not' : 'only'} been configured to support Pokémon Showdown.` +
55
- `\n(running \`npx install-pkmn-engine --options='${opts.join(' ')}'\` can fix this issue).`);
56
- }
57
- }
58
- exports.check = check;
59
- function supports(showdown, trace) {
60
- if (!load()[+showdown])
61
- return false;
62
- if (trace === undefined)
63
- return true;
64
- return ADDON[+showdown].options.trace === trace;
65
- }
66
- exports.supports = supports;
67
- function update(index, showdown, battle, c1, c2, log) {
68
- return _1.Result.decode(ADDON[+showdown].bindings[index]
69
- .update(battle, _1.Choice.encode(c1), _1.Choice.encode(c2), log));
70
- }
71
- exports.update = update;
72
- function choices(index, showdown, battle, player, choice, buf) {
73
- const request = choice[0] === 'p' ? 0 : choice[0] === 'm' ? 1 : 2;
74
- const n = ADDON[+showdown].bindings[index].choices(battle, +(player !== 'p1'), request, buf);
75
- // The top-level API signature means our hands our tied with respect to
76
- // writing really fast bindings here. The simplest approach would be to return
77
- // the ArrayBuffer the bindings populate as well as its size and only decode a
78
- // Choice after the selection. However, given that we need to return
79
- // `Choices[]` we need to decode all of them even if they're not all being
80
- // used which is wasteful. This shouldn't be *that* bad as its a very small
81
- // list, but its still wasted work. Switching the top-level API to return an
82
- // Iterable<Choice> doesn't help as we need both the length and the ability to
83
- // randomly access it, so the best way to make the current API fast would be
84
- // to have the Zig bindings create Choice objects directly, only that won't
85
- // scale well as it would require us to basically rewrite the low-level
86
- // choices function for each generation within node.zig. We could do something
87
- // really galaxy-brained and return some sort of frankenstein subclass of
88
- // Array backed by ArrayBuffer which would lazily decode the Choice on access,
89
- // but thats ultimately not worth the effort. You can't have both a high-level
90
- // idiomatic API and performance here, hence why the choose function below
91
- // exists.
92
- const options = new Array(n);
93
- const data = new Uint8Array(buf);
94
- for (let i = 0; i < n; i++)
95
- options[i] = _1.Choice.decode(data[i]);
96
- return options;
97
- }
98
- exports.choices = choices;
99
- function choose(index, showdown, battle, player, choice, buf, fn) {
100
- const request = choice[0] === 'p' ? 0 : choice[0] === 'm' ? 1 : 2;
101
- const n = ADDON[+showdown].bindings[index].choices(battle, +(player !== 'p1'), request, buf);
102
- const data = new Uint8Array(buf);
103
- return _1.Choice.decode(data[fn(n)]);
104
- }
105
- exports.choose = choose;
106
- function size(index, type) {
107
- const bindings = (ADDON[1] ?? ADDON[0]).bindings[index];
108
- return type[0] === 'o' ? bindings.OPTIONS_SIZE : bindings.LOGS_SIZE;
109
- }
110
- exports.size = size;
111
- //# sourceMappingURL=addon.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"addon.js","sourceRoot":"","sources":["../../src/pkg/addon.ts"],"names":[],"mappings":"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"}