@pkmn/engine 0.1.0-dev.62399ed5 → 0.1.0-dev.6be9a775

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.
@@ -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));
@@ -27,6 +27,8 @@ const Species = data.Species;
27
27
  const Stats = data.Stats;
28
28
  const Status = data.Status;
29
29
 
30
+ const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
31
+
30
32
  pub const Options = struct {
31
33
  cleric: bool = showdown,
32
34
  };
@@ -151,7 +153,7 @@ pub const Pokemon = struct {
151
153
  var pokemon = data.Pokemon{};
152
154
  pokemon.species = p.species;
153
155
  const species = Species.get(p.species);
154
- inline for (@typeInfo(@TypeOf(pokemon.stats)).Struct.fields) |field| {
156
+ inline for (@field(@typeInfo(@TypeOf(pokemon.stats)), @tagName(Struct)).fields) |field| {
155
157
  const hp = comptime std.mem.eql(u8, field.name, "hp");
156
158
  const spc =
157
159
  comptime std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
@@ -192,7 +194,7 @@ pub const Pokemon = struct {
192
194
 
193
195
  var stats: Stats(u16) = .{};
194
196
  const dvs = gen1.DVs.random(rand);
195
- inline for (@typeInfo(@TypeOf(stats)).Struct.fields) |field| {
197
+ inline for (@field(@typeInfo(@TypeOf(stats)), @tagName(Struct)).fields) |field| {
196
198
  const hp = std.mem.eql(u8, field.name, "hp");
197
199
  const spc = std.mem.eql(u8, field.name, "spa") or std.mem.eql(u8, field.name, "spd");
198
200
  @field(stats, field.name) = Stats(u16).calc(
@@ -106,6 +106,7 @@ pub const State = struct {
106
106
  type: Type, // BATTLE_VARS_MOVE_TYPE
107
107
  accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
108
108
  move: Move, // wCurPlayerMove
109
+ hit: u3 = 0,
109
110
  from: Move = .None,
110
111
  mslot: u4, // wCurMoveNum
111
112
  first: bool, // wEnemyGoesFirst
@@ -256,7 +257,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
256
257
  } else {
257
258
  try options.log.switched(.{ battle.active(player), incoming });
258
259
  }
259
- options.chance.switched(player, side.pokemon[0].position, out);
260
260
 
261
261
  if (side.conditions.Spikes and !active.types.includes(.Flying)) {
262
262
  incoming.hp -|= @max(incoming.stats.hp / 8, 1);
@@ -442,18 +442,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
442
442
  if (Status.is(stored.status, .SLP)) slp: {
443
443
  const before = stored.status;
444
444
  // Even if the EXT bit is set this will still correctly modify the sleep duration
445
- // if (options.calc.modify(player, .sleep)) |extend| {
446
- // if (!extend) stored.status = 0;
447
- // } else {
448
445
  stored.status -= 1;
449
- // }
450
-
451
- const duration = Status.duration(stored.status);
452
- if (!Status.is(stored.status, .EXT)) {
453
- try options.chance.sleep(player, duration);
454
- }
455
446
 
456
- if (duration == 0) {
447
+ if (Status.duration(stored.status) == 0) {
457
448
  try log.curestatus(.{ ident, before, .Message });
458
449
  stored.status = 0; // clears EXT if present
459
450
  resetCant(volatiles);
@@ -483,12 +474,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
483
474
  }
484
475
 
485
476
  if (volatiles.disable.duration > 0) {
486
- // if (options.calc.modify(player, .disable)) |extend| {
487
- // if (!extend) volatiles.disable.duration = 0;
488
- // } else {
489
477
  volatiles.disable.duration -= 1;
490
- // }
491
- try options.chance.disable(player, volatiles.disable.duration);
492
478
 
493
479
  if (volatiles.disable.duration == 0) {
494
480
  volatiles.disable.move = 0;
@@ -498,13 +484,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
498
484
 
499
485
  if (volatiles.Confusion) {
500
486
  assert(volatiles.confusion > 0);
501
-
502
- // if (options.calc.modify(player, .confusion)) |extend| {
503
- // if (!extend) volatiles.confusion = 0;
504
- // } else {
505
487
  volatiles.confusion -= 1;
506
- // }
507
- try options.chance.confusion(player, volatiles.confusion);
508
488
 
509
489
  if (volatiles.confusion == 0) {
510
490
  volatiles.Confusion = false;
@@ -665,7 +645,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
665
645
  }
666
646
  assert(stage <= 4);
667
647
 
668
- state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
648
+ state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
669
649
  }
670
650
 
671
651
  pub fn calcDamage(
@@ -733,7 +713,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
733
713
 
734
714
  pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
735
715
  if (state.damage <= 1) return;
736
- const random = try Rolls.damage(battle, player, options);
716
+ const random = try Rolls.damage(battle, player, state.hit, options);
737
717
  options.calc.base(player.foe(), state.damage);
738
718
  state.damage = @intCast(@as(u32, state.damage) *% random / 255);
739
719
  options.calc.final(player.foe(), state.damage);
@@ -1341,13 +1321,7 @@ pub const Effects = struct {
1341
1321
 
1342
1322
  if (!volatiles.Bide) return;
1343
1323
 
1344
- // if (options.calc.modify(player, .attacking)) |extend| {
1345
- // if (!extend) volatiles.attacks = 0;
1346
- // } else {
1347
1324
  volatiles.attacks -= 1;
1348
- // }
1349
- try options.chance.attacking(player, volatiles.attacks);
1350
-
1351
1325
  if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
1352
1326
 
1353
1327
  volatiles.Bide = false;
@@ -2876,7 +2850,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
2876
2850
 
2877
2851
  pub const Rolls = struct {
2878
2852
  fn speedTie(battle: anytype, options: anytype) !bool {
2879
- const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
2853
+ const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
2880
2854
  player == .P1
2881
2855
  else if (showdown)
2882
2856
  battle.rng.range(u8, 0, 2) == 0
@@ -2888,7 +2862,7 @@ pub const Rolls = struct {
2888
2862
  }
2889
2863
 
2890
2864
  fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
2891
- const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
2865
+ const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
2892
2866
  val == .true
2893
2867
  else if (showdown)
2894
2868
  battle.rng.chance(u8, 60, 256)
@@ -2900,7 +2874,7 @@ pub const Rolls = struct {
2900
2874
  }
2901
2875
 
2902
2876
  fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
2903
- const ok = if (options.calc.overridden(player, .hit)) |val|
2877
+ const ok = if (options.calc.overridden(player, 0, .hit)) |val|
2904
2878
  val == .true
2905
2879
  else if (showdown)
2906
2880
  battle.rng.chance(u8, accuracy, 256)
@@ -2911,20 +2885,22 @@ pub const Rolls = struct {
2911
2885
  return ok;
2912
2886
  }
2913
2887
 
2914
- fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2915
- const crit = if (options.calc.overridden(player, .critical_hit)) |val|
2888
+ fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
2889
+ _ = .{ player, options };
2890
+ const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
2916
2891
  val == .true
2917
2892
  else if (showdown)
2918
2893
  battle.rng.chance(u8, rate, 256)
2919
2894
  else
2920
2895
  battle.rng.next() < rate;
2921
2896
 
2922
- try options.chance.criticalHit(player, crit, rate);
2897
+ try options.chance.criticalHit(player, i, crit, rate);
2923
2898
  return crit;
2924
2899
  }
2925
2900
 
2926
- fn damage(battle: anytype, player: Player, options: anytype) !u8 {
2927
- const roll = if (options.calc.overridden(player, .damage)) |val|
2901
+ fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
2902
+ _ = .{ player, options };
2903
+ const roll = if (options.calc.overridden(player, i, .damages)) |val|
2928
2904
  val
2929
2905
  else roll: {
2930
2906
  if (showdown) break :roll battle.rng.range(u8, 217, 256);
@@ -2935,12 +2911,12 @@ pub const Rolls = struct {
2935
2911
  };
2936
2912
 
2937
2913
  assert(roll >= 217 and roll <= 255);
2938
- try options.chance.damage(player, roll);
2914
+ try options.chance.damage(player, i, roll);
2939
2915
  return roll;
2940
2916
  }
2941
2917
 
2942
2918
  fn confused(battle: anytype, player: Player, options: anytype) !bool {
2943
- const cfz = if (options.calc.overridden(player, .confused)) |val|
2919
+ const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
2944
2920
  val == .true
2945
2921
  else if (showdown)
2946
2922
  !battle.rng.chance(u8, 128, 256)
@@ -2952,7 +2928,7 @@ pub const Rolls = struct {
2952
2928
  }
2953
2929
 
2954
2930
  fn attract(battle: anytype, player: Player, options: anytype) !bool {
2955
- const cant = if (options.calc.overridden(player, .attract)) |val|
2931
+ const cant = if (options.calc.overridden(player, 0, .attract)) |val|
2956
2932
  val == .true
2957
2933
  else if (showdown)
2958
2934
  battle.rng.chance(u8, 1, 2)
@@ -2964,7 +2940,7 @@ pub const Rolls = struct {
2964
2940
  }
2965
2941
 
2966
2942
  fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2967
- const par = if (options.calc.overridden(player, .paralyzed)) |val|
2943
+ const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
2968
2944
  val == .true
2969
2945
  else if (showdown)
2970
2946
  battle.rng.chance(u8, 63, 256)
@@ -2976,7 +2952,7 @@ pub const Rolls = struct {
2976
2952
  }
2977
2953
 
2978
2954
  fn defrost(battle: anytype, player: Player, options: anytype) !bool {
2979
- const thaw = if (options.calc.overridden(player, .defrost)) |val|
2955
+ const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
2980
2956
  val == .true
2981
2957
  else if (showdown)
2982
2958
  battle.rng.chance(u8, 25, 256)
@@ -2989,7 +2965,7 @@ pub const Rolls = struct {
2989
2965
 
2990
2966
  fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2991
2967
  // GLITCH: 100% secondary chance will still fail 1/256 of the time
2992
- const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2968
+ const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
2993
2969
  val == .true
2994
2970
  else if (showdown)
2995
2971
  battle.rng.chance(u8, rate, 256)
@@ -3001,7 +2977,7 @@ pub const Rolls = struct {
3001
2977
  }
3002
2978
 
3003
2979
  fn item(battle: anytype, player: Player, options: anytype) !bool {
3004
- const proc = if (options.calc.overridden(player, .item)) |val|
2980
+ const proc = if (options.calc.overridden(player, 0, .item)) |val|
3005
2981
  val == .true
3006
2982
  else if (showdown)
3007
2983
  battle.rng.chance(u8, 30, 256)
@@ -3013,7 +2989,7 @@ pub const Rolls = struct {
3013
2989
  }
3014
2990
 
3015
2991
  fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
3016
- const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
2992
+ const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
3017
2993
  @enumFromInt(@intFromEnum(val) - 1)
3018
2994
  else if (showdown)
3019
2995
  @enumFromInt(battle.rng.range(u2, 0, 2))
@@ -3029,7 +3005,7 @@ pub const Rolls = struct {
3029
3005
  }
3030
3006
 
3031
3007
  fn present(battle: anytype, player: Player, options: anytype) !u8 {
3032
- const power = if (options.calc.overridden(player, .present)) |val|
3008
+ const power = if (options.calc.overridden(player, 0, .present)) |val|
3033
3009
  (val - 1) * 40
3034
3010
  else if (showdown) power: {
3035
3011
  const r = battle.rng.range(u8, 0, 10);
@@ -3051,7 +3027,7 @@ pub const Rolls = struct {
3051
3027
  }
3052
3028
 
3053
3029
  fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
3054
- const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
3030
+ const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
3055
3031
  val + 3
3056
3032
  else if (showdown) num: {
3057
3033
  const r = battle.rng.range(u8, 0, 100);
@@ -3079,7 +3055,7 @@ pub const Rolls = struct {
3079
3055
  }
3080
3056
 
3081
3057
  fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
3082
- const hits = if (options.calc.overridden(player, .triple_kick)) |val|
3058
+ const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
3083
3059
  val
3084
3060
  else if (showdown)
3085
3061
  battle.rng.range(u2, 1, 4)
@@ -3097,7 +3073,7 @@ pub const Rolls = struct {
3097
3073
 
3098
3074
  fn spite(battle: anytype, player: Player, options: anytype) !u3 {
3099
3075
  assert(!showdown);
3100
- const pp: u3 = if (options.calc.overridden(player, .spite)) |val|
3076
+ const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
3101
3077
  val + 1
3102
3078
  else
3103
3079
  @intCast((battle.rng.next() & 3) + 2);
@@ -3114,7 +3090,7 @@ pub const Rolls = struct {
3114
3090
  var i: u8 = 0;
3115
3091
  const neutral = @intFromEnum(Effectiveness.Neutral);
3116
3092
  assert(mtype != .@"???");
3117
- const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
3093
+ const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
3118
3094
  @enumFromInt(@intFromEnum(val) - 1)
3119
3095
  else if (showdown) ty: {
3120
3096
  for (Type.SHOWDOWN) |t| {
@@ -3148,7 +3124,7 @@ pub const Rolls = struct {
3148
3124
  assert(count <= 8);
3149
3125
  const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
3150
3126
 
3151
- const ok = if (options.calc.overridden(player, .protect)) |val|
3127
+ const ok = if (options.calc.overridden(player, 0, .protect)) |val|
3152
3128
  val == .true
3153
3129
  else if (showdown)
3154
3130
  battle.rng.chance(u8, num, 255)
@@ -3170,7 +3146,7 @@ pub const Rolls = struct {
3170
3146
  const moves = active.moves;
3171
3147
 
3172
3148
  // TODO: consider throwing error instead of rerolling?
3173
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3149
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3174
3150
  if (convertible(active, moves[val - 1].id)) val else null
3175
3151
  else
3176
3152
  null;
@@ -3207,7 +3183,7 @@ pub const Rolls = struct {
3207
3183
  const moves = active.moves;
3208
3184
 
3209
3185
  // TODO: consider throwing error instead of rerolling?
3210
- const overridden = if (options.calc.overridden(player, .move_slot)) |val|
3186
+ const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
3211
3187
  if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
3212
3188
  else
3213
3189
  null;
@@ -3251,7 +3227,7 @@ pub const Rolls = struct {
3251
3227
  assert(n > 0 and n <= 6);
3252
3228
 
3253
3229
  // TODO: consider throwing error instead of rerolling?
3254
- const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
3230
+ const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
3255
3231
  for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
3256
3232
  break :val null;
3257
3233
  } else null;
@@ -3277,7 +3253,7 @@ pub const Rolls = struct {
3277
3253
  const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
3278
3254
 
3279
3255
  fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
3280
- const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
3256
+ const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
3281
3257
  @intCast(val)
3282
3258
  else if (showdown)
3283
3259
  DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
@@ -3292,7 +3268,7 @@ pub const Rolls = struct {
3292
3268
  }
3293
3269
 
3294
3270
  fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
3295
- const power = if (options.calc.overridden(player, .psywave)) |val|
3271
+ const power = if (options.calc.overridden(player, 0, .psywave)) |val|
3296
3272
  val
3297
3273
  else if (showdown)
3298
3274
  battle.rng.range(u8, 1, max)
@@ -3318,7 +3294,7 @@ pub const Rolls = struct {
3318
3294
  }
3319
3295
 
3320
3296
  // TODO: consider throwing error instead of rerolling?
3321
- const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
3297
+ const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
3322
3298
  for (moves) |m| if (m.id == val) break :val null;
3323
3299
  break :val val;
3324
3300
  } else null;
@@ -3350,7 +3326,7 @@ pub const Rolls = struct {
3350
3326
  }
3351
3327
 
3352
3328
  fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
3353
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3329
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3354
3330
  @intCast(val)
3355
3331
  else if (showdown)
3356
3332
  battle.rng.range(u3, 1, 7)
@@ -3362,12 +3338,12 @@ pub const Rolls = struct {
3362
3338
  };
3363
3339
 
3364
3340
  assert(duration >= 1 and duration <= 6);
3365
- options.chance.duration(.sleep, player, player.foe(), duration);
3341
+ options.chance.duration(player, duration);
3366
3342
  return duration + 1;
3367
3343
  }
3368
3344
 
3369
3345
  fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
3370
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3346
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3371
3347
  @intCast(val)
3372
3348
  else if (showdown)
3373
3349
  // Pokémon Showdown incorrectly uses the same duration for self-confusion
@@ -3376,16 +3352,16 @@ pub const Rolls = struct {
3376
3352
  @intCast((battle.rng.next() & if (self) 1 else 3) + 2);
3377
3353
 
3378
3354
  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);
3355
+ options.chance.duration(player, duration);
3380
3356
  return duration;
3381
3357
  }
3382
3358
 
3383
3359
  fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
3384
- const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
3360
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3385
3361
  @intCast(val)
3386
3362
  else if (showdown)
3387
3363
  // Pokémon Showdown incorrectly inherits Generation III's Disable duration
3388
- battle.rng.range(u4, 2, 6)
3364
+ battle.rng.range(u3, 2, 6)
3389
3365
  else duration: {
3390
3366
  while (true) {
3391
3367
  const r = battle.rng.next() & 7;
@@ -3394,12 +3370,12 @@ pub const Rolls = struct {
3394
3370
  };
3395
3371
 
3396
3372
  assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
3397
- options.chance.duration(.disable, player, player.foe(), duration);
3373
+ options.chance.duration(player, duration);
3398
3374
  return duration;
3399
3375
  }
3400
3376
 
3401
3377
  fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
3402
- const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
3378
+ const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
3403
3379
  @intCast(val)
3404
3380
  else if (showdown)
3405
3381
  battle.rng.range(u3, 2, 4)
@@ -3407,12 +3383,12 @@ pub const Rolls = struct {
3407
3383
  @intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
3408
3384
 
3409
3385
  assert(duration >= 2 and duration <= 3);
3410
- options.chance.duration(.attacking, player, player, duration);
3386
+ options.chance.duration(player, duration);
3411
3387
  return duration;
3412
3388
  }
3413
3389
 
3414
3390
  fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
3415
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3391
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3416
3392
  @intCast(val)
3417
3393
  else if (showdown)
3418
3394
  // TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
@@ -3421,13 +3397,13 @@ pub const Rolls = struct {
3421
3397
  @intCast(battle.rng.next() & 3 + 2));
3422
3398
 
3423
3399
  assert(duration >= 2 and duration <= 5);
3424
- options.chance.duration(.binding, player, player.foe(), duration);
3400
+ options.chance.duration(player, duration);
3425
3401
  return duration + 1;
3426
3402
  }
3427
3403
 
3428
3404
  // TODO: consider sharing implementation with bindingDuration
3429
3405
  fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
3430
- const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
3406
+ const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
3431
3407
  @intCast(val)
3432
3408
  else if (showdown)
3433
3409
  battle.rng.range(u3, 2, 6)
@@ -3435,7 +3411,7 @@ pub const Rolls = struct {
3435
3411
  @intCast(battle.rng.next() & 3 + 2));
3436
3412
 
3437
3413
  assert(duration >= 2 and duration <= 5);
3438
- options.chance.duration(.encore, player, player.foe(), duration);
3414
+ options.chance.duration(player, duration);
3439
3415
  return duration + 1;
3440
3416
  }
3441
3417
  };
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 {};