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

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 (41) 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 +187 -72
  15. package/package.json +18 -16
  16. package/src/bin/install-pkmn-engine +17 -7
  17. package/src/lib/common/array.zig +77 -0
  18. package/src/lib/common/data.zig +8 -8
  19. package/src/lib/common/debug.zig +2 -2
  20. package/src/lib/common/js.zig +4 -2
  21. package/src/lib/common/optional.zig +13 -9
  22. package/src/lib/common/options.zig +2 -0
  23. package/src/lib/common/protocol.zig +51 -44
  24. package/src/lib/common/rational.zig +114 -24
  25. package/src/lib/common/rng.zig +10 -10
  26. package/src/lib/common/util.zig +50 -1
  27. package/src/lib/gen1/calc.zig +506 -128
  28. package/src/lib/gen1/chance.zig +383 -276
  29. package/src/lib/gen1/data/species.zig +1 -1
  30. package/src/lib/gen1/data.zig +31 -30
  31. package/src/lib/gen1/helpers.zig +95 -538
  32. package/src/lib/gen1/mechanics.zig +265 -162
  33. package/src/lib/gen2/calc.zig +34 -16
  34. package/src/lib/gen2/chance.zig +90 -502
  35. package/src/lib/gen2/data.zig +15 -14
  36. package/src/lib/gen2/helpers.zig +41 -13
  37. package/src/lib/gen2/mechanics.zig +158 -103
  38. package/src/lib/pkmn.zig +10 -16
  39. package/src/test/display/pkmn.html.tmpl +5 -5
  40. package/src/lib/gen1/README.md +0 -894
  41. package/src/lib/gen2/README.md +0 -329
@@ -6,7 +6,6 @@ const common = @import("../common/data.zig");
6
6
  const DEBUG = @import("../common/debug.zig").print;
7
7
  const protocol = @import("../common/protocol.zig");
8
8
  const rng = @import("../common/rng.zig");
9
- const util = @import("../common/util.zig");
10
9
 
11
10
  const data = @import("data.zig");
12
11
  const generated = @import("generated.zig");
@@ -34,17 +33,12 @@ const Start = protocol.Start;
34
33
 
35
34
  const Gen12 = rng.Gen12;
36
35
 
37
- const PointerType = util.PointerType;
38
-
39
36
  const ActivePokemon = data.ActivePokemon;
40
37
  const Boosts = data.Boosts;
41
38
  const Effectiveness = data.Effectiveness;
42
39
  const Item = data.Item;
43
40
  const Move = data.Move;
44
- const MoveSlot = data.MoveSlot;
45
- const Pokemon = data.Pokemon;
46
41
  const Side = data.Side;
47
- const Species = data.Species;
48
42
  const Stats = data.Stats;
49
43
  const Status = data.Status;
50
44
  const TriAttack = data.TriAttack;
@@ -106,6 +100,7 @@ pub const State = struct {
106
100
  type: Type, // BATTLE_VARS_MOVE_TYPE
107
101
  accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
108
102
  move: Move, // wCurPlayerMove
103
+ hit: u3 = 0,
109
104
  from: Move = .None,
110
105
  mslot: u4, // wCurMoveNum
111
106
  first: bool, // wEnemyGoesFirst
@@ -256,7 +251,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
256
251
  } else {
257
252
  try options.log.switched(.{ battle.active(player), incoming });
258
253
  }
259
- options.chance.switched(player, side.pokemon[0].position, out);
260
254
 
261
255
  if (side.conditions.Spikes and !active.types.includes(.Flying)) {
262
256
  incoming.hp -|= @max(incoming.stats.hp / 8, 1);
@@ -442,18 +436,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
442
436
  if (Status.is(stored.status, .SLP)) slp: {
443
437
  const before = stored.status;
444
438
  // 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
439
  stored.status -= 1;
449
- // }
450
440
 
451
- const duration = Status.duration(stored.status);
452
- if (!Status.is(stored.status, .EXT)) {
453
- try options.chance.sleep(player, duration);
454
- }
455
-
456
- if (duration == 0) {
441
+ if (Status.duration(stored.status) == 0) {
457
442
  try log.curestatus(.{ ident, before, .Message });
458
443
  stored.status = 0; // clears EXT if present
459
444
  resetCant(volatiles);
@@ -483,12 +468,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
483
468
  }
484
469
 
485
470
  if (volatiles.disable.duration > 0) {
486
- // if (options.calc.modify(player, .disable)) |extend| {
487
- // if (!extend) volatiles.disable.duration = 0;
488
- // } else {
489
471
  volatiles.disable.duration -= 1;
490
- // }
491
- try options.chance.disable(player, volatiles.disable.duration);
492
472
 
493
473
  if (volatiles.disable.duration == 0) {
494
474
  volatiles.disable.move = 0;
@@ -498,13 +478,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
498
478
 
499
479
  if (volatiles.Confusion) {
500
480
  assert(volatiles.confusion > 0);
501
-
502
- // if (options.calc.modify(player, .confusion)) |extend| {
503
- // if (!extend) volatiles.confusion = 0;
504
- // } else {
505
481
  volatiles.confusion -= 1;
506
- // }
507
- try options.chance.confusion(player, volatiles.confusion);
508
482
 
509
483
  if (volatiles.confusion == 0) {
510
484
  volatiles.Confusion = false;
@@ -665,7 +639,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
665
639
  }
666
640
  assert(stage <= 4);
667
641
 
668
- state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
642
+ state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
669
643
  }
670
644
 
671
645
  pub fn calcDamage(
@@ -733,7 +707,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
733
707
 
734
708
  pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
735
709
  if (state.damage <= 1) return;
736
- const random = try Rolls.damage(battle, player, options);
710
+ const random = try Rolls.damage(battle, player, state.hit, options);
737
711
  options.calc.base(player.foe(), state.damage);
738
712
  state.damage = @intCast(@as(u32, state.damage) *% random / 255);
739
713
  options.calc.final(player.foe(), state.damage);
@@ -1304,14 +1278,32 @@ pub const Effects = struct {
1304
1278
  }
1305
1279
 
1306
1280
  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
- //
1281
+ assert(!state.miss);
1282
+ var side = battle.side(player);
1283
+
1284
+ const ident = battle.active(player);
1285
+
1286
+ assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
1287
+ if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
1288
+ return options.log.fail(.{ ident, .None });
1289
+ }
1290
+
1291
+ const hp = @max(side.stored().stats.hp / 2, 1);
1292
+ if (side.stored().hp <= hp) {
1293
+ try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
1294
+ try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
1295
+ return options.log.fail(.{ ident, .None });
1296
+ } else {
1297
+ side.stored().hp -= hp;
1298
+ try options.log.damage(.{ ident, side.stored(), .None });
1299
+
1300
+ const before = side.active.boosts.atk;
1301
+ var i: usize = 0;
1302
+ while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
1303
+ try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
1304
+ }
1305
+ try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
1306
+ }
1315
1307
  }
1316
1308
 
1317
1309
  pub const bide = struct {
@@ -1323,13 +1315,7 @@ pub const Effects = struct {
1323
1315
 
1324
1316
  if (!volatiles.Bide) return;
1325
1317
 
1326
- // if (options.calc.modify(player, .attacking)) |extend| {
1327
- // if (!extend) volatiles.attacks = 0;
1328
- // } else {
1329
1318
  volatiles.attacks -= 1;
1330
- // }
1331
- try options.chance.attacking(player, volatiles.attacks);
1332
-
1333
1319
  if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
1334
1320
 
1335
1321
  volatiles.Bide = false;
@@ -1596,8 +1582,16 @@ pub const Effects = struct {
1596
1582
  try options.log.start(.{ foe_ident, .Encore, move.id });
1597
1583
  }
1598
1584
 
1599
- pub fn explode(battle: anytype, player: Player, state: *State, options: anytype) !void {
1600
- _ = .{ battle, player, state, options }; // TODO
1585
+ pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
1586
+ var side = battle.side(player);
1587
+ var stored = side.stored();
1588
+
1589
+ stored.hp = 0;
1590
+ // Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
1591
+ // to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
1592
+ if (!showdown) stored.status = 0;
1593
+ side.active.volatiles.LeechSeed = false;
1594
+ side.active.volatiles.DestinyBond = false;
1601
1595
  }
1602
1596
 
1603
1597
  pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
@@ -1723,8 +1717,26 @@ pub const Effects = struct {
1723
1717
  foe.active.volatiles.frozen = true;
1724
1718
  }
1725
1719
 
1726
- pub fn furyCutter(battle: anytype, player: Player, state: *State, options: anytype) !void {
1727
- _ = .{ battle, player, state, options }; // TODO
1720
+ // TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
1721
+ pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
1722
+ var side = battle.side(player);
1723
+ var volatiles = &side.active.volatiles;
1724
+
1725
+ if (state.miss) {
1726
+ volatiles.fury_cutter = 0;
1727
+ return;
1728
+ }
1729
+
1730
+ assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
1731
+ if (volatiles.fury_cutter == 5) return;
1732
+ volatiles.fury_cutter += 1;
1733
+
1734
+ if (showdown) {
1735
+ state.bp *= 2;
1736
+ assert(state.bp <= 160);
1737
+ } else {
1738
+ state.damage *|= 2;
1739
+ }
1728
1740
  }
1729
1741
 
1730
1742
  pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
@@ -1885,7 +1897,29 @@ pub const Effects = struct {
1885
1897
  }
1886
1898
 
1887
1899
  pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
1888
- _ = .{ battle, player, state, options }; // TODO
1900
+ var side = battle.side(player);
1901
+
1902
+ if (state.miss) {
1903
+ if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
1904
+ try options.log.lastmiss(.{});
1905
+ return options.log.miss(.{battle.active(player)});
1906
+ }
1907
+
1908
+ const move = battle.foe(player).lastMove(false);
1909
+ if (move == .None or move == .Struggle) {
1910
+ return options.log.fail(.{ battle.active(player.foe()), .None });
1911
+ }
1912
+
1913
+ var slot: u4 = 0;
1914
+ for (side.active.moves, 0..) |m, i| {
1915
+ if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
1916
+ if (m.id == .Mimic) slot = @intCast(i + 1);
1917
+ }
1918
+ assert(slot != 0 and slot == state.mslot);
1919
+
1920
+ side.active.move(slot).id = move;
1921
+ side.active.move(slot).pp = 5;
1922
+ try options.log.start(.{ battle.active(player), .Mimic, move });
1889
1923
  }
1890
1924
 
1891
1925
  pub fn mirrorMove(
@@ -2393,17 +2427,37 @@ pub const Effects = struct {
2393
2427
  }
2394
2428
 
2395
2429
  pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
2396
- _ = .{ battle, player, state, options }; // TODO
2397
- // var foe = battle.foe(player);
2430
+ var foe = battle.foe(player);
2398
2431
 
2399
- // if (state.miss) return options.log.fail(.{ battle.active(player), .None });
2432
+ const last = foe.lastMove(false);
2433
+ if (state.miss or last == .None or last == .Struggle) {
2434
+ return options.log.fail(.{ battle.active(player.foe()), .None });
2435
+ }
2400
2436
 
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
- // }
2437
+ var slot: u4 = 0;
2438
+ for (foe.active.moves, 0..) |m, i| {
2439
+ if (m.id == last) slot = @intCast(i + 1);
2440
+ }
2441
+ // If the move was called via Metronome it might not exist
2442
+ if (slot == 0 or foe.active.move(slot).pp == 0) {
2443
+ return options.log.fail(.{ battle.active(player.foe()), .None });
2444
+ }
2445
+
2446
+ // Pokémon Showdown always deducts 4 instead of randomizing
2447
+ const pp = @min(
2448
+ foe.active.move(slot).pp,
2449
+ if (showdown) 4 else try Rolls.spite(battle, player, options),
2450
+ );
2451
+ foe.active.move(slot).pp -= pp;
2452
+ if (!foe.active.volatiles.Transform) {
2453
+ var move_slot = foe.stored().move(slot);
2454
+ assert(move_slot.pp > 0);
2455
+
2456
+ move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
2457
+ assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
2458
+ }
2459
+
2460
+ try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
2407
2461
  }
2408
2462
 
2409
2463
  pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
@@ -2790,7 +2844,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
2790
2844
 
2791
2845
  pub const Rolls = struct {
2792
2846
  fn speedTie(battle: anytype, options: anytype) !bool {
2793
- const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
2847
+ const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
2794
2848
  player == .P1
2795
2849
  else if (showdown)
2796
2850
  battle.rng.range(u8, 0, 2) == 0
@@ -2802,7 +2856,7 @@ pub const Rolls = struct {
2802
2856
  }
2803
2857
 
2804
2858
  fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
2805
- const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
2859
+ const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
2806
2860
  val == .true
2807
2861
  else if (showdown)
2808
2862
  battle.rng.chance(u8, 60, 256)
@@ -2814,7 +2868,7 @@ pub const Rolls = struct {
2814
2868
  }
2815
2869
 
2816
2870
  fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
2817
- const ok = if (options.calc.overridden(player, .hit)) |val|
2871
+ const ok = if (options.calc.overridden(player, 0, .hit)) |val|
2818
2872
  val == .true
2819
2873
  else if (showdown)
2820
2874
  battle.rng.chance(u8, accuracy, 256)
@@ -2825,20 +2879,22 @@ pub const Rolls = struct {
2825
2879
  return ok;
2826
2880
  }
2827
2881
 
2828
- fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2829
- const crit = if (options.calc.overridden(player, .critical_hit)) |val|
2882
+ fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
2883
+ _ = .{ player, options };
2884
+ const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
2830
2885
  val == .true
2831
2886
  else if (showdown)
2832
2887
  battle.rng.chance(u8, rate, 256)
2833
2888
  else
2834
2889
  battle.rng.next() < rate;
2835
2890
 
2836
- try options.chance.criticalHit(player, crit, rate);
2891
+ try options.chance.criticalHit(player, i, crit, rate);
2837
2892
  return crit;
2838
2893
  }
2839
2894
 
2840
- fn damage(battle: anytype, player: Player, options: anytype) !u8 {
2841
- const roll = if (options.calc.overridden(player, .damage)) |val|
2895
+ fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
2896
+ _ = .{ player, options };
2897
+ const roll = if (options.calc.overridden(player, i, .damages)) |val|
2842
2898
  val
2843
2899
  else roll: {
2844
2900
  if (showdown) break :roll battle.rng.range(u8, 217, 256);
@@ -2849,12 +2905,12 @@ pub const Rolls = struct {
2849
2905
  };
2850
2906
 
2851
2907
  assert(roll >= 217 and roll <= 255);
2852
- try options.chance.damage(player, roll);
2908
+ try options.chance.damage(player, i, roll);
2853
2909
  return roll;
2854
2910
  }
2855
2911
 
2856
2912
  fn confused(battle: anytype, player: Player, options: anytype) !bool {
2857
- const cfz = if (options.calc.overridden(player, .confused)) |val|
2913
+ const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
2858
2914
  val == .true
2859
2915
  else if (showdown)
2860
2916
  !battle.rng.chance(u8, 128, 256)
@@ -2866,7 +2922,7 @@ pub const Rolls = struct {
2866
2922
  }
2867
2923
 
2868
2924
  fn attract(battle: anytype, player: Player, options: anytype) !bool {
2869
- const cant = if (options.calc.overridden(player, .attract)) |val|
2925
+ const cant = if (options.calc.overridden(player, 0, .attract)) |val|
2870
2926
  val == .true
2871
2927
  else if (showdown)
2872
2928
  battle.rng.chance(u8, 1, 2)
@@ -2878,7 +2934,7 @@ pub const Rolls = struct {
2878
2934
  }
2879
2935
 
2880
2936
  fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2881
- const par = if (options.calc.overridden(player, .paralyzed)) |val|
2937
+ const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
2882
2938
  val == .true
2883
2939
  else if (showdown)
2884
2940
  battle.rng.chance(u8, 63, 256)
@@ -2890,7 +2946,7 @@ pub const Rolls = struct {
2890
2946
  }
2891
2947
 
2892
2948
  fn defrost(battle: anytype, player: Player, options: anytype) !bool {
2893
- const thaw = if (options.calc.overridden(player, .defrost)) |val|
2949
+ const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
2894
2950
  val == .true
2895
2951
  else if (showdown)
2896
2952
  battle.rng.chance(u8, 25, 256)
@@ -2903,7 +2959,7 @@ pub const Rolls = struct {
2903
2959
 
2904
2960
  fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2905
2961
  // GLITCH: 100% secondary chance will still fail 1/256 of the time
2906
- const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2962
+ const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
2907
2963
  val == .true
2908
2964
  else if (showdown)
2909
2965
  battle.rng.chance(u8, rate, 256)
@@ -2915,7 +2971,7 @@ pub const Rolls = struct {
2915
2971
  }
2916
2972
 
2917
2973
  fn item(battle: anytype, player: Player, options: anytype) !bool {
2918
- const proc = if (options.calc.overridden(player, .item)) |val|
2974
+ const proc = if (options.calc.overridden(player, 0, .item)) |val|
2919
2975
  val == .true
2920
2976
  else if (showdown)
2921
2977
  battle.rng.chance(u8, 30, 256)
@@ -2927,7 +2983,7 @@ pub const Rolls = struct {
2927
2983
  }
2928
2984
 
2929
2985
  fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
2930
- const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
2986
+ const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
2931
2987
  @enumFromInt(@intFromEnum(val) - 1)
2932
2988
  else if (showdown)
2933
2989
  @enumFromInt(battle.rng.range(u2, 0, 2))
@@ -2943,7 +2999,7 @@ pub const Rolls = struct {
2943
2999
  }
2944
3000
 
2945
3001
  fn present(battle: anytype, player: Player, options: anytype) !u8 {
2946
- const power = if (options.calc.overridden(player, .present)) |val|
3002
+ const power = if (options.calc.overridden(player, 0, .present)) |val|
2947
3003
  (val - 1) * 40
2948
3004
  else if (showdown) power: {
2949
3005
  const r = battle.rng.range(u8, 0, 10);
@@ -2965,7 +3021,7 @@ pub const Rolls = struct {
2965
3021
  }
2966
3022
 
2967
3023
  fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
2968
- const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
3024
+ const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
2969
3025
  val + 3
2970
3026
  else if (showdown) num: {
2971
3027
  const r = battle.rng.range(u8, 0, 100);
@@ -2993,7 +3049,7 @@ pub const Rolls = struct {
2993
3049
  }
2994
3050
 
2995
3051
  fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
2996
- const hits = if (options.calc.overridden(player, .triple_kick)) |val|
3052
+ const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
2997
3053
  val
2998
3054
  else if (showdown)
2999
3055
  battle.rng.range(u2, 1, 4)
@@ -3009,13 +3065,12 @@ pub const Rolls = struct {
3009
3065
  return hits;
3010
3066
  }
3011
3067
 
3012
- fn spite(battle: anytype, player: Player, options: anytype) !u8 {
3013
- const pp = if (options.calc.overridden(player, .spite)) |val|
3068
+ fn spite(battle: anytype, player: Player, options: anytype) !u3 {
3069
+ assert(!showdown);
3070
+ const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
3014
3071
  val + 1
3015
- else if (showdown)
3016
- battle.rng.range(u8, 2, 6)
3017
3072
  else
3018
- (battle.rng.next() & 3) + 2;
3073
+ @intCast((battle.rng.next() & 3) + 2);
3019
3074
 
3020
3075
  assert(pp >= 2 and pp <= 5);
3021
3076
  try options.chance.spite(player, pp);
@@ -3029,7 +3084,7 @@ pub const Rolls = struct {
3029
3084
  var i: u8 = 0;
3030
3085
  const neutral = @intFromEnum(Effectiveness.Neutral);
3031
3086
  assert(mtype != .@"???");
3032
- const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
3087
+ const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
3033
3088
  @enumFromInt(@intFromEnum(val) - 1)
3034
3089
  else if (showdown) ty: {
3035
3090
  for (Type.SHOWDOWN) |t| {
@@ -3063,7 +3118,7 @@ pub const Rolls = struct {
3063
3118
  assert(count <= 8);
3064
3119
  const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
3065
3120
 
3066
- const ok = if (options.calc.overridden(player, .protect)) |val|
3121
+ const ok = if (options.calc.overridden(player, 0, .protect)) |val|
3067
3122
  val == .true
3068
3123
  else if (showdown)
3069
3124
  battle.rng.chance(u8, num, 255)
@@ -3085,7 +3140,7 @@ pub const Rolls = struct {
3085
3140
  const moves = active.moves;
3086
3141
 
3087
3142
  // TODO: consider throwing error instead of rerolling?
3088
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3143
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3089
3144
  if (convertible(active, moves[val - 1].id)) val else null
3090
3145
  else
3091
3146
  null;
@@ -3122,7 +3177,7 @@ pub const Rolls = struct {
3122
3177
  const moves = active.moves;
3123
3178
 
3124
3179
  // TODO: consider throwing error instead of rerolling?
3125
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3180
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3126
3181
  if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
3127
3182
  else
3128
3183
  null;
@@ -3166,7 +3221,7 @@ pub const Rolls = struct {
3166
3221
  assert(n > 0 and n <= 6);
3167
3222
 
3168
3223
  // TODO: consider throwing error instead of rerolling?
3169
- const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
3224
+ const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
3170
3225
  for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
3171
3226
  break :val null;
3172
3227
  } else null;
@@ -3192,7 +3247,7 @@ pub const Rolls = struct {
3192
3247
  const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
3193
3248
 
3194
3249
  fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
3195
- const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
3250
+ const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
3196
3251
  @intCast(val)
3197
3252
  else if (showdown)
3198
3253
  DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
@@ -3207,7 +3262,7 @@ pub const Rolls = struct {
3207
3262
  }
3208
3263
 
3209
3264
  fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
3210
- const power = if (options.calc.overridden(player, .psywave)) |val|
3265
+ const power = if (options.calc.overridden(player, 0, .psywave)) |val|
3211
3266
  val
3212
3267
  else if (showdown)
3213
3268
  battle.rng.range(u8, 1, max)
@@ -3233,7 +3288,7 @@ pub const Rolls = struct {
3233
3288
  }
3234
3289
 
3235
3290
  // TODO: consider throwing error instead of rerolling?
3236
- const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
3291
+ const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
3237
3292
  for (moves) |m| if (m.id == val) break :val null;
3238
3293
  break :val val;
3239
3294
  } else null;
@@ -3265,7 +3320,7 @@ pub const Rolls = struct {
3265
3320
  }
3266
3321
 
3267
3322
  fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
3268
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3323
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3269
3324
  @intCast(val)
3270
3325
  else if (showdown)
3271
3326
  battle.rng.range(u3, 1, 7)
@@ -3277,12 +3332,12 @@ pub const Rolls = struct {
3277
3332
  };
3278
3333
 
3279
3334
  assert(duration >= 1 and duration <= 6);
3280
- options.chance.duration(.sleep, player, player.foe(), duration);
3335
+ options.chance.duration(player, duration);
3281
3336
  return duration + 1;
3282
3337
  }
3283
3338
 
3284
3339
  fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
3285
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3340
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3286
3341
  @intCast(val)
3287
3342
  else if (showdown)
3288
3343
  // Pokémon Showdown incorrectly uses the same duration for self-confusion
@@ -3291,16 +3346,16 @@ pub const Rolls = struct {
3291
3346
  @intCast((battle.rng.next() & if (self) 1 else 3) + 2);
3292
3347
 
3293
3348
  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);
3349
+ options.chance.duration(player, duration);
3295
3350
  return duration;
3296
3351
  }
3297
3352
 
3298
3353
  fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
3299
- const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
3354
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3300
3355
  @intCast(val)
3301
3356
  else if (showdown)
3302
3357
  // Pokémon Showdown incorrectly inherits Generation III's Disable duration
3303
- battle.rng.range(u4, 2, 6)
3358
+ battle.rng.range(u3, 2, 6)
3304
3359
  else duration: {
3305
3360
  while (true) {
3306
3361
  const r = battle.rng.next() & 7;
@@ -3309,12 +3364,12 @@ pub const Rolls = struct {
3309
3364
  };
3310
3365
 
3311
3366
  assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
3312
- options.chance.duration(.disable, player, player.foe(), duration);
3367
+ options.chance.duration(player, duration);
3313
3368
  return duration;
3314
3369
  }
3315
3370
 
3316
3371
  fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
3317
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3372
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3318
3373
  @intCast(val)
3319
3374
  else if (showdown)
3320
3375
  battle.rng.range(u3, 2, 4)
@@ -3322,12 +3377,12 @@ pub const Rolls = struct {
3322
3377
  @intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
3323
3378
 
3324
3379
  assert(duration >= 2 and duration <= 3);
3325
- options.chance.duration(.attacking, player, player, duration);
3380
+ options.chance.duration(player, duration);
3326
3381
  return duration;
3327
3382
  }
3328
3383
 
3329
3384
  fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
3330
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3385
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3331
3386
  @intCast(val)
3332
3387
  else if (showdown)
3333
3388
  // TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
@@ -3336,13 +3391,13 @@ pub const Rolls = struct {
3336
3391
  @intCast(battle.rng.next() & 3 + 2));
3337
3392
 
3338
3393
  assert(duration >= 2 and duration <= 5);
3339
- options.chance.duration(.binding, player, player.foe(), duration);
3394
+ options.chance.duration(player, duration);
3340
3395
  return duration + 1;
3341
3396
  }
3342
3397
 
3343
3398
  // TODO: consider sharing implementation with bindingDuration
3344
3399
  fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
3345
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3400
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3346
3401
  @intCast(val)
3347
3402
  else if (showdown)
3348
3403
  battle.rng.range(u3, 2, 6)
@@ -3350,7 +3405,7 @@ pub const Rolls = struct {
3350
3405
  @intCast(battle.rng.next() & 3 + 2));
3351
3406
 
3352
3407
  assert(duration >= 2 and duration <= 5);
3353
- options.chance.duration(.encore, player, player.foe(), duration);
3408
+ options.chance.duration(player, duration);
3354
3409
  return duration + 1;
3355
3410
  }
3356
3411
  };
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 {};