@pkmn/engine 0.1.0-dev.deda8593 → 0.1.0-dev.e0a786f8

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 (79) hide show
  1. package/README.md +9 -6
  2. package/build/pkg/addon/browser.d.ts +3 -0
  3. package/build/pkg/addon/browser.js +47 -0
  4. package/build/pkg/addon/browser.js.map +1 -0
  5. package/build/pkg/addon/node.d.ts +3 -0
  6. package/build/pkg/addon/node.js +110 -0
  7. package/build/pkg/addon/node.js.map +1 -0
  8. package/build/pkg/addon/wasm.d.ts +2 -0
  9. package/build/pkg/addon/wasm.js +53 -0
  10. package/build/pkg/addon/wasm.js.map +1 -0
  11. package/build/pkg/addon.d.ts +25 -3
  12. package/build/pkg/addon.js +34 -65
  13. package/build/pkg/addon.js.map +1 -1
  14. package/build/pkg/data/layout.json +15 -2
  15. package/build/pkg/data/protocol.json +23 -9
  16. package/build/pkg/data.d.ts +23 -1
  17. package/build/pkg/data.js.map +1 -1
  18. package/build/pkg/gen1.d.ts +15 -18
  19. package/build/pkg/gen1.js +28 -19
  20. package/build/pkg/gen1.js.map +1 -1
  21. package/build/pkg/index.d.ts +40 -90
  22. package/build/pkg/index.js +23 -70
  23. package/build/pkg/index.js.map +1 -1
  24. package/build/pkg/protocol.d.ts +20 -1
  25. package/build/pkg/protocol.js +105 -103
  26. package/build/pkg/protocol.js.map +1 -1
  27. package/build/tools/debug.js +52 -76
  28. package/build/tools/debug.js.map +1 -1
  29. package/build/tools/display/debug.jsx +152 -0
  30. package/build/tools/display/debug.jsx.map +1 -0
  31. package/build/tools/display/index.js +152 -0
  32. package/build/tools/display/index.js.map +1 -0
  33. package/build/tools/display/ui.jsx +359 -0
  34. package/build/tools/display/ui.jsx.map +1 -0
  35. package/build/tools/display/util.js +33 -0
  36. package/build/tools/display/util.js.map +1 -0
  37. package/build.zig +330 -85
  38. package/package.json +54 -135
  39. package/src/bin/install-pkmn-engine +10 -7
  40. package/src/bin/pkmn-debug +1 -1
  41. package/src/lib/bindings/c.zig +255 -0
  42. package/src/lib/bindings/node.zig +121 -0
  43. package/src/lib/bindings/wasm.zig +59 -0
  44. package/src/lib/common/array.zig +77 -0
  45. package/src/lib/common/data.zig +8 -8
  46. package/src/lib/common/debug.zig +39 -8
  47. package/src/lib/common/js.zig +4 -2
  48. package/src/lib/common/optional.zig +13 -9
  49. package/src/lib/common/options.zig +4 -0
  50. package/src/lib/common/protocol.zig +45 -44
  51. package/src/lib/common/rational.zig +323 -157
  52. package/src/lib/common/rng.zig +5 -7
  53. package/src/lib/common/util.zig +60 -1
  54. package/src/lib/gen1/calc.zig +527 -161
  55. package/src/lib/gen1/chance.zig +366 -240
  56. package/src/lib/gen1/data/moves.zig +2 -4
  57. package/src/lib/gen1/data/species.zig +1 -3
  58. package/src/lib/gen1/data/types.zig +1 -2
  59. package/src/lib/gen1/data.zig +35 -38
  60. package/src/lib/gen1/helpers.zig +17 -554
  61. package/src/lib/gen1/mechanics.zig +280 -193
  62. package/src/lib/gen2/calc.zig +37 -27
  63. package/src/lib/gen2/chance.zig +90 -503
  64. package/src/lib/gen2/data/items.zig +1 -3
  65. package/src/lib/gen2/data/moves.zig +2 -4
  66. package/src/lib/gen2/data/species.zig +1 -3
  67. package/src/lib/gen2/data/types.zig +1 -2
  68. package/src/lib/gen2/data.zig +26 -29
  69. package/src/lib/gen2/generated.zig +18 -23
  70. package/src/lib/gen2/helpers.zig +9 -18
  71. package/src/lib/gen2/mechanics.zig +88 -115
  72. package/src/lib/node.zig +3 -116
  73. package/src/lib/pkmn.zig +14 -7
  74. package/src/lib/wasm.zig +13 -9
  75. package/src/tools/display/dom.ts +93 -0
  76. package/src/tools/display/ui.css +354 -0
  77. package/build/test/display.js +0 -400
  78. package/build/test/display.js.map +0 -1
  79. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -1,50 +1,37 @@
1
- const std = @import("std");
2
-
3
- const pkmn = @import("../pkmn.zig");
4
-
5
- const common = @import("../common/data.zig");
6
1
  const DEBUG = @import("../common/debug.zig").print;
7
- const protocol = @import("../common/protocol.zig");
8
- const rng = @import("../common/rng.zig");
9
- const util = @import("../common/util.zig");
10
2
 
3
+ const builtin = @import("builtin");
4
+ const common = @import("../common/data.zig");
11
5
  const data = @import("data.zig");
12
6
  const generated = @import("generated.zig");
13
-
14
- const assert = std.debug.assert;
15
-
16
- const expectEqual = std.testing.expectEqual;
17
- const expectEqualSlices = std.testing.expectEqualSlices;
18
- const expectEqualStrings = std.testing.expectEqualStrings;
19
-
20
- const showdown = pkmn.options.showdown;
21
-
22
- const Choice = common.Choice;
23
- const ID = common.ID;
24
- const Player = common.Player;
25
- const Result = common.Result;
7
+ const pkmn = @import("../pkmn.zig");
8
+ const protocol = @import("../common/protocol.zig");
9
+ const rng = @import("../common/rng.zig");
10
+ const std = @import("std");
26
11
 
27
12
  const Activate = protocol.Activate;
13
+ const ActivePokemon = data.ActivePokemon;
14
+ const assert = std.debug.assert;
28
15
  const Boost = protocol.Boost;
16
+ const Boosts = data.Boosts;
17
+ const Choice = common.Choice;
29
18
  const Damage = protocol.Damage;
19
+ const Effectiveness = data.Effectiveness;
30
20
  const End = protocol.End;
21
+ const expectEqual = std.testing.expectEqual;
22
+ const expectEqualSlices = std.testing.expectEqualSlices;
23
+ const expectEqualStrings = std.testing.expectEqualStrings;
31
24
  const Fail = protocol.Fail;
32
- const Heal = protocol.Heal;
33
- const Start = protocol.Start;
34
-
35
25
  const Gen12 = rng.Gen12;
36
-
37
- const PointerType = util.PointerType;
38
-
39
- const ActivePokemon = data.ActivePokemon;
40
- const Boosts = data.Boosts;
41
- const Effectiveness = data.Effectiveness;
26
+ const Heal = protocol.Heal;
27
+ const ID = common.ID;
42
28
  const Item = data.Item;
43
29
  const Move = data.Move;
44
- const MoveSlot = data.MoveSlot;
45
- const Pokemon = data.Pokemon;
30
+ const Player = common.Player;
31
+ const Result = common.Result;
32
+ const showdown = pkmn.options.showdown;
46
33
  const Side = data.Side;
47
- const Species = data.Species;
34
+ const Start = protocol.Start;
48
35
  const Stats = data.Stats;
49
36
  const Status = data.Status;
50
37
  const TriAttack = data.TriAttack;
@@ -106,6 +93,7 @@ pub const State = struct {
106
93
  type: Type, // BATTLE_VARS_MOVE_TYPE
107
94
  accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
108
95
  move: Move, // wCurPlayerMove
96
+ hit: u3 = 0,
109
97
  from: Move = .None,
110
98
  mslot: u4, // wCurMoveNum
111
99
  first: bool, // wEnemyGoesFirst
@@ -256,7 +244,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
256
244
  } else {
257
245
  try options.log.switched(.{ battle.active(player), incoming });
258
246
  }
259
- options.chance.switched(player, side.pokemon[0].position, out);
260
247
 
261
248
  if (side.conditions.Spikes and !active.types.includes(.Flying)) {
262
249
  incoming.hp -|= @max(incoming.stats.hp / 8, 1);
@@ -442,18 +429,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
442
429
  if (Status.is(stored.status, .SLP)) slp: {
443
430
  const before = stored.status;
444
431
  // 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
432
  stored.status -= 1;
449
- // }
450
433
 
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) {
434
+ if (Status.duration(stored.status) == 0) {
457
435
  try log.curestatus(.{ ident, before, .Message });
458
436
  stored.status = 0; // clears EXT if present
459
437
  resetCant(volatiles);
@@ -483,12 +461,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
483
461
  }
484
462
 
485
463
  if (volatiles.disable.duration > 0) {
486
- // if (options.calc.modify(player, .disable)) |extend| {
487
- // if (!extend) volatiles.disable.duration = 0;
488
- // } else {
489
464
  volatiles.disable.duration -= 1;
490
- // }
491
- try options.chance.disable(player, volatiles.disable.duration);
492
465
 
493
466
  if (volatiles.disable.duration == 0) {
494
467
  volatiles.disable.move = 0;
@@ -498,13 +471,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
498
471
 
499
472
  if (volatiles.Confusion) {
500
473
  assert(volatiles.confusion > 0);
501
-
502
- // if (options.calc.modify(player, .confusion)) |extend| {
503
- // if (!extend) volatiles.confusion = 0;
504
- // } else {
505
474
  volatiles.confusion -= 1;
506
- // }
507
- try options.chance.confusion(player, volatiles.confusion);
508
475
 
509
476
  if (volatiles.confusion == 0) {
510
477
  volatiles.Confusion = false;
@@ -665,7 +632,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
665
632
  }
666
633
  assert(stage <= 4);
667
634
 
668
- state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
635
+ state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
669
636
  }
670
637
 
671
638
  pub fn calcDamage(
@@ -733,7 +700,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
733
700
 
734
701
  pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
735
702
  if (state.damage <= 1) return;
736
- const random = try Rolls.damage(battle, player, options);
703
+ const random = try Rolls.damage(battle, player, state.hit, options);
737
704
  options.calc.base(player.foe(), state.damage);
738
705
  state.damage = @intCast(@as(u32, state.damage) *% random / 255);
739
706
  options.calc.final(player.foe(), state.damage);
@@ -1226,7 +1193,7 @@ fn updateSideCondition(
1226
1193
  fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
1227
1194
  battle.turn += 1;
1228
1195
 
1229
- if (showdown and battle.turn >= 1000) {
1196
+ if (pkmn.options.mod and battle.turn >= 1000) {
1230
1197
  try options.log.tie(.{});
1231
1198
  return Result.Tie;
1232
1199
  } else if (battle.turn >= 65535) {
@@ -1341,13 +1308,7 @@ pub const Effects = struct {
1341
1308
 
1342
1309
  if (!volatiles.Bide) return;
1343
1310
 
1344
- // if (options.calc.modify(player, .attacking)) |extend| {
1345
- // if (!extend) volatiles.attacks = 0;
1346
- // } else {
1347
1311
  volatiles.attacks -= 1;
1348
- // }
1349
- try options.chance.attacking(player, volatiles.attacks);
1350
-
1351
1312
  if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
1352
1313
 
1353
1314
  volatiles.Bide = false;
@@ -1466,7 +1427,8 @@ pub const Effects = struct {
1466
1427
  if (move.type.special() != (last == .MirrorCoat)) return;
1467
1428
  if (state.damage == 0) return;
1468
1429
 
1469
- state.damage *|= 2;
1430
+ // NOTE: llvm/llvm-project#58557
1431
+ state.damage = smul(state.damage, 2);
1470
1432
  state.miss = false;
1471
1433
  }
1472
1434
 
@@ -1561,15 +1523,18 @@ pub const Effects = struct {
1561
1523
 
1562
1524
  pub const double = struct {
1563
1525
  pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
1564
- if (battle.foe(player).active.volatiles.Flying) state.damage *|= 2;
1526
+ if (!battle.foe(player).active.volatiles.Flying) return;
1527
+ state.damage = smul(state.damage, 2);
1565
1528
  }
1566
1529
 
1567
1530
  pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
1568
- if (battle.foe(player).active.volatiles.Underground) state.damage *|= 2;
1531
+ if (!battle.foe(player).active.volatiles.Underground) return;
1532
+ state.damage = smul(state.damage, 2);
1569
1533
  }
1570
1534
 
1571
1535
  pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
1572
- if (battle.foe(player).active.volatiles.minimized) state.damage *|= 2;
1536
+ if (!battle.foe(player).active.volatiles.minimized) return;
1537
+ state.damage = smul(state.damage, 2);
1573
1538
  }
1574
1539
  };
1575
1540
 
@@ -1738,7 +1703,7 @@ pub const Effects = struct {
1738
1703
  if (!state.proc) return;
1739
1704
  if (foe.conditions.Safeguard) return;
1740
1705
  // Freeze Clause Mod
1741
- if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1706
+ if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
1742
1707
 
1743
1708
  foe_stored.status = Status.init(.FRZ);
1744
1709
  try options.log.status(.{ foe_ident, foe_stored.status, .None });
@@ -1767,7 +1732,7 @@ pub const Effects = struct {
1767
1732
  state.bp *= 2;
1768
1733
  assert(state.bp <= 160);
1769
1734
  } else {
1770
- state.damage *|= 2;
1735
+ state.damage = smul(state.damage, 2);
1771
1736
  }
1772
1737
  }
1773
1738
 
@@ -2238,7 +2203,7 @@ pub const Effects = struct {
2238
2203
 
2239
2204
  pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
2240
2205
  // TODO: remove switching variable and just look at Choice saved in Battle struct?
2241
- if (battle.foe(player).active.volatiles.switching) state.damage *|= 2;
2206
+ if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
2242
2207
  }
2243
2208
 
2244
2209
  pub const rage = struct {
@@ -2251,7 +2216,7 @@ pub const Effects = struct {
2251
2216
  const side = battle.side(player);
2252
2217
 
2253
2218
  assert(side.active.volatiles.Rage);
2254
- state.damage *|= (side.active.volatiles.rage +| 1);
2219
+ state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
2255
2220
  }
2256
2221
  };
2257
2222
 
@@ -2346,7 +2311,7 @@ pub const Effects = struct {
2346
2311
  side.active.volatiles.attacks += 1;
2347
2312
  side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
2348
2313
 
2349
- if (side.active.volatiles.DefenseCurl) state.damage *|= 2;
2314
+ if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
2350
2315
  }
2351
2316
  };
2352
2317
 
@@ -2416,7 +2381,7 @@ pub const Effects = struct {
2416
2381
  }
2417
2382
 
2418
2383
  // Sleep Clause Mod
2419
- if (showdown) {
2384
+ if (pkmn.options.mod) {
2420
2385
  for (foe.pokemon) |p| {
2421
2386
  if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
2422
2387
  }
@@ -2874,9 +2839,15 @@ fn convertible(active: ActivePokemon, m: Move) bool {
2874
2839
  return t != .@"???" and !active.types.includes(t);
2875
2840
  }
2876
2841
 
2842
+ // NOTE: llvm/llvm-project#58557
2843
+ fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
2844
+ if (!comptime builtin.target.isWasm()) return a *| b;
2845
+ return @max(a *% b, std.math.maxInt(@TypeOf(a)));
2846
+ }
2847
+
2877
2848
  pub const Rolls = struct {
2878
2849
  fn speedTie(battle: anytype, options: anytype) !bool {
2879
- const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
2850
+ const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
2880
2851
  player == .P1
2881
2852
  else if (showdown)
2882
2853
  battle.rng.range(u8, 0, 2) == 0
@@ -2888,7 +2859,7 @@ pub const Rolls = struct {
2888
2859
  }
2889
2860
 
2890
2861
  fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
2891
- const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
2862
+ const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
2892
2863
  val == .true
2893
2864
  else if (showdown)
2894
2865
  battle.rng.chance(u8, 60, 256)
@@ -2900,7 +2871,7 @@ pub const Rolls = struct {
2900
2871
  }
2901
2872
 
2902
2873
  fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
2903
- const ok = if (options.calc.overridden(player, .hit)) |val|
2874
+ const ok = if (options.calc.overridden(player, 0, .hit)) |val|
2904
2875
  val == .true
2905
2876
  else if (showdown)
2906
2877
  battle.rng.chance(u8, accuracy, 256)
@@ -2911,20 +2882,22 @@ pub const Rolls = struct {
2911
2882
  return ok;
2912
2883
  }
2913
2884
 
2914
- fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2915
- const crit = if (options.calc.overridden(player, .critical_hit)) |val|
2885
+ fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
2886
+ _ = .{ player, options };
2887
+ const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
2916
2888
  val == .true
2917
2889
  else if (showdown)
2918
2890
  battle.rng.chance(u8, rate, 256)
2919
2891
  else
2920
2892
  battle.rng.next() < rate;
2921
2893
 
2922
- try options.chance.criticalHit(player, crit, rate);
2894
+ try options.chance.criticalHit(player, i, crit, rate);
2923
2895
  return crit;
2924
2896
  }
2925
2897
 
2926
- fn damage(battle: anytype, player: Player, options: anytype) !u8 {
2927
- const roll = if (options.calc.overridden(player, .damage)) |val|
2898
+ fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
2899
+ _ = .{ player, options };
2900
+ const roll = if (options.calc.overridden(player, i, .damages)) |val|
2928
2901
  val
2929
2902
  else roll: {
2930
2903
  if (showdown) break :roll battle.rng.range(u8, 217, 256);
@@ -2935,12 +2908,12 @@ pub const Rolls = struct {
2935
2908
  };
2936
2909
 
2937
2910
  assert(roll >= 217 and roll <= 255);
2938
- try options.chance.damage(player, roll);
2911
+ try options.chance.damage(player, i, roll);
2939
2912
  return roll;
2940
2913
  }
2941
2914
 
2942
2915
  fn confused(battle: anytype, player: Player, options: anytype) !bool {
2943
- const cfz = if (options.calc.overridden(player, .confused)) |val|
2916
+ const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
2944
2917
  val == .true
2945
2918
  else if (showdown)
2946
2919
  !battle.rng.chance(u8, 128, 256)
@@ -2952,7 +2925,7 @@ pub const Rolls = struct {
2952
2925
  }
2953
2926
 
2954
2927
  fn attract(battle: anytype, player: Player, options: anytype) !bool {
2955
- const cant = if (options.calc.overridden(player, .attract)) |val|
2928
+ const cant = if (options.calc.overridden(player, 0, .attract)) |val|
2956
2929
  val == .true
2957
2930
  else if (showdown)
2958
2931
  battle.rng.chance(u8, 1, 2)
@@ -2964,7 +2937,7 @@ pub const Rolls = struct {
2964
2937
  }
2965
2938
 
2966
2939
  fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2967
- const par = if (options.calc.overridden(player, .paralyzed)) |val|
2940
+ const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
2968
2941
  val == .true
2969
2942
  else if (showdown)
2970
2943
  battle.rng.chance(u8, 63, 256)
@@ -2976,7 +2949,7 @@ pub const Rolls = struct {
2976
2949
  }
2977
2950
 
2978
2951
  fn defrost(battle: anytype, player: Player, options: anytype) !bool {
2979
- const thaw = if (options.calc.overridden(player, .defrost)) |val|
2952
+ const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
2980
2953
  val == .true
2981
2954
  else if (showdown)
2982
2955
  battle.rng.chance(u8, 25, 256)
@@ -2989,7 +2962,7 @@ pub const Rolls = struct {
2989
2962
 
2990
2963
  fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2991
2964
  // GLITCH: 100% secondary chance will still fail 1/256 of the time
2992
- const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2965
+ const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
2993
2966
  val == .true
2994
2967
  else if (showdown)
2995
2968
  battle.rng.chance(u8, rate, 256)
@@ -3001,7 +2974,7 @@ pub const Rolls = struct {
3001
2974
  }
3002
2975
 
3003
2976
  fn item(battle: anytype, player: Player, options: anytype) !bool {
3004
- const proc = if (options.calc.overridden(player, .item)) |val|
2977
+ const proc = if (options.calc.overridden(player, 0, .item)) |val|
3005
2978
  val == .true
3006
2979
  else if (showdown)
3007
2980
  battle.rng.chance(u8, 30, 256)
@@ -3013,7 +2986,7 @@ pub const Rolls = struct {
3013
2986
  }
3014
2987
 
3015
2988
  fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
3016
- const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
2989
+ const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
3017
2990
  @enumFromInt(@intFromEnum(val) - 1)
3018
2991
  else if (showdown)
3019
2992
  @enumFromInt(battle.rng.range(u2, 0, 2))
@@ -3029,7 +3002,7 @@ pub const Rolls = struct {
3029
3002
  }
3030
3003
 
3031
3004
  fn present(battle: anytype, player: Player, options: anytype) !u8 {
3032
- const power = if (options.calc.overridden(player, .present)) |val|
3005
+ const power = if (options.calc.overridden(player, 0, .present)) |val|
3033
3006
  (val - 1) * 40
3034
3007
  else if (showdown) power: {
3035
3008
  const r = battle.rng.range(u8, 0, 10);
@@ -3051,7 +3024,7 @@ pub const Rolls = struct {
3051
3024
  }
3052
3025
 
3053
3026
  fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
3054
- const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
3027
+ const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
3055
3028
  val + 3
3056
3029
  else if (showdown) num: {
3057
3030
  const r = battle.rng.range(u8, 0, 100);
@@ -3079,7 +3052,7 @@ pub const Rolls = struct {
3079
3052
  }
3080
3053
 
3081
3054
  fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
3082
- const hits = if (options.calc.overridden(player, .triple_kick)) |val|
3055
+ const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
3083
3056
  val
3084
3057
  else if (showdown)
3085
3058
  battle.rng.range(u2, 1, 4)
@@ -3097,7 +3070,7 @@ pub const Rolls = struct {
3097
3070
 
3098
3071
  fn spite(battle: anytype, player: Player, options: anytype) !u3 {
3099
3072
  assert(!showdown);
3100
- const pp: u3 = if (options.calc.overridden(player, .spite)) |val|
3073
+ const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
3101
3074
  val + 1
3102
3075
  else
3103
3076
  @intCast((battle.rng.next() & 3) + 2);
@@ -3114,7 +3087,7 @@ pub const Rolls = struct {
3114
3087
  var i: u8 = 0;
3115
3088
  const neutral = @intFromEnum(Effectiveness.Neutral);
3116
3089
  assert(mtype != .@"???");
3117
- const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
3090
+ const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
3118
3091
  @enumFromInt(@intFromEnum(val) - 1)
3119
3092
  else if (showdown) ty: {
3120
3093
  for (Type.SHOWDOWN) |t| {
@@ -3148,7 +3121,7 @@ pub const Rolls = struct {
3148
3121
  assert(count <= 8);
3149
3122
  const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
3150
3123
 
3151
- const ok = if (options.calc.overridden(player, .protect)) |val|
3124
+ const ok = if (options.calc.overridden(player, 0, .protect)) |val|
3152
3125
  val == .true
3153
3126
  else if (showdown)
3154
3127
  battle.rng.chance(u8, num, 255)
@@ -3170,7 +3143,7 @@ pub const Rolls = struct {
3170
3143
  const moves = active.moves;
3171
3144
 
3172
3145
  // TODO: consider throwing error instead of rerolling?
3173
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3146
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3174
3147
  if (convertible(active, moves[val - 1].id)) val else null
3175
3148
  else
3176
3149
  null;
@@ -3207,7 +3180,7 @@ pub const Rolls = struct {
3207
3180
  const moves = active.moves;
3208
3181
 
3209
3182
  // TODO: consider throwing error instead of rerolling?
3210
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3183
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3211
3184
  if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
3212
3185
  else
3213
3186
  null;
@@ -3251,7 +3224,7 @@ pub const Rolls = struct {
3251
3224
  assert(n > 0 and n <= 6);
3252
3225
 
3253
3226
  // TODO: consider throwing error instead of rerolling?
3254
- const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
3227
+ const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
3255
3228
  for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
3256
3229
  break :val null;
3257
3230
  } else null;
@@ -3277,7 +3250,7 @@ pub const Rolls = struct {
3277
3250
  const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
3278
3251
 
3279
3252
  fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
3280
- const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
3253
+ const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
3281
3254
  @intCast(val)
3282
3255
  else if (showdown)
3283
3256
  DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
@@ -3292,7 +3265,7 @@ pub const Rolls = struct {
3292
3265
  }
3293
3266
 
3294
3267
  fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
3295
- const power = if (options.calc.overridden(player, .psywave)) |val|
3268
+ const power = if (options.calc.overridden(player, 0, .psywave)) |val|
3296
3269
  val
3297
3270
  else if (showdown)
3298
3271
  battle.rng.range(u8, 1, max)
@@ -3318,7 +3291,7 @@ pub const Rolls = struct {
3318
3291
  }
3319
3292
 
3320
3293
  // TODO: consider throwing error instead of rerolling?
3321
- const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
3294
+ const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
3322
3295
  for (moves) |m| if (m.id == val) break :val null;
3323
3296
  break :val val;
3324
3297
  } else null;
@@ -3350,7 +3323,7 @@ pub const Rolls = struct {
3350
3323
  }
3351
3324
 
3352
3325
  fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
3353
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3326
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3354
3327
  @intCast(val)
3355
3328
  else if (showdown)
3356
3329
  battle.rng.range(u3, 1, 7)
@@ -3362,12 +3335,12 @@ pub const Rolls = struct {
3362
3335
  };
3363
3336
 
3364
3337
  assert(duration >= 1 and duration <= 6);
3365
- options.chance.duration(.sleep, player, player.foe(), duration);
3338
+ options.chance.duration(player, duration);
3366
3339
  return duration + 1;
3367
3340
  }
3368
3341
 
3369
3342
  fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
3370
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3343
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3371
3344
  @intCast(val)
3372
3345
  else if (showdown)
3373
3346
  // Pokémon Showdown incorrectly uses the same duration for self-confusion
@@ -3376,16 +3349,16 @@ pub const Rolls = struct {
3376
3349
  @intCast((battle.rng.next() & if (self) 1 else 3) + 2);
3377
3350
 
3378
3351
  assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
3379
- options.chance.duration(.confusion, player, if (self) player else player.foe(), duration);
3352
+ options.chance.duration(player, duration);
3380
3353
  return duration;
3381
3354
  }
3382
3355
 
3383
3356
  fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
3384
- const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
3357
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3385
3358
  @intCast(val)
3386
3359
  else if (showdown)
3387
3360
  // Pokémon Showdown incorrectly inherits Generation III's Disable duration
3388
- battle.rng.range(u4, 2, 6)
3361
+ battle.rng.range(u3, 2, 6)
3389
3362
  else duration: {
3390
3363
  while (true) {
3391
3364
  const r = battle.rng.next() & 7;
@@ -3394,12 +3367,12 @@ pub const Rolls = struct {
3394
3367
  };
3395
3368
 
3396
3369
  assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
3397
- options.chance.duration(.disable, player, player.foe(), duration);
3370
+ options.chance.duration(player, duration);
3398
3371
  return duration;
3399
3372
  }
3400
3373
 
3401
3374
  fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
3402
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3375
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3403
3376
  @intCast(val)
3404
3377
  else if (showdown)
3405
3378
  battle.rng.range(u3, 2, 4)
@@ -3407,12 +3380,12 @@ pub const Rolls = struct {
3407
3380
  @intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
3408
3381
 
3409
3382
  assert(duration >= 2 and duration <= 3);
3410
- options.chance.duration(.attacking, player, player, duration);
3383
+ options.chance.duration(player, duration);
3411
3384
  return duration;
3412
3385
  }
3413
3386
 
3414
3387
  fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
3415
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3388
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3416
3389
  @intCast(val)
3417
3390
  else if (showdown)
3418
3391
  // TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
@@ -3421,13 +3394,13 @@ pub const Rolls = struct {
3421
3394
  @intCast(battle.rng.next() & 3 + 2));
3422
3395
 
3423
3396
  assert(duration >= 2 and duration <= 5);
3424
- options.chance.duration(.binding, player, player.foe(), duration);
3397
+ options.chance.duration(player, duration);
3425
3398
  return duration + 1;
3426
3399
  }
3427
3400
 
3428
3401
  // TODO: consider sharing implementation with bindingDuration
3429
3402
  fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
3430
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3403
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3431
3404
  @intCast(val)
3432
3405
  else if (showdown)
3433
3406
  battle.rng.range(u3, 2, 6)
@@ -3435,7 +3408,7 @@ pub const Rolls = struct {
3435
3408
  @intCast(battle.rng.next() & 3 + 2));
3436
3409
 
3437
3410
  assert(duration >= 2 and duration <= 5);
3438
- options.chance.duration(.encore, player, player.foe(), duration);
3411
+ options.chance.duration(player, duration);
3439
3412
  return duration + 1;
3440
3413
  }
3441
3414
  };