@pkmn/engine 0.1.0-dev.a0167a9d → 0.1.0-dev.a0447192

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 (60) hide show
  1. package/README.md +4 -2
  2. package/build/pkg/addon/browser.d.ts +3 -0
  3. package/build/pkg/addon/browser.js +43 -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 +96 -0
  7. package/build/pkg/addon/node.js.map +1 -0
  8. package/build/pkg/addon.d.ts +2 -0
  9. package/build/pkg/addon.js +29 -51
  10. package/build/pkg/addon.js.map +1 -1
  11. package/build/pkg/data.d.ts +23 -1
  12. package/build/pkg/data.js.map +1 -1
  13. package/build/pkg/gen1.js +1 -1
  14. package/build/pkg/gen1.js.map +1 -1
  15. package/build/pkg/index.d.ts +22 -9
  16. package/build/pkg/index.js +15 -10
  17. package/build/pkg/index.js.map +1 -1
  18. package/build/pkg/protocol.d.ts +20 -1
  19. package/build/pkg/protocol.js +105 -103
  20. package/build/pkg/protocol.js.map +1 -1
  21. package/build/tools/debug.js +35 -68
  22. package/build/tools/debug.js.map +1 -1
  23. package/build/tools/display/debug.jsx +141 -0
  24. package/build/tools/display/debug.jsx.map +1 -0
  25. package/build/tools/display/index.js +132 -0
  26. package/build/tools/display/index.js.map +1 -0
  27. package/build/tools/display/ui.jsx +349 -0
  28. package/build/tools/display/ui.jsx.map +1 -0
  29. package/build/tools/display/util.js +18 -0
  30. package/build/tools/display/util.js.map +1 -0
  31. package/build.zig +199 -66
  32. package/package.json +46 -29
  33. package/src/bin/install-pkmn-engine +7 -5
  34. package/src/bin/pkmn-debug +1 -1
  35. package/src/lib/common/array.zig +77 -0
  36. package/src/lib/common/data.zig +8 -8
  37. package/src/lib/common/debug.zig +39 -8
  38. package/src/lib/common/js.zig +4 -2
  39. package/src/lib/common/optional.zig +13 -9
  40. package/src/lib/common/options.zig +2 -0
  41. package/src/lib/common/protocol.zig +41 -36
  42. package/src/lib/common/rational.zig +53 -51
  43. package/src/lib/common/rng.zig +4 -4
  44. package/src/lib/common/util.zig +50 -1
  45. package/src/lib/gen1/calc.zig +508 -127
  46. package/src/lib/gen1/chance.zig +316 -173
  47. package/src/lib/gen1/data.zig +28 -27
  48. package/src/lib/gen1/helpers.zig +15 -450
  49. package/src/lib/gen1/mechanics.zig +216 -145
  50. package/src/lib/gen2/calc.zig +34 -16
  51. package/src/lib/gen2/chance.zig +90 -592
  52. package/src/lib/gen2/data.zig +15 -14
  53. package/src/lib/gen2/helpers.zig +4 -6
  54. package/src/lib/gen2/mechanics.zig +47 -77
  55. package/src/lib/pkmn.zig +7 -5
  56. package/src/tools/display/dom.ts +96 -0
  57. package/src/tools/display/ui.css +359 -0
  58. package/build/test/display.js +0 -400
  59. package/build/test/display.js.map +0 -1
  60. package/src/test/display/pkmn.html.tmpl +0 -368
@@ -45,6 +45,7 @@ const Player = data.Player;
45
45
  const Result = data.Result;
46
46
 
47
47
  const PointerType = util.PointerType;
48
+ const isPointerTo = util.isPointerTo;
48
49
 
49
50
  const showdown = pkmn.options.showdown;
50
51
 
@@ -63,12 +64,12 @@ pub fn Battle(comptime RNG: anytype) type {
63
64
  _: [padding]u8 = .{0} ** padding,
64
65
 
65
66
  pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
66
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
67
+ assert(isPointerTo(self, Self));
67
68
  return &self.sides[@intFromEnum(player)];
68
69
  }
69
70
 
70
71
  pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
71
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
72
+ assert(isPointerTo(self, Self));
72
73
  return &self.sides[@intFromEnum(player.foe())];
73
74
  }
74
75
 
@@ -134,7 +135,7 @@ pub const Side = extern struct {
134
135
  }
135
136
 
136
137
  pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
137
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
138
+ assert(isPointerTo(self, Side));
138
139
  assert(slot > 0 and slot <= 6);
139
140
  const id = self.pokemon[slot - 1].position;
140
141
  assert(id > 0 and id <= 6);
@@ -142,7 +143,7 @@ pub const Side = extern struct {
142
143
  }
143
144
 
144
145
  pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
145
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
146
+ assert(isPointerTo(self, Side));
146
147
  return self.get(1);
147
148
  }
148
149
 
@@ -176,7 +177,7 @@ pub const ActivePokemon = extern struct {
176
177
  }
177
178
 
178
179
  pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
179
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
180
+ assert(isPointerTo(self, Pokemon));
180
181
  assert(mslot > 0 and mslot <= 4);
181
182
  assert(self.moves[mslot - 1].id != .None);
182
183
  return &self.moves[mslot - 1];
@@ -201,7 +202,7 @@ pub const Pokemon = extern struct {
201
202
  }
202
203
 
203
204
  pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
204
- assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
205
+ assert(isPointerTo(self, Pokemon));
205
206
  assert(mslot > 0 and mslot <= 4);
206
207
  assert(self.moves[mslot - 1].id != .None);
207
208
  return &self.moves[mslot - 1];
@@ -248,12 +249,12 @@ test DVs {
248
249
  var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
249
250
  try expectEqual(Gender.Unknown, dvs.gender);
250
251
  try expectEqual(Type.Ice, dvs.type);
251
- try expectEqual(70, dvs.power());
252
+ try expectEqual(@as(u8, 70), dvs.power());
252
253
 
253
254
  dvs = DVs.from(.Pikachu, .{ .spc = 14 });
254
255
  try expectEqual(Gender.Male, dvs.gender);
255
256
  try expectEqual(Type.Dark, dvs.type);
256
- try expectEqual(69, dvs.power());
257
+ try expectEqual(@as(u8, 69), dvs.power());
257
258
 
258
259
  dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
259
260
  try expectEqual(Gender.Female, dvs.gender);
@@ -380,9 +381,9 @@ test Stats {
380
381
  try expectEqual(0, stats.def);
381
382
 
382
383
  var base = Species.get(.Gyarados).stats;
383
- try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
384
+ try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
384
385
  base = Species.get(.Pidgeot).stats;
385
- try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
386
+ try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
386
387
  }
387
388
 
388
389
  pub const Boosts = packed struct(u32) {
@@ -428,8 +429,8 @@ test Move {
428
429
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
429
430
  try expectEqual(rng.Gen12.percent(50), move.accuracy);
430
431
  try expectEqual(rng.Gen12.percent(100), move.chance);
431
- try expectEqual(5, Move.pp(.DynamicPunch));
432
- try expectEqual(-1, Move.get(.Counter).priority);
432
+ try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
433
+ try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
433
434
  try expect(!move.effect.isHighCritical());
434
435
  try expect(Move.get(.KarateChop).effect.isHighCritical());
435
436
  try expect(Move.get(.RazorWind).effect.isHighCritical());
@@ -439,7 +440,7 @@ pub const Species = species.Species;
439
440
 
440
441
  test Species {
441
442
  try expectEqual(152, @intFromEnum(Species.Chikorita));
442
- try expectEqual(100, Species.get(.Celebi).stats.spd);
443
+ try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
443
444
  }
444
445
 
445
446
  pub const Type = types.Type;
@@ -452,7 +453,7 @@ test Types {
452
453
  try expect(!Type.Steel.special());
453
454
  try expect(Type.Dark.special());
454
455
 
455
- try expectEqual(3, Type.Water.precedence());
456
+ try expectEqual(@as(u8, 3), Type.Water.precedence());
456
457
  try expect(Type.Bug.precedence() > Type.Poison.precedence());
457
458
 
458
459
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
@@ -2,7 +2,6 @@ const std = @import("std");
2
2
 
3
3
  const common = @import("../common/data.zig");
4
4
  const options = @import("../common/options.zig");
5
- const protocol = @import("../common/protocol.zig");
6
5
  const rng = @import("../common/rng.zig");
7
6
 
8
7
  const gen1 = @import("../gen1/data.zig");
@@ -11,8 +10,6 @@ const data = @import("data.zig");
11
10
 
12
11
  const assert = std.debug.assert;
13
12
 
14
- const expectEqual = std.testing.expectEqual;
15
-
16
13
  const Choice = common.Choice;
17
14
 
18
15
  const showdown = options.showdown;
@@ -25,7 +22,8 @@ const Move = data.Move;
25
22
  const MoveSlot = data.MoveSlot;
26
23
  const Species = data.Species;
27
24
  const Stats = data.Stats;
28
- const Status = data.Status;
25
+
26
+ const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
29
27
 
30
28
  pub const Options = struct {
31
29
  cleric: bool = showdown,
@@ -151,7 +149,7 @@ pub const Pokemon = struct {
151
149
  var pokemon = data.Pokemon{};
152
150
  pokemon.species = p.species;
153
151
  const species = Species.get(p.species);
154
- inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
152
+ inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
155
153
  const hp = comptime std.mem.eql(u8, field.name, "hp");
156
154
  const spc =
157
155
  comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
@@ -192,7 +190,7 @@ pub const Pokemon = struct {
192
190
 
193
191
  var stats: Stats(u16) = .{};
194
192
  const dvs = gen1.DVs.random(rand);
195
- inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
193
+ inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
196
194
  const hp = std.mem.eql(u8, field.name, "hp");
197
195
  const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
198
196
  @field(stats, field.name) = Stats(u16).calc(
@@ -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);
@@ -1341,13 +1315,7 @@ pub const Effects = struct {
1341
1315
 
1342
1316
  if (!volatiles.Bide) return;
1343
1317
 
1344
- // if (options.calc.modify(player, .attacking)) |extend| {
1345
- // if (!extend) volatiles.attacks = 0;
1346
- // } else {
1347
1318
  volatiles.attacks -= 1;
1348
- // }
1349
- try options.chance.attacking(player, volatiles.attacks);
1350
-
1351
1319
  if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
1352
1320
 
1353
1321
  volatiles.Bide = false;
@@ -2876,7 +2844,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
2876
2844
 
2877
2845
  pub const Rolls = struct {
2878
2846
  fn speedTie(battle: anytype, options: anytype) !bool {
2879
- const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
2847
+ const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
2880
2848
  player == .P1
2881
2849
  else if (showdown)
2882
2850
  battle.rng.range(u8, 0, 2) == 0
@@ -2888,7 +2856,7 @@ pub const Rolls = struct {
2888
2856
  }
2889
2857
 
2890
2858
  fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
2891
- const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
2859
+ const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
2892
2860
  val == .true
2893
2861
  else if (showdown)
2894
2862
  battle.rng.chance(u8, 60, 256)
@@ -2900,7 +2868,7 @@ pub const Rolls = struct {
2900
2868
  }
2901
2869
 
2902
2870
  fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
2903
- const ok = if (options.calc.overridden(player, .hit)) |val|
2871
+ const ok = if (options.calc.overridden(player, 0, .hit)) |val|
2904
2872
  val == .true
2905
2873
  else if (showdown)
2906
2874
  battle.rng.chance(u8, accuracy, 256)
@@ -2911,20 +2879,22 @@ pub const Rolls = struct {
2911
2879
  return ok;
2912
2880
  }
2913
2881
 
2914
- fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2915
- 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|
2916
2885
  val == .true
2917
2886
  else if (showdown)
2918
2887
  battle.rng.chance(u8, rate, 256)
2919
2888
  else
2920
2889
  battle.rng.next() < rate;
2921
2890
 
2922
- try options.chance.criticalHit(player, crit, rate);
2891
+ try options.chance.criticalHit(player, i, crit, rate);
2923
2892
  return crit;
2924
2893
  }
2925
2894
 
2926
- fn damage(battle: anytype, player: Player, options: anytype) !u8 {
2927
- 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|
2928
2898
  val
2929
2899
  else roll: {
2930
2900
  if (showdown) break :roll battle.rng.range(u8, 217, 256);
@@ -2935,12 +2905,12 @@ pub const Rolls = struct {
2935
2905
  };
2936
2906
 
2937
2907
  assert(roll >= 217 and roll <= 255);
2938
- try options.chance.damage(player, roll);
2908
+ try options.chance.damage(player, i, roll);
2939
2909
  return roll;
2940
2910
  }
2941
2911
 
2942
2912
  fn confused(battle: anytype, player: Player, options: anytype) !bool {
2943
- const cfz = if (options.calc.overridden(player, .confused)) |val|
2913
+ const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
2944
2914
  val == .true
2945
2915
  else if (showdown)
2946
2916
  !battle.rng.chance(u8, 128, 256)
@@ -2952,7 +2922,7 @@ pub const Rolls = struct {
2952
2922
  }
2953
2923
 
2954
2924
  fn attract(battle: anytype, player: Player, options: anytype) !bool {
2955
- const cant = if (options.calc.overridden(player, .attract)) |val|
2925
+ const cant = if (options.calc.overridden(player, 0, .attract)) |val|
2956
2926
  val == .true
2957
2927
  else if (showdown)
2958
2928
  battle.rng.chance(u8, 1, 2)
@@ -2964,7 +2934,7 @@ pub const Rolls = struct {
2964
2934
  }
2965
2935
 
2966
2936
  fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2967
- const par = if (options.calc.overridden(player, .paralyzed)) |val|
2937
+ const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
2968
2938
  val == .true
2969
2939
  else if (showdown)
2970
2940
  battle.rng.chance(u8, 63, 256)
@@ -2976,7 +2946,7 @@ pub const Rolls = struct {
2976
2946
  }
2977
2947
 
2978
2948
  fn defrost(battle: anytype, player: Player, options: anytype) !bool {
2979
- const thaw = if (options.calc.overridden(player, .defrost)) |val|
2949
+ const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
2980
2950
  val == .true
2981
2951
  else if (showdown)
2982
2952
  battle.rng.chance(u8, 25, 256)
@@ -2989,7 +2959,7 @@ pub const Rolls = struct {
2989
2959
 
2990
2960
  fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2991
2961
  // GLITCH: 100% secondary chance will still fail 1/256 of the time
2992
- const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2962
+ const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
2993
2963
  val == .true
2994
2964
  else if (showdown)
2995
2965
  battle.rng.chance(u8, rate, 256)
@@ -3001,7 +2971,7 @@ pub const Rolls = struct {
3001
2971
  }
3002
2972
 
3003
2973
  fn item(battle: anytype, player: Player, options: anytype) !bool {
3004
- const proc = if (options.calc.overridden(player, .item)) |val|
2974
+ const proc = if (options.calc.overridden(player, 0, .item)) |val|
3005
2975
  val == .true
3006
2976
  else if (showdown)
3007
2977
  battle.rng.chance(u8, 30, 256)
@@ -3013,7 +2983,7 @@ pub const Rolls = struct {
3013
2983
  }
3014
2984
 
3015
2985
  fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
3016
- const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
2986
+ const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
3017
2987
  @enumFromInt(@intFromEnum(val) - 1)
3018
2988
  else if (showdown)
3019
2989
  @enumFromInt(battle.rng.range(u2, 0, 2))
@@ -3029,7 +2999,7 @@ pub const Rolls = struct {
3029
2999
  }
3030
3000
 
3031
3001
  fn present(battle: anytype, player: Player, options: anytype) !u8 {
3032
- const power = if (options.calc.overridden(player, .present)) |val|
3002
+ const power = if (options.calc.overridden(player, 0, .present)) |val|
3033
3003
  (val - 1) * 40
3034
3004
  else if (showdown) power: {
3035
3005
  const r = battle.rng.range(u8, 0, 10);
@@ -3051,7 +3021,7 @@ pub const Rolls = struct {
3051
3021
  }
3052
3022
 
3053
3023
  fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
3054
- const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
3024
+ const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
3055
3025
  val + 3
3056
3026
  else if (showdown) num: {
3057
3027
  const r = battle.rng.range(u8, 0, 100);
@@ -3079,7 +3049,7 @@ pub const Rolls = struct {
3079
3049
  }
3080
3050
 
3081
3051
  fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
3082
- const hits = if (options.calc.overridden(player, .triple_kick)) |val|
3052
+ const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
3083
3053
  val
3084
3054
  else if (showdown)
3085
3055
  battle.rng.range(u2, 1, 4)
@@ -3097,7 +3067,7 @@ pub const Rolls = struct {
3097
3067
 
3098
3068
  fn spite(battle: anytype, player: Player, options: anytype) !u3 {
3099
3069
  assert(!showdown);
3100
- const pp: u3 = if (options.calc.overridden(player, .spite)) |val|
3070
+ const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
3101
3071
  val + 1
3102
3072
  else
3103
3073
  @intCast((battle.rng.next() & 3) + 2);
@@ -3114,7 +3084,7 @@ pub const Rolls = struct {
3114
3084
  var i: u8 = 0;
3115
3085
  const neutral = @intFromEnum(Effectiveness.Neutral);
3116
3086
  assert(mtype != .@"???");
3117
- const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
3087
+ const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
3118
3088
  @enumFromInt(@intFromEnum(val) - 1)
3119
3089
  else if (showdown) ty: {
3120
3090
  for (Type.SHOWDOWN) |t| {
@@ -3148,7 +3118,7 @@ pub const Rolls = struct {
3148
3118
  assert(count <= 8);
3149
3119
  const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
3150
3120
 
3151
- const ok = if (options.calc.overridden(player, .protect)) |val|
3121
+ const ok = if (options.calc.overridden(player, 0, .protect)) |val|
3152
3122
  val == .true
3153
3123
  else if (showdown)
3154
3124
  battle.rng.chance(u8, num, 255)
@@ -3170,7 +3140,7 @@ pub const Rolls = struct {
3170
3140
  const moves = active.moves;
3171
3141
 
3172
3142
  // TODO: consider throwing error instead of rerolling?
3173
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3143
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3174
3144
  if (convertible(active, moves[val - 1].id)) val else null
3175
3145
  else
3176
3146
  null;
@@ -3207,7 +3177,7 @@ pub const Rolls = struct {
3207
3177
  const moves = active.moves;
3208
3178
 
3209
3179
  // TODO: consider throwing error instead of rerolling?
3210
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3180
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3211
3181
  if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
3212
3182
  else
3213
3183
  null;
@@ -3251,7 +3221,7 @@ pub const Rolls = struct {
3251
3221
  assert(n > 0 and n <= 6);
3252
3222
 
3253
3223
  // TODO: consider throwing error instead of rerolling?
3254
- const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
3224
+ const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
3255
3225
  for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
3256
3226
  break :val null;
3257
3227
  } else null;
@@ -3277,7 +3247,7 @@ pub const Rolls = struct {
3277
3247
  const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
3278
3248
 
3279
3249
  fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
3280
- const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
3250
+ const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
3281
3251
  @intCast(val)
3282
3252
  else if (showdown)
3283
3253
  DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
@@ -3292,7 +3262,7 @@ pub const Rolls = struct {
3292
3262
  }
3293
3263
 
3294
3264
  fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
3295
- const power = if (options.calc.overridden(player, .psywave)) |val|
3265
+ const power = if (options.calc.overridden(player, 0, .psywave)) |val|
3296
3266
  val
3297
3267
  else if (showdown)
3298
3268
  battle.rng.range(u8, 1, max)
@@ -3318,7 +3288,7 @@ pub const Rolls = struct {
3318
3288
  }
3319
3289
 
3320
3290
  // TODO: consider throwing error instead of rerolling?
3321
- const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
3291
+ const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
3322
3292
  for (moves) |m| if (m.id == val) break :val null;
3323
3293
  break :val val;
3324
3294
  } else null;
@@ -3350,7 +3320,7 @@ pub const Rolls = struct {
3350
3320
  }
3351
3321
 
3352
3322
  fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
3353
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3323
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3354
3324
  @intCast(val)
3355
3325
  else if (showdown)
3356
3326
  battle.rng.range(u3, 1, 7)
@@ -3362,12 +3332,12 @@ pub const Rolls = struct {
3362
3332
  };
3363
3333
 
3364
3334
  assert(duration >= 1 and duration <= 6);
3365
- options.chance.duration(.sleep, player, player.foe(), duration);
3335
+ options.chance.duration(player, duration);
3366
3336
  return duration + 1;
3367
3337
  }
3368
3338
 
3369
3339
  fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
3370
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3340
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3371
3341
  @intCast(val)
3372
3342
  else if (showdown)
3373
3343
  // Pokémon Showdown incorrectly uses the same duration for self-confusion
@@ -3376,16 +3346,16 @@ pub const Rolls = struct {
3376
3346
  @intCast((battle.rng.next() & if (self) 1 else 3) + 2);
3377
3347
 
3378
3348
  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);
3349
+ options.chance.duration(player, duration);
3380
3350
  return duration;
3381
3351
  }
3382
3352
 
3383
3353
  fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
3384
- const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
3354
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3385
3355
  @intCast(val)
3386
3356
  else if (showdown)
3387
3357
  // Pokémon Showdown incorrectly inherits Generation III's Disable duration
3388
- battle.rng.range(u4, 2, 6)
3358
+ battle.rng.range(u3, 2, 6)
3389
3359
  else duration: {
3390
3360
  while (true) {
3391
3361
  const r = battle.rng.next() & 7;
@@ -3394,12 +3364,12 @@ pub const Rolls = struct {
3394
3364
  };
3395
3365
 
3396
3366
  assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
3397
- options.chance.duration(.disable, player, player.foe(), duration);
3367
+ options.chance.duration(player, duration);
3398
3368
  return duration;
3399
3369
  }
3400
3370
 
3401
3371
  fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
3402
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3372
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3403
3373
  @intCast(val)
3404
3374
  else if (showdown)
3405
3375
  battle.rng.range(u3, 2, 4)
@@ -3407,12 +3377,12 @@ pub const Rolls = struct {
3407
3377
  @intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
3408
3378
 
3409
3379
  assert(duration >= 2 and duration <= 3);
3410
- options.chance.duration(.attacking, player, player, duration);
3380
+ options.chance.duration(player, duration);
3411
3381
  return duration;
3412
3382
  }
3413
3383
 
3414
3384
  fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
3415
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3385
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3416
3386
  @intCast(val)
3417
3387
  else if (showdown)
3418
3388
  // TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
@@ -3421,13 +3391,13 @@ pub const Rolls = struct {
3421
3391
  @intCast(battle.rng.next() & 3 + 2));
3422
3392
 
3423
3393
  assert(duration >= 2 and duration <= 5);
3424
- options.chance.duration(.binding, player, player.foe(), duration);
3394
+ options.chance.duration(player, duration);
3425
3395
  return duration + 1;
3426
3396
  }
3427
3397
 
3428
3398
  // TODO: consider sharing implementation with bindingDuration
3429
3399
  fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
3430
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3400
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3431
3401
  @intCast(val)
3432
3402
  else if (showdown)
3433
3403
  battle.rng.range(u3, 2, 6)
@@ -3435,7 +3405,7 @@ pub const Rolls = struct {
3435
3405
  @intCast(battle.rng.next() & 3 + 2));
3436
3406
 
3437
3407
  assert(duration >= 2 and duration <= 5);
3438
- options.chance.duration(.encore, player, player.foe(), duration);
3408
+ options.chance.duration(player, duration);
3439
3409
  return duration + 1;
3440
3410
  }
3441
3411
  };
package/src/lib/pkmn.zig CHANGED
@@ -22,6 +22,11 @@ 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
 
@@ -81,8 +86,6 @@ pub const gen1 = struct {
81
86
  pub const Calc = @import("gen1/calc.zig").Calc;
82
87
  /// Namespace for types associated with supported Generation I Pokémon damage calc features.
83
88
  pub const calc = if (options.internal) @import("gen1/calc.zig") else struct {
84
- /// TODO
85
- pub const Overrides = @import("gen1/calc.zig").Overrides;
86
89
  /// Information relevant to damage calculation that occured during a Generation I
87
90
  /// battle `update`.
88
91
  pub const Summaries = @import("gen1/calc.zig").Summaries;
@@ -111,7 +114,6 @@ pub const gen2 = struct {
111
114
  };
112
115
  pub const Calc = @import("gen2/calc.zig").Calc;
113
116
  pub const calc = if (options.internal) @import("gen2/calc.zig") else struct {
114
- pub const Overrides = @import("gen2/calc.zig").Overrides;
115
117
  pub const Summaries = @import("gen2/calc.zig").Summaries;
116
118
  pub const Summary = @import("gen2/calc.zig").Summary;
117
119
  pub const NULL = @import("gen2/calc.zig").NULL;
@@ -121,8 +123,8 @@ pub const gen2 = struct {
121
123
 
122
124
  // Internal APIs used by other pkmn libraries, not actually part of the public API.
123
125
  // NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
124
- pub const js = if (options.internal) @import("common/js.zig") else struct {};
126
+ pub const js = if (options.internal) @import("common/js.zig") else {};
125
127
  pub const bindings = if (options.internal) struct {
126
128
  pub const node = @import("node.zig");
127
129
  pub const wasm = @import("wasm.zig");
128
- } else struct {};
130
+ } else {};