@pkmn/engine 0.1.0-dev.44b2e6ec → 0.1.0-dev.46850a3d

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.
@@ -14,7 +14,7 @@ const util = @import("../common/util.zig");
14
14
  const Player = @import("../common/data.zig").Player;
15
15
  const Optional = @import("../common/optional.zig").Optional;
16
16
 
17
- const data = @import("./data.zig");
17
+ const data = @import("data.zig");
18
18
 
19
19
  const enabled = options.chance;
20
20
  const showdown = options.showdown;
@@ -92,13 +92,14 @@ pub const Actions = extern struct {
92
92
  }
93
93
  };
94
94
 
95
+ // TODO: ziglang/zig#19730
95
96
  test Actions {
96
97
  const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
97
98
  const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
98
99
  const c: Actions = .{ .p1 = .{ .hit = .true } };
99
- const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
100
- const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
101
- const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
100
+ // const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
101
+ // const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
102
+ // const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
102
103
 
103
104
  try expect(a.eql(a));
104
105
  try expect(!a.eql(b));
@@ -111,8 +112,8 @@ test Actions {
111
112
  try expect(b.matches(a));
112
113
  try expect(!a.matches(c));
113
114
  try expect(!c.matches(a));
114
- try expect(d.matches(e));
115
- try expect(!d.matches(f));
115
+ // try expect(d.matches(e));
116
+ // try expect(!d.matches(f));
116
117
  }
117
118
 
118
119
  /// Information about the RNG that was observed during a Generation I battle `update` for a
@@ -706,19 +707,19 @@ test "Chance.damage" {
706
707
 
707
708
  try chance.damage(.P1, 217);
708
709
  if (showdown) {
709
- try expectValue(@as(u8, 217), chance.actions.p1.damage);
710
+ try expectValue(217, chance.actions.p1.damage);
710
711
  try expectProbability(&chance.probability, 1, 39);
711
712
  } else {
712
713
  try expectProbability(&chance.probability, 1, 1);
713
- try expectValue(@as(u8, 0), chance.actions.p1.damage);
714
+ try expectValue(0, chance.actions.p1.damage);
714
715
 
715
716
  try chance.commit(.P1, .miss);
716
717
  try expectProbability(&chance.probability, 1, 1);
717
- try expectValue(@as(u8, 0), chance.actions.p1.damage);
718
+ try expectValue(0, chance.actions.p1.damage);
718
719
 
719
720
  try chance.commit(.P1, .hit);
720
721
  try expectProbability(&chance.probability, 1, 39);
721
- try expectValue(@as(u8, 217), chance.actions.p1.damage);
722
+ try expectValue(217, chance.actions.p1.damage);
722
723
  }
723
724
  }
724
725
 
@@ -770,13 +771,13 @@ test "Chance.moveSlot" {
770
771
 
771
772
  try chance.moveSlot(.P2, 2, &ms, 2);
772
773
  try expectProbability(&chance.probability, 1, 2);
773
- try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
774
+ try expectValue(2, chance.actions.p2.move_slot);
774
775
 
775
776
  chance.reset();
776
777
 
777
778
  try chance.moveSlot(.P1, 1, &ms, 0);
778
779
  try expectProbability(&chance.probability, 1, 3);
779
- try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
780
+ try expectValue(1, chance.actions.p1.move_slot);
780
781
  }
781
782
 
782
783
  test "Chance.multiHit" {
@@ -784,32 +785,32 @@ test "Chance.multiHit" {
784
785
 
785
786
  try chance.multiHit(.P1, 3);
786
787
  try expectProbability(&chance.probability, 3, 8);
787
- try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
788
+ try expectValue(3, chance.actions.p1.multi_hit);
788
789
 
789
790
  chance.reset();
790
791
 
791
792
  try chance.multiHit(.P2, 5);
792
793
  try expectProbability(&chance.probability, 1, 8);
793
- try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
794
+ try expectValue(5, chance.actions.p2.multi_hit);
794
795
  }
795
796
 
796
797
  test "Chance.duration" {
797
798
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
798
799
 
799
800
  chance.duration(.sleep, .P1, .P2, 2);
800
- try expectValue(@as(u4, 2), chance.actions.p1.duration);
801
- try expectValue(@as(u3, 1), chance.actions.p2.durations.sleep);
801
+ try expectValue(2, chance.actions.p1.duration);
802
+ try expectValue(1, chance.actions.p2.durations.sleep);
802
803
 
803
804
  chance.reset();
804
805
 
805
806
  chance.duration(.binding, .P2, .P2, 4);
806
807
  if (!showdown) {
807
- try expectValue(@as(u4, 0), chance.actions.p2.duration);
808
- try expectValue(@as(u3, 0), chance.actions.p2.durations.binding);
808
+ try expectValue(0, chance.actions.p2.duration);
809
+ try expectValue(0, chance.actions.p2.durations.binding);
809
810
  try chance.commit(.P2, .hit);
810
811
  }
811
- try expectValue(@as(u4, 4), chance.actions.p2.duration);
812
- try expectValue(@as(u3, 1), chance.actions.p2.durations.binding);
812
+ try expectValue(4, chance.actions.p2.duration);
813
+ try expectValue(1, chance.actions.p2.durations.binding);
813
814
  }
814
815
 
815
816
  test "Chance.sleep" {
@@ -819,7 +820,7 @@ test "Chance.sleep" {
819
820
  chance.actions.p1.durations.sleep = @intCast(i);
820
821
  try chance.sleep(.P1, 0);
821
822
  try expectProbability(&chance.probability, 1, d);
822
- try expectValue(@as(u3, 0), chance.actions.p1.durations.sleep);
823
+ try expectValue(0, chance.actions.p1.durations.sleep);
823
824
 
824
825
  chance.reset();
825
826
 
@@ -842,7 +843,7 @@ test "Chance.confusion" {
842
843
  chance.actions.p2.durations.confusion = @intCast(i);
843
844
  try chance.confusion(.P2, 0);
844
845
  try expectProbability(&chance.probability, 1, d);
845
- try expectValue(@as(u3, 0), chance.actions.p2.durations.confusion);
846
+ try expectValue(0, chance.actions.p2.durations.confusion);
846
847
 
847
848
  chance.reset();
848
849
  }
@@ -865,7 +866,7 @@ test "Chance.disable" {
865
866
  chance.actions.p1.durations.disable = @intCast(i);
866
867
  try chance.disable(.P1, 0);
867
868
  try expectProbability(&chance.probability, 1, d);
868
- try expectValue(@as(u4, 0), chance.actions.p1.durations.disable);
869
+ try expectValue(0, chance.actions.p1.durations.disable);
869
870
 
870
871
  chance.reset();
871
872
 
@@ -888,7 +889,7 @@ test "Chance.attacking" {
888
889
  chance.actions.p2.durations.attacking = @intCast(i);
889
890
  try chance.attacking(.P2, 0);
890
891
  try expectProbability(&chance.probability, 1, d);
891
- try expectValue(@as(u3, 0), chance.actions.p2.durations.attacking);
892
+ try expectValue(0, chance.actions.p2.durations.attacking);
892
893
 
893
894
  chance.reset();
894
895
  }
@@ -914,7 +915,7 @@ test "Chance.binding" {
914
915
  chance.actions.p1.durations.binding = @intCast(i);
915
916
  try chance.binding(.P1, 0);
916
917
  try expectProbability(&chance.probability, p, q);
917
- try expectValue(@as(u3, 0), chance.actions.p1.durations.binding);
918
+ try expectValue(0, chance.actions.p1.durations.binding);
918
919
 
919
920
  chance.reset();
920
921
 
@@ -934,7 +935,7 @@ test "Chance.psywave" {
934
935
 
935
936
  try chance.psywave(.P2, 100, 150);
936
937
  try expectProbability(&chance.probability, 1, 150);
937
- try expectValue(@as(u8, 101), chance.actions.p2.psywave);
938
+ try expectValue(101, chance.actions.p2.psywave);
938
939
  }
939
940
 
940
941
  test "Chance.metronome" {
@@ -1091,7 +1091,7 @@ pub const Species = enum(u8) {
1091
1091
  /// The number of Pokémon species in this generation.
1092
1092
  pub const size = 151;
1093
1093
 
1094
- /// The Pokémon's critical hit rate=io out of 256.
1094
+ /// The Pokémon's critical hit ratio out of 256.
1095
1095
  pub fn chance(id: Species) u8 {
1096
1096
  assert(id != .None);
1097
1097
  return CHANCES[@intFromEnum(id) - 1];
@@ -112,7 +112,7 @@ pub fn Battle(comptime RNG: anytype) type {
112
112
  /// make (i.e. on the cartridge a soft-lock occurs).
113
113
  ///
114
114
  /// This function will always return choices > 0 if `pkmn.options.showdown` is true.
115
- pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
115
+ pub fn choices(self: *const Self, player: Player, request: Choice.Type, out: []Choice) u8 {
116
116
  return mechanics.choices(self, player, request, out);
117
117
  }
118
118
  };
@@ -316,12 +316,12 @@ test Status {
316
316
  try expect(Status.is(Status.slp(5), .SLP));
317
317
  try expect(!Status.is(Status.slp(5), .EXT));
318
318
  try expect(!Status.is(Status.slp(7), .PSN));
319
- try expectEqual(@as(u3, 5), Status.duration(Status.slp(5)));
319
+ try expectEqual(5, Status.duration(Status.slp(5)));
320
320
  try expect(Status.is(Status.slf(2), .SLP));
321
321
  try expect(Status.is(Status.slf(2), .EXT));
322
322
  try expect(Status.is(Status.TOX, .PSN));
323
323
  try expect(Status.is(Status.TOX, .EXT));
324
- try expectEqual(@as(u3, 1), Status.duration(Status.slp(1)));
324
+ try expectEqual(1, Status.duration(Status.slp(1)));
325
325
  }
326
326
 
327
327
  /// Bitfield representation of volatile statuses and associated data in Generation I.
@@ -405,13 +405,13 @@ test Volatiles {
405
405
  try expect(!volatiles.Transform);
406
406
  try expect(!volatiles.MultiHit);
407
407
 
408
- try expectEqual(@as(u16, 235), volatiles.state);
409
- try expectEqual(@as(u8, 42), volatiles.substitute);
410
- try expectEqual(@as(u4, 2), volatiles.disable_move);
411
- try expectEqual(@as(u4, 4), volatiles.disable_duration);
412
- try expectEqual(@as(u4, 2), volatiles.confusion);
413
- try expectEqual(@as(u5, 4), volatiles.toxic);
414
- try expectEqual(@as(u4, 3), volatiles.attacks);
408
+ try expectEqual(235, volatiles.state);
409
+ try expectEqual(42, volatiles.substitute);
410
+ try expectEqual(2, volatiles.disable_move);
411
+ try expectEqual(4, volatiles.disable_duration);
412
+ try expectEqual(2, volatiles.confusion);
413
+ try expectEqual(4, volatiles.toxic);
414
+ try expectEqual(3, volatiles.attacks);
415
415
  }
416
416
 
417
417
  /// Representaiton of a Pokémon's stats in Generation I.
@@ -442,9 +442,9 @@ test Stats {
442
442
  try expectEqual(0, stats.def);
443
443
 
444
444
  var base = Species.get(.Gyarados).stats;
445
- try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
445
+ try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
446
446
  base = Species.get(.Pidgeot).stats;
447
- try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
447
+ try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
448
448
  }
449
449
 
450
450
  /// Representation of a Pokémon's boosts in Generation I.
@@ -537,7 +537,7 @@ pub const Species = species.Species;
537
537
 
538
538
  test Species {
539
539
  try expectEqual(2, @intFromEnum(Species.Ivysaur));
540
- try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
540
+ try expectEqual(100, Species.get(.Mew).stats.def);
541
541
  }
542
542
 
543
543
  /// Representation of a Generation I type in Pokémon.
@@ -576,8 +576,8 @@ test Types {
576
576
  try expectEqual(Effectiveness.Resisted, Type.effectiveness(.Fire, .Water));
577
577
  try expectEqual(Effectiveness.Neutral, Type.effectiveness(.Normal, .Grass));
578
578
 
579
- try expectEqual(@as(u8, 3), Type.precedence(.Water, .Water));
580
- try expectEqual(@as(u8, 26), Type.precedence(.Bug, .Ghost));
579
+ try expectEqual(3, Type.precedence(.Water, .Water));
580
+ try expectEqual(26, Type.precedence(.Bug, .Ghost));
581
581
  try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
582
582
 
583
583
  const t: Types = .{ .type1 = .Rock, .type2 = .Ground };
@@ -617,18 +617,18 @@ pub const DVs = struct {
617
617
 
618
618
  test DVs {
619
619
  var dvs: DVs = .{ .spc = 15, .spe = 15 };
620
- try expectEqual(@as(u4, 15), dvs.hp());
620
+ try expectEqual(15, dvs.hp());
621
621
  dvs = .{
622
622
  .atk = 5,
623
623
  .def = 15,
624
624
  .spe = 13,
625
625
  .spc = 13,
626
626
  };
627
- try expectEqual(@as(u4, 15), dvs.hp());
627
+ try expectEqual(15, dvs.hp());
628
628
  dvs = .{
629
629
  .def = 3,
630
630
  .spe = 10,
631
631
  .spc = 11,
632
632
  };
633
- try expectEqual(@as(u4, 13), dvs.hp());
633
+ try expectEqual(13, dvs.hp());
634
634
  }
@@ -478,12 +478,12 @@ test "Rolls.damage" {
478
478
  test "Rolls.coalesce" {
479
479
  var summaries =
480
480
  calc.Summaries{ .p1 = .{ .damage = .{ .base = 74, .final = 69, .capped = true } } };
481
- try expectEqual(@as(u8, 0), try Rolls.coalesce(.P2, 0, &summaries, false));
482
- try expectEqual(@as(u8, 241), try Rolls.coalesce(.P2, 238, &summaries, false));
483
- try expectEqual(@as(u8, 255), try Rolls.coalesce(.P2, 238, &summaries, true));
481
+ try expectEqual(0, try Rolls.coalesce(.P2, 0, &summaries, false));
482
+ try expectEqual(241, try Rolls.coalesce(.P2, 238, &summaries, false));
483
+ try expectEqual(255, try Rolls.coalesce(.P2, 238, &summaries, true));
484
484
  summaries.p1.damage.final = 74;
485
- try expectEqual(@as(u8, 217), try Rolls.coalesce(.P2, 217, &summaries, false));
486
- try expectEqual(@as(u8, 255), try Rolls.coalesce(.P2, 217, &summaries, true));
485
+ try expectEqual(217, try Rolls.coalesce(.P2, 217, &summaries, false));
486
+ try expectEqual(255, try Rolls.coalesce(.P2, 217, &summaries, true));
487
487
  }
488
488
 
489
489
  test "Rolls.hit" {
@@ -771,9 +771,9 @@ test "Rolls.psywave" {
771
771
 
772
772
  try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p1, &side, .None));
773
773
  var rolls = Rolls.psywave(actions.p2, &side, .None);
774
- try expectEqual(@as(u8, 150), rolls[rolls.len - 1]);
774
+ try expectEqual(150, rolls[rolls.len - 1]);
775
775
  side.stored().level = 81;
776
776
  rolls = Rolls.psywave(actions.p2, &side, .None);
777
- try expectEqual(@as(u8, 121), rolls[rolls.len - 1]);
777
+ try expectEqual(121, rolls[rolls.len - 1]);
778
778
  try expectEqualSlices(u8, &.{0}, Rolls.psywave(actions.p2, &side, .false));
779
779
  }
@@ -1053,7 +1053,7 @@ fn checkCriticalHit(battle: anytype, player: Player, move: Move.Data, options: a
1053
1053
  const side = battle.side(player);
1054
1054
 
1055
1055
  // Base speed is used for the critical hit calculation, even when Transform-ed
1056
- var rate = @as(u16, Species.chance(side.stored().species));
1056
+ var rate: u16 = Species.chance(side.stored().species);
1057
1057
  // GLITCH: Focus Energy reduces critical hit rate instead of increasing it
1058
1058
  rate = if (side.active.volatiles.FocusEnergy) rate / 2 else @min(rate * 2, 255);
1059
1059
  rate = if (move.effect == .HighCritical) @min(rate * 4, 255) else rate / 2;
@@ -1110,9 +1110,9 @@ fn calcDamage(
1110
1110
  def = @max((def / 4) & 255, if (showdown) 1 else 0);
1111
1111
  }
1112
1112
 
1113
- const lvl = @as(u32, side.stored().level * @as(u2, if (crit) 2 else 1));
1113
+ const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
1114
1114
 
1115
- def = @as(u32, if (move.effect == .Explode) @max(def / 2, 1) else def);
1115
+ def = if (move.effect == .Explode) @max(def / 2, 1) else def;
1116
1116
 
1117
1117
  if (def == 0) return false;
1118
1118
 
@@ -1395,7 +1395,7 @@ fn moveHit(
1395
1395
  const overwritten = overwrite and state > 0;
1396
1396
  assert(!overwritten or (0 < state and state <= 255 and !side.active.volatiles.Bide));
1397
1397
 
1398
- var accuracy = if (overwritten) state else @as(u16, move.accuracy);
1398
+ var accuracy: u16 = if (overwritten) state else move.accuracy;
1399
1399
  var boost = BOOSTS[@as(u4, @intCast(@as(i8, side.active.boosts.accuracy) + 6))];
1400
1400
  accuracy = accuracy * boost[0] / boost[1];
1401
1401
  boost = BOOSTS[@as(u4, @intCast(@as(i8, -foe.active.boosts.evasion) + 6))];
@@ -2689,6 +2689,8 @@ pub const Rolls = struct {
2689
2689
  fn speedTie(battle: anytype, options: anytype) !bool {
2690
2690
  const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
2691
2691
  player == .P1
2692
+ else if (@hasDecl(@TypeOf(battle.rng), "speedTie"))
2693
+ battle.rng.speedTie()
2692
2694
  else if (showdown)
2693
2695
  battle.rng.range(u8, 0, 2) == 0
2694
2696
  else
@@ -2701,6 +2703,8 @@ pub const Rolls = struct {
2701
2703
  fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2702
2704
  const crit = if (options.calc.overridden(player, .critical_hit)) |val|
2703
2705
  val == .true
2706
+ else if (@hasDecl(@TypeOf(battle.rng), "criticalHit"))
2707
+ battle.rng.criticalHit(player, rate)
2704
2708
  else if (showdown)
2705
2709
  battle.rng.chance(u8, rate, 256)
2706
2710
  else
@@ -2713,6 +2717,8 @@ pub const Rolls = struct {
2713
2717
  fn damage(battle: anytype, player: Player, options: anytype) !u8 {
2714
2718
  const roll = if (options.calc.overridden(player, .damage)) |val|
2715
2719
  val
2720
+ else if (@hasDecl(@TypeOf(battle.rng), "damage"))
2721
+ battle.rng.damage(player)
2716
2722
  else roll: {
2717
2723
  if (showdown) break :roll battle.rng.range(u8, 217, 256);
2718
2724
  while (true) {
@@ -2729,6 +2735,8 @@ pub const Rolls = struct {
2729
2735
  fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) bool {
2730
2736
  const ok = if (options.calc.overridden(player, .hit)) |val|
2731
2737
  val == .true
2738
+ else if (@hasDecl(@TypeOf(battle.rng), "hit"))
2739
+ battle.rng.hit(player, accuracy)
2732
2740
  else if (showdown)
2733
2741
  battle.rng.chance(u8, accuracy, 256)
2734
2742
  else
@@ -2741,6 +2749,8 @@ pub const Rolls = struct {
2741
2749
  fn confused(battle: anytype, player: Player, options: anytype) !bool {
2742
2750
  const cfz = if (options.calc.overridden(player, .confused)) |val|
2743
2751
  val == .true
2752
+ else if (@hasDecl(@TypeOf(battle.rng), "confused"))
2753
+ battle.rng.confused(player)
2744
2754
  else if (showdown)
2745
2755
  !battle.rng.chance(u8, 128, 256)
2746
2756
  else
@@ -2753,6 +2763,8 @@ pub const Rolls = struct {
2753
2763
  fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2754
2764
  const par = if (options.calc.overridden(player, .paralyzed)) |val|
2755
2765
  val == .true
2766
+ else if (@hasDecl(@TypeOf(battle.rng), "paralyzed"))
2767
+ battle.rng.paralyzed(player)
2756
2768
  else if (showdown)
2757
2769
  battle.rng.chance(u8, 63, 256)
2758
2770
  else
@@ -2765,6 +2777,8 @@ pub const Rolls = struct {
2765
2777
  fn confusionChance(battle: anytype, player: Player, options: anytype) !bool {
2766
2778
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2767
2779
  val == .true
2780
+ else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
2781
+ battle.rng.secondaryChance(player, 25)
2768
2782
  else if (showdown)
2769
2783
  battle.rng.chance(u8, 25, 256)
2770
2784
  else
@@ -2779,6 +2793,8 @@ pub const Rolls = struct {
2779
2793
 
2780
2794
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2781
2795
  val == .true
2796
+ else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
2797
+ battle.rng.secondaryChance(player, rate)
2782
2798
  else if (showdown)
2783
2799
  battle.rng.chance(u8, rate, 256)
2784
2800
  else
@@ -2793,6 +2809,8 @@ pub const Rolls = struct {
2793
2809
 
2794
2810
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2795
2811
  val == .true
2812
+ else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
2813
+ battle.rng.secondaryChance(player, rate)
2796
2814
  else if (showdown)
2797
2815
  battle.rng.chance(u8, rate, 256)
2798
2816
  else
@@ -2805,6 +2823,8 @@ pub const Rolls = struct {
2805
2823
  fn unboost(battle: anytype, player: Player, options: anytype) !bool {
2806
2824
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2807
2825
  val == .true
2826
+ else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
2827
+ battle.rng.secondaryChance(player, 85)
2808
2828
  else if (showdown)
2809
2829
  battle.rng.chance(u8, 85, 256)
2810
2830
  else
@@ -2817,6 +2837,8 @@ pub const Rolls = struct {
2817
2837
  fn metronome(battle: anytype, player: Player, options: anytype) !Move {
2818
2838
  const move: Move = if (options.calc.overridden(player, .metronome)) |val|
2819
2839
  val
2840
+ else if (@hasDecl(@TypeOf(battle.rng), "metronome"))
2841
+ battle.rng.metronome(player)
2820
2842
  else if (showdown)
2821
2843
  Move.METRONOME[battle.rng.range(u8, 0, Move.METRONOME.len)]
2822
2844
  else move: {
@@ -2835,6 +2857,8 @@ pub const Rolls = struct {
2835
2857
  fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
2836
2858
  const power = if (options.calc.overridden(player, .psywave)) |val|
2837
2859
  val - 1
2860
+ else if (@hasDecl(@TypeOf(battle.rng), "psywave"))
2861
+ battle.rng.psywave(player)
2838
2862
  else if (showdown)
2839
2863
  battle.rng.range(u8, 0, max)
2840
2864
  else power: {
@@ -2852,6 +2876,8 @@ pub const Rolls = struct {
2852
2876
  fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
2853
2877
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2854
2878
  @intCast(val)
2879
+ else if (@hasDecl(@TypeOf(battle.rng), "sleepDuration"))
2880
+ battle.rng.sleepDuration(player)
2855
2881
  else if (showdown)
2856
2882
  battle.rng.range(u3, 1, 8)
2857
2883
  else duration: {
@@ -2869,6 +2895,8 @@ pub const Rolls = struct {
2869
2895
  fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
2870
2896
  const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
2871
2897
  val
2898
+ else if (@hasDecl(@TypeOf(battle.rng), "disableDuration"))
2899
+ battle.rng.disableDuration(player)
2872
2900
  else if (showdown)
2873
2901
  battle.rng.range(u4, 1, 9)
2874
2902
  else
@@ -2882,6 +2910,8 @@ pub const Rolls = struct {
2882
2910
  fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
2883
2911
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2884
2912
  @intCast(val)
2913
+ else if (@hasDecl(@TypeOf(battle.rng), "confusionDuration"))
2914
+ battle.rng.confusionDuration(player, self)
2885
2915
  else if (showdown)
2886
2916
  battle.rng.range(u3, 2, 6)
2887
2917
  else
@@ -2895,6 +2925,8 @@ pub const Rolls = struct {
2895
2925
  fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
2896
2926
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2897
2927
  @intCast(val)
2928
+ else if (@hasDecl(@TypeOf(battle.rng), "attackingDuration"))
2929
+ battle.rng.attackingDuration(player)
2898
2930
  else if (showdown)
2899
2931
  battle.rng.range(u3, 2, 4)
2900
2932
  else
@@ -2916,6 +2948,8 @@ pub const Rolls = struct {
2916
2948
  const roll = if (effect == .Binding) .duration else .multi_hit;
2917
2949
  const n: u3 = if (options.calc.overridden(player, roll)) |val|
2918
2950
  @intCast(val)
2951
+ else if (@hasDecl(@TypeOf(battle.rng), "distribution"))
2952
+ battle.rng.distribution(player)
2919
2953
  else if (showdown)
2920
2954
  DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
2921
2955
  else n: {
@@ -2948,7 +2982,9 @@ pub const Rolls = struct {
2948
2982
  else
2949
2983
  null;
2950
2984
 
2951
- const slot: u4 = overridden orelse slot: {
2985
+ const slot: u4 = overridden orelse if (@hasDecl(@TypeOf(battle.rng), "moveSlot"))
2986
+ battle.rng.moveSlot(player, moves, check_pp)
2987
+ else slot: {
2952
2988
  if (showdown) {
2953
2989
  if (check_pp == 0) {
2954
2990
  var i: usize = moves.len;