@pkmn/engine 0.1.0-dev.eaae4aa6 → 0.1.0-dev.f34af416

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.
Files changed (40) hide show
  1. package/LICENSE +0 -2
  2. package/README.md +15 -11
  3. package/build/pkg/addon.js +6 -7
  4. package/build/pkg/addon.js.map +1 -1
  5. package/build/pkg/data.d.ts +2 -3
  6. package/build/pkg/index.d.ts +1 -1
  7. package/build/pkg/index.js.map +1 -1
  8. package/build/pkg/protocol.js +4 -4
  9. package/build/pkg/protocol.js.map +1 -1
  10. package/build/test/display.js +8 -3
  11. package/build/test/display.js.map +1 -1
  12. package/build/tools/debug.js +2 -3
  13. package/build/tools/debug.js.map +1 -1
  14. package/build.zig +189 -72
  15. package/package.json +17 -16
  16. package/src/bin/install-pkmn-engine +17 -7
  17. package/src/lib/common/data.zig +8 -8
  18. package/src/lib/common/debug.zig +2 -2
  19. package/src/lib/common/js.zig +4 -2
  20. package/src/lib/common/optional.zig +13 -9
  21. package/src/lib/common/options.zig +2 -0
  22. package/src/lib/common/protocol.zig +51 -44
  23. package/src/lib/common/rational.zig +114 -24
  24. package/src/lib/common/rng.zig +10 -10
  25. package/src/lib/common/util.zig +50 -1
  26. package/src/lib/gen1/calc.zig +508 -128
  27. package/src/lib/gen1/chance.zig +383 -276
  28. package/src/lib/gen1/data/species.zig +1 -1
  29. package/src/lib/gen1/data.zig +29 -28
  30. package/src/lib/gen1/helpers.zig +96 -536
  31. package/src/lib/gen1/mechanics.zig +266 -160
  32. package/src/lib/gen2/calc.zig +34 -16
  33. package/src/lib/gen2/chance.zig +91 -500
  34. package/src/lib/gen2/data.zig +15 -14
  35. package/src/lib/gen2/helpers.zig +41 -9
  36. package/src/lib/gen2/mechanics.zig +158 -97
  37. package/src/lib/pkmn.zig +10 -16
  38. package/src/test/display/pkmn.html.tmpl +5 -5
  39. package/src/lib/gen1/README.md +0 -894
  40. package/src/lib/gen2/README.md +0 -329
@@ -106,6 +106,7 @@ pub const State = struct {
106
106
  type: Type, // BATTLE_VARS_MOVE_TYPE
107
107
  accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
108
108
  move: Move, // wCurPlayerMove
109
+ hit: u3 = 0,
109
110
  from: Move = .None,
110
111
  mslot: u4, // wCurMoveNum
111
112
  first: bool, // wEnemyGoesFirst
@@ -256,7 +257,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
256
257
  } else {
257
258
  try options.log.switched(.{ battle.active(player), incoming });
258
259
  }
259
- options.chance.switched(player, side.pokemon[0].position, out);
260
260
 
261
261
  if (side.conditions.Spikes and !active.types.includes(.Flying)) {
262
262
  incoming.hp -|= @max(incoming.stats.hp / 8, 1);
@@ -442,18 +442,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
442
442
  if (Status.is(stored.status, .SLP)) slp: {
443
443
  const before = stored.status;
444
444
  // Even if the EXT bit is set this will still correctly modify the sleep duration
445
- // if (options.calc.modify(player, .sleep)) |extend| {
446
- // if (!extend) stored.status = 0;
447
- // } else {
448
445
  stored.status -= 1;
449
- // }
450
-
451
- const duration = Status.duration(stored.status);
452
- if (!Status.is(stored.status, .EXT)) {
453
- try options.chance.sleep(player, duration);
454
- }
455
446
 
456
- if (duration == 0) {
447
+ if (Status.duration(stored.status) == 0) {
457
448
  try log.curestatus(.{ ident, before, .Message });
458
449
  stored.status = 0; // clears EXT if present
459
450
  resetCant(volatiles);
@@ -483,12 +474,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
483
474
  }
484
475
 
485
476
  if (volatiles.disable.duration > 0) {
486
- // if (options.calc.modify(player, .disable)) |extend| {
487
- // if (!extend) volatiles.disable.duration = 0;
488
- // } else {
489
477
  volatiles.disable.duration -= 1;
490
- // }
491
- try options.chance.disable(player, volatiles.disable.duration);
492
478
 
493
479
  if (volatiles.disable.duration == 0) {
494
480
  volatiles.disable.move = 0;
@@ -498,13 +484,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
498
484
 
499
485
  if (volatiles.Confusion) {
500
486
  assert(volatiles.confusion > 0);
501
-
502
- // if (options.calc.modify(player, .confusion)) |extend| {
503
- // if (!extend) volatiles.confusion = 0;
504
- // } else {
505
487
  volatiles.confusion -= 1;
506
- // }
507
- try options.chance.confusion(player, volatiles.confusion);
508
488
 
509
489
  if (volatiles.confusion == 0) {
510
490
  volatiles.Confusion = false;
@@ -665,7 +645,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
665
645
  }
666
646
  assert(stage <= 4);
667
647
 
668
- state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
648
+ state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
669
649
  }
670
650
 
671
651
  pub fn calcDamage(
@@ -733,7 +713,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
733
713
 
734
714
  pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
735
715
  if (state.damage <= 1) return;
736
- const random = try Rolls.damage(battle, player, options);
716
+ const random = try Rolls.damage(battle, player, state.hit, options);
737
717
  options.calc.base(player.foe(), state.damage);
738
718
  state.damage = @intCast(@as(u32, state.damage) *% random / 255);
739
719
  options.calc.final(player.foe(), state.damage);
@@ -1304,14 +1284,32 @@ pub const Effects = struct {
1304
1284
  }
1305
1285
 
1306
1286
  pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
1307
- _ = .{ battle, player, state, options }; // TODO
1308
- // assert(!state.miss);
1309
- // var side = battle.side(player);
1310
- // try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
1311
- // TODO
1312
- // |-boost|p1a: Poliwag|atk|2|[silent]
1313
- // |-fail|p1a: Poliwag
1314
- //
1287
+ assert(!state.miss);
1288
+ var side = battle.side(player);
1289
+
1290
+ const ident = battle.active(player);
1291
+
1292
+ assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
1293
+ if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
1294
+ return options.log.fail(.{ ident, .None });
1295
+ }
1296
+
1297
+ const hp = @max(side.stored().stats.hp / 2, 1);
1298
+ if (side.stored().hp <= hp) {
1299
+ try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
1300
+ try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
1301
+ return options.log.fail(.{ ident, .None });
1302
+ } else {
1303
+ side.stored().hp -= hp;
1304
+ try options.log.damage(.{ ident, side.stored(), .None });
1305
+
1306
+ const before = side.active.boosts.atk;
1307
+ var i: usize = 0;
1308
+ while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
1309
+ try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
1310
+ }
1311
+ try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
1312
+ }
1315
1313
  }
1316
1314
 
1317
1315
  pub const bide = struct {
@@ -1323,13 +1321,7 @@ pub const Effects = struct {
1323
1321
 
1324
1322
  if (!volatiles.Bide) return;
1325
1323
 
1326
- // if (options.calc.modify(player, .attacking)) |extend| {
1327
- // if (!extend) volatiles.attacks = 0;
1328
- // } else {
1329
1324
  volatiles.attacks -= 1;
1330
- // }
1331
- try options.chance.attacking(player, volatiles.attacks);
1332
-
1333
1325
  if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
1334
1326
 
1335
1327
  volatiles.Bide = false;
@@ -1596,8 +1588,16 @@ pub const Effects = struct {
1596
1588
  try options.log.start(.{ foe_ident, .Encore, move.id });
1597
1589
  }
1598
1590
 
1599
- pub fn explode(battle: anytype, player: Player, state: *State, options: anytype) !void {
1600
- _ = .{ battle, player, state, options }; // TODO
1591
+ pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
1592
+ var side = battle.side(player);
1593
+ var stored = side.stored();
1594
+
1595
+ stored.hp = 0;
1596
+ // Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
1597
+ // to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
1598
+ if (!showdown) stored.status = 0;
1599
+ side.active.volatiles.LeechSeed = false;
1600
+ side.active.volatiles.DestinyBond = false;
1601
1601
  }
1602
1602
 
1603
1603
  pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
@@ -1723,8 +1723,26 @@ pub const Effects = struct {
1723
1723
  foe.active.volatiles.frozen = true;
1724
1724
  }
1725
1725
 
1726
- pub fn furyCutter(battle: anytype, player: Player, state: *State, options: anytype) !void {
1727
- _ = .{ battle, player, state, options }; // TODO
1726
+ // TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
1727
+ pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
1728
+ var side = battle.side(player);
1729
+ var volatiles = &side.active.volatiles;
1730
+
1731
+ if (state.miss) {
1732
+ volatiles.fury_cutter = 0;
1733
+ return;
1734
+ }
1735
+
1736
+ assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
1737
+ if (volatiles.fury_cutter == 5) return;
1738
+ volatiles.fury_cutter += 1;
1739
+
1740
+ if (showdown) {
1741
+ state.bp *= 2;
1742
+ assert(state.bp <= 160);
1743
+ } else {
1744
+ state.damage *|= 2;
1745
+ }
1728
1746
  }
1729
1747
 
1730
1748
  pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
@@ -1885,7 +1903,29 @@ pub const Effects = struct {
1885
1903
  }
1886
1904
 
1887
1905
  pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
1888
- _ = .{ battle, player, state, options }; // TODO
1906
+ var side = battle.side(player);
1907
+
1908
+ if (state.miss) {
1909
+ if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
1910
+ try options.log.lastmiss(.{});
1911
+ return options.log.miss(.{battle.active(player)});
1912
+ }
1913
+
1914
+ const move = battle.foe(player).lastMove(false);
1915
+ if (move == .None or move == .Struggle) {
1916
+ return options.log.fail(.{ battle.active(player.foe()), .None });
1917
+ }
1918
+
1919
+ var slot: u4 = 0;
1920
+ for (side.active.moves, 0..) |m, i| {
1921
+ if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
1922
+ if (m.id == .Mimic) slot = @intCast(i + 1);
1923
+ }
1924
+ assert(slot != 0 and slot == state.mslot);
1925
+
1926
+ side.active.move(slot).id = move;
1927
+ side.active.move(slot).pp = 5;
1928
+ try options.log.start(.{ battle.active(player), .Mimic, move });
1889
1929
  }
1890
1930
 
1891
1931
  pub fn mirrorMove(
@@ -2393,17 +2433,37 @@ pub const Effects = struct {
2393
2433
  }
2394
2434
 
2395
2435
  pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
2396
- _ = .{ battle, player, state, options }; // TODO
2397
- // var foe = battle.foe(player);
2436
+ var foe = battle.foe(player);
2398
2437
 
2399
- // if (state.miss) return options.log.fail(.{ battle.active(player), .None });
2438
+ const last = foe.lastMove(false);
2439
+ if (state.miss or last == .None or last == .Struggle) {
2440
+ return options.log.fail(.{ battle.active(player.foe()), .None });
2441
+ }
2400
2442
 
2401
- // const last = foe.lastMove(false);
2402
- // if (showdown) {
2403
- // const roll = try Rolls.spite(battle, player, options);
2404
- // } else {
2405
- // if (last != .None) {}
2406
- // }
2443
+ var slot: u4 = 0;
2444
+ for (foe.active.moves, 0..) |m, i| {
2445
+ if (m.id == last) slot = @intCast(i + 1);
2446
+ }
2447
+ // If the move was called via Metronome it might not exist
2448
+ if (slot == 0 or foe.active.move(slot).pp == 0) {
2449
+ return options.log.fail(.{ battle.active(player.foe()), .None });
2450
+ }
2451
+
2452
+ // Pokémon Showdown always deducts 4 instead of randomizing
2453
+ const pp = @min(
2454
+ foe.active.move(slot).pp,
2455
+ if (showdown) 4 else try Rolls.spite(battle, player, options),
2456
+ );
2457
+ foe.active.move(slot).pp -= pp;
2458
+ if (!foe.active.volatiles.Transform) {
2459
+ var move_slot = foe.stored().move(slot);
2460
+ assert(move_slot.pp > 0);
2461
+
2462
+ move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
2463
+ assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
2464
+ }
2465
+
2466
+ try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
2407
2467
  }
2408
2468
 
2409
2469
  pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
@@ -2790,7 +2850,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
2790
2850
 
2791
2851
  pub const Rolls = struct {
2792
2852
  fn speedTie(battle: anytype, options: anytype) !bool {
2793
- const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
2853
+ const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
2794
2854
  player == .P1
2795
2855
  else if (showdown)
2796
2856
  battle.rng.range(u8, 0, 2) == 0
@@ -2802,7 +2862,7 @@ pub const Rolls = struct {
2802
2862
  }
2803
2863
 
2804
2864
  fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
2805
- const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
2865
+ const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
2806
2866
  val == .true
2807
2867
  else if (showdown)
2808
2868
  battle.rng.chance(u8, 60, 256)
@@ -2814,7 +2874,7 @@ pub const Rolls = struct {
2814
2874
  }
2815
2875
 
2816
2876
  fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
2817
- const ok = if (options.calc.overridden(player, .hit)) |val|
2877
+ const ok = if (options.calc.overridden(player, 0, .hit)) |val|
2818
2878
  val == .true
2819
2879
  else if (showdown)
2820
2880
  battle.rng.chance(u8, accuracy, 256)
@@ -2825,20 +2885,22 @@ pub const Rolls = struct {
2825
2885
  return ok;
2826
2886
  }
2827
2887
 
2828
- fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2829
- const crit = if (options.calc.overridden(player, .critical_hit)) |val|
2888
+ fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
2889
+ _ = .{ player, options };
2890
+ const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
2830
2891
  val == .true
2831
2892
  else if (showdown)
2832
2893
  battle.rng.chance(u8, rate, 256)
2833
2894
  else
2834
2895
  battle.rng.next() < rate;
2835
2896
 
2836
- try options.chance.criticalHit(player, crit, rate);
2897
+ try options.chance.criticalHit(player, i, crit, rate);
2837
2898
  return crit;
2838
2899
  }
2839
2900
 
2840
- fn damage(battle: anytype, player: Player, options: anytype) !u8 {
2841
- const roll = if (options.calc.overridden(player, .damage)) |val|
2901
+ fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
2902
+ _ = .{ player, options };
2903
+ const roll = if (options.calc.overridden(player, i, .damages)) |val|
2842
2904
  val
2843
2905
  else roll: {
2844
2906
  if (showdown) break :roll battle.rng.range(u8, 217, 256);
@@ -2849,12 +2911,12 @@ pub const Rolls = struct {
2849
2911
  };
2850
2912
 
2851
2913
  assert(roll >= 217 and roll <= 255);
2852
- try options.chance.damage(player, roll);
2914
+ try options.chance.damage(player, i, roll);
2853
2915
  return roll;
2854
2916
  }
2855
2917
 
2856
2918
  fn confused(battle: anytype, player: Player, options: anytype) !bool {
2857
- const cfz = if (options.calc.overridden(player, .confused)) |val|
2919
+ const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
2858
2920
  val == .true
2859
2921
  else if (showdown)
2860
2922
  !battle.rng.chance(u8, 128, 256)
@@ -2866,7 +2928,7 @@ pub const Rolls = struct {
2866
2928
  }
2867
2929
 
2868
2930
  fn attract(battle: anytype, player: Player, options: anytype) !bool {
2869
- const cant = if (options.calc.overridden(player, .attract)) |val|
2931
+ const cant = if (options.calc.overridden(player, 0, .attract)) |val|
2870
2932
  val == .true
2871
2933
  else if (showdown)
2872
2934
  battle.rng.chance(u8, 1, 2)
@@ -2878,7 +2940,7 @@ pub const Rolls = struct {
2878
2940
  }
2879
2941
 
2880
2942
  fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2881
- const par = if (options.calc.overridden(player, .paralyzed)) |val|
2943
+ const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
2882
2944
  val == .true
2883
2945
  else if (showdown)
2884
2946
  battle.rng.chance(u8, 63, 256)
@@ -2890,7 +2952,7 @@ pub const Rolls = struct {
2890
2952
  }
2891
2953
 
2892
2954
  fn defrost(battle: anytype, player: Player, options: anytype) !bool {
2893
- const thaw = if (options.calc.overridden(player, .defrost)) |val|
2955
+ const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
2894
2956
  val == .true
2895
2957
  else if (showdown)
2896
2958
  battle.rng.chance(u8, 25, 256)
@@ -2903,7 +2965,7 @@ pub const Rolls = struct {
2903
2965
 
2904
2966
  fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2905
2967
  // GLITCH: 100% secondary chance will still fail 1/256 of the time
2906
- const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2968
+ const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
2907
2969
  val == .true
2908
2970
  else if (showdown)
2909
2971
  battle.rng.chance(u8, rate, 256)
@@ -2915,7 +2977,7 @@ pub const Rolls = struct {
2915
2977
  }
2916
2978
 
2917
2979
  fn item(battle: anytype, player: Player, options: anytype) !bool {
2918
- const proc = if (options.calc.overridden(player, .item)) |val|
2980
+ const proc = if (options.calc.overridden(player, 0, .item)) |val|
2919
2981
  val == .true
2920
2982
  else if (showdown)
2921
2983
  battle.rng.chance(u8, 30, 256)
@@ -2927,7 +2989,7 @@ pub const Rolls = struct {
2927
2989
  }
2928
2990
 
2929
2991
  fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
2930
- const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
2992
+ const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
2931
2993
  @enumFromInt(@intFromEnum(val) - 1)
2932
2994
  else if (showdown)
2933
2995
  @enumFromInt(battle.rng.range(u2, 0, 2))
@@ -2943,7 +3005,7 @@ pub const Rolls = struct {
2943
3005
  }
2944
3006
 
2945
3007
  fn present(battle: anytype, player: Player, options: anytype) !u8 {
2946
- const power = if (options.calc.overridden(player, .present)) |val|
3008
+ const power = if (options.calc.overridden(player, 0, .present)) |val|
2947
3009
  (val - 1) * 40
2948
3010
  else if (showdown) power: {
2949
3011
  const r = battle.rng.range(u8, 0, 10);
@@ -2965,7 +3027,7 @@ pub const Rolls = struct {
2965
3027
  }
2966
3028
 
2967
3029
  fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
2968
- const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
3030
+ const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
2969
3031
  val + 3
2970
3032
  else if (showdown) num: {
2971
3033
  const r = battle.rng.range(u8, 0, 100);
@@ -2993,7 +3055,7 @@ pub const Rolls = struct {
2993
3055
  }
2994
3056
 
2995
3057
  fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
2996
- const hits = if (options.calc.overridden(player, .triple_kick)) |val|
3058
+ const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
2997
3059
  val
2998
3060
  else if (showdown)
2999
3061
  battle.rng.range(u2, 1, 4)
@@ -3009,13 +3071,12 @@ pub const Rolls = struct {
3009
3071
  return hits;
3010
3072
  }
3011
3073
 
3012
- fn spite(battle: anytype, player: Player, options: anytype) !u8 {
3013
- const pp = if (options.calc.overridden(player, .spite)) |val|
3074
+ fn spite(battle: anytype, player: Player, options: anytype) !u3 {
3075
+ assert(!showdown);
3076
+ const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
3014
3077
  val + 1
3015
- else if (showdown)
3016
- battle.rng.range(u8, 2, 6)
3017
3078
  else
3018
- (battle.rng.next() & 3) + 2;
3079
+ @intCast((battle.rng.next() & 3) + 2);
3019
3080
 
3020
3081
  assert(pp >= 2 and pp <= 5);
3021
3082
  try options.chance.spite(player, pp);
@@ -3029,7 +3090,7 @@ pub const Rolls = struct {
3029
3090
  var i: u8 = 0;
3030
3091
  const neutral = @intFromEnum(Effectiveness.Neutral);
3031
3092
  assert(mtype != .@"???");
3032
- const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
3093
+ const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
3033
3094
  @enumFromInt(@intFromEnum(val) - 1)
3034
3095
  else if (showdown) ty: {
3035
3096
  for (Type.SHOWDOWN) |t| {
@@ -3063,7 +3124,7 @@ pub const Rolls = struct {
3063
3124
  assert(count <= 8);
3064
3125
  const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
3065
3126
 
3066
- const ok = if (options.calc.overridden(player, .protect)) |val|
3127
+ const ok = if (options.calc.overridden(player, 0, .protect)) |val|
3067
3128
  val == .true
3068
3129
  else if (showdown)
3069
3130
  battle.rng.chance(u8, num, 255)
@@ -3085,7 +3146,7 @@ pub const Rolls = struct {
3085
3146
  const moves = active.moves;
3086
3147
 
3087
3148
  // TODO: consider throwing error instead of rerolling?
3088
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3149
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3089
3150
  if (convertible(active, moves[val - 1].id)) val else null
3090
3151
  else
3091
3152
  null;
@@ -3122,7 +3183,7 @@ pub const Rolls = struct {
3122
3183
  const moves = active.moves;
3123
3184
 
3124
3185
  // TODO: consider throwing error instead of rerolling?
3125
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3186
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3126
3187
  if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
3127
3188
  else
3128
3189
  null;
@@ -3166,7 +3227,7 @@ pub const Rolls = struct {
3166
3227
  assert(n > 0 and n <= 6);
3167
3228
 
3168
3229
  // TODO: consider throwing error instead of rerolling?
3169
- const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
3230
+ const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
3170
3231
  for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
3171
3232
  break :val null;
3172
3233
  } else null;
@@ -3192,7 +3253,7 @@ pub const Rolls = struct {
3192
3253
  const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
3193
3254
 
3194
3255
  fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
3195
- const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
3256
+ const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
3196
3257
  @intCast(val)
3197
3258
  else if (showdown)
3198
3259
  DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
@@ -3207,7 +3268,7 @@ pub const Rolls = struct {
3207
3268
  }
3208
3269
 
3209
3270
  fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
3210
- const power = if (options.calc.overridden(player, .psywave)) |val|
3271
+ const power = if (options.calc.overridden(player, 0, .psywave)) |val|
3211
3272
  val
3212
3273
  else if (showdown)
3213
3274
  battle.rng.range(u8, 1, max)
@@ -3233,7 +3294,7 @@ pub const Rolls = struct {
3233
3294
  }
3234
3295
 
3235
3296
  // TODO: consider throwing error instead of rerolling?
3236
- const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
3297
+ const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
3237
3298
  for (moves) |m| if (m.id == val) break :val null;
3238
3299
  break :val val;
3239
3300
  } else null;
@@ -3265,7 +3326,7 @@ pub const Rolls = struct {
3265
3326
  }
3266
3327
 
3267
3328
  fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
3268
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3329
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3269
3330
  @intCast(val)
3270
3331
  else if (showdown)
3271
3332
  battle.rng.range(u3, 1, 7)
@@ -3277,12 +3338,12 @@ pub const Rolls = struct {
3277
3338
  };
3278
3339
 
3279
3340
  assert(duration >= 1 and duration <= 6);
3280
- options.chance.duration(.sleep, player, player.foe(), duration);
3341
+ options.chance.duration(player, duration);
3281
3342
  return duration + 1;
3282
3343
  }
3283
3344
 
3284
3345
  fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
3285
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3346
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3286
3347
  @intCast(val)
3287
3348
  else if (showdown)
3288
3349
  // Pokémon Showdown incorrectly uses the same duration for self-confusion
@@ -3291,16 +3352,16 @@ pub const Rolls = struct {
3291
3352
  @intCast((battle.rng.next() & if (self) 1 else 3) + 2);
3292
3353
 
3293
3354
  assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
3294
- options.chance.duration(.confusion, player, if (self) player else player.foe(), duration);
3355
+ options.chance.duration(player, duration);
3295
3356
  return duration;
3296
3357
  }
3297
3358
 
3298
3359
  fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
3299
- const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
3360
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3300
3361
  @intCast(val)
3301
3362
  else if (showdown)
3302
3363
  // Pokémon Showdown incorrectly inherits Generation III's Disable duration
3303
- battle.rng.range(u4, 2, 6)
3364
+ battle.rng.range(u3, 2, 6)
3304
3365
  else duration: {
3305
3366
  while (true) {
3306
3367
  const r = battle.rng.next() & 7;
@@ -3309,12 +3370,12 @@ pub const Rolls = struct {
3309
3370
  };
3310
3371
 
3311
3372
  assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
3312
- options.chance.duration(.disable, player, player.foe(), duration);
3373
+ options.chance.duration(player, duration);
3313
3374
  return duration;
3314
3375
  }
3315
3376
 
3316
3377
  fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
3317
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3378
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3318
3379
  @intCast(val)
3319
3380
  else if (showdown)
3320
3381
  battle.rng.range(u3, 2, 4)
@@ -3322,12 +3383,12 @@ pub const Rolls = struct {
3322
3383
  @intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
3323
3384
 
3324
3385
  assert(duration >= 2 and duration <= 3);
3325
- options.chance.duration(.attacking, player, player, duration);
3386
+ options.chance.duration(player, duration);
3326
3387
  return duration;
3327
3388
  }
3328
3389
 
3329
3390
  fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
3330
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3391
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3331
3392
  @intCast(val)
3332
3393
  else if (showdown)
3333
3394
  // TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
@@ -3336,13 +3397,13 @@ pub const Rolls = struct {
3336
3397
  @intCast(battle.rng.next() & 3 + 2));
3337
3398
 
3338
3399
  assert(duration >= 2 and duration <= 5);
3339
- options.chance.duration(.binding, player, player.foe(), duration);
3400
+ options.chance.duration(player, duration);
3340
3401
  return duration + 1;
3341
3402
  }
3342
3403
 
3343
3404
  // TODO: consider sharing implementation with bindingDuration
3344
3405
  fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
3345
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3406
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3346
3407
  @intCast(val)
3347
3408
  else if (showdown)
3348
3409
  battle.rng.range(u3, 2, 6)
@@ -3350,7 +3411,7 @@ pub const Rolls = struct {
3350
3411
  @intCast(battle.rng.next() & 3 + 2));
3351
3412
 
3352
3413
  assert(duration >= 2 and duration <= 5);
3353
- options.chance.duration(.encore, player, player.foe(), duration);
3414
+ options.chance.duration(player, duration);
3354
3415
  return duration + 1;
3355
3416
  }
3356
3417
  };
package/src/lib/pkmn.zig CHANGED
@@ -22,13 +22,16 @@ pub const Choice = @import("common/data.zig").Choice;
22
22
  /// considered terminal.
23
23
  pub const Result = @import("common/data.zig").Result;
24
24
 
25
+ // TODO
26
+ pub const Array = @import("common/array.zig").Array;
27
+ // TODO
28
+ pub const Optional = @import("common/optional.zig").Optional;
29
+
25
30
  /// Pokémon Showdown's RNG (backed by a Generation V & VI RNG).
26
31
  pub const PSRNG = @import("common/rng.zig").PSRNG;
27
32
 
28
33
  /// Namespace for helpers related to protocol message logging.
29
- pub const protocol = if (options.internal) struct {
30
- pub usingnamespace @import("common/protocol.zig");
31
- } else struct {
34
+ pub const protocol = if (options.internal) @import("common/protocol.zig") else struct {
32
35
  /// Logs protocol information to its `Writer` during a battle update when
33
36
  /// `options.log` is enabled.
34
37
  pub const Log = @import("common/protocol.zig").Log;
@@ -82,11 +85,7 @@ pub const gen1 = struct {
82
85
  /// via `overrides` and tracks `summaries` of information relevant to damage calculation.
83
86
  pub const Calc = @import("gen1/calc.zig").Calc;
84
87
  /// Namespace for types associated with supported Generation I Pokémon damage calc features.
85
- pub const calc = if (options.internal) struct {
86
- pub usingnamespace @import("gen1/calc.zig");
87
- } else struct {
88
- /// TODO
89
- pub const Overrides = @import("gen1/calc.zig").Overrides;
88
+ pub const calc = if (options.internal) @import("gen1/calc.zig") else struct {
90
89
  /// Information relevant to damage calculation that occured during a Generation I
91
90
  /// battle `update`.
92
91
  pub const Summaries = @import("gen1/calc.zig").Summaries;
@@ -114,10 +113,7 @@ pub const gen2 = struct {
114
113
  pub const NULL = @import("gen2/chance.zig").NULL;
115
114
  };
116
115
  pub const Calc = @import("gen2/calc.zig").Calc;
117
- pub const calc = if (options.internal) struct {
118
- pub usingnamespace @import("gen2/calc.zig");
119
- } else struct {
120
- pub const Overrides = @import("gen2/calc.zig").Overrides;
116
+ pub const calc = if (options.internal) @import("gen2/calc.zig") else struct {
121
117
  pub const Summaries = @import("gen2/calc.zig").Summaries;
122
118
  pub const Summary = @import("gen2/calc.zig").Summary;
123
119
  pub const NULL = @import("gen2/calc.zig").NULL;
@@ -127,10 +123,8 @@ pub const gen2 = struct {
127
123
 
128
124
  // Internal APIs used by other pkmn libraries, not actually part of the public API.
129
125
  // NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
130
- pub const js = if (options.internal) struct {
131
- pub usingnamespace @import("common/js.zig");
132
- } else struct {};
126
+ pub const js = if (options.internal) @import("common/js.zig") else {};
133
127
  pub const bindings = if (options.internal) struct {
134
128
  pub const node = @import("node.zig");
135
129
  pub const wasm = @import("wasm.zig");
136
- } else struct {};
130
+ } else {};