@pkmn/engine 0.1.0-dev.c4f94762 → 0.1.0-dev.c4ffaf39

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.
@@ -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) {
@@ -2726,21 +2732,27 @@ pub const Rolls = struct {
2726
2732
  return roll;
2727
2733
  }
2728
2734
 
2735
+ const uN = if (pkmn.options.miss) u8 else u9;
2729
2736
  fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) bool {
2737
+ const acc: uN = if (pkmn.options.miss) accuracy else @as(uN, accuracy) + 1;
2730
2738
  const ok = if (options.calc.overridden(player, .hit)) |val|
2731
2739
  val == .true
2740
+ else if (@hasDecl(@TypeOf(battle.rng), "hit"))
2741
+ battle.rng.hit(player, acc)
2732
2742
  else if (showdown)
2733
- battle.rng.chance(u8, accuracy, 256)
2743
+ battle.rng.chance(uN, acc, 256)
2734
2744
  else
2735
- battle.rng.next() < accuracy;
2745
+ battle.rng.next() < acc;
2736
2746
 
2737
- options.chance.hit(ok, accuracy);
2747
+ options.chance.hit(ok, acc);
2738
2748
  return ok;
2739
2749
  }
2740
2750
 
2741
2751
  fn confused(battle: anytype, player: Player, options: anytype) !bool {
2742
2752
  const cfz = if (options.calc.overridden(player, .confused)) |val|
2743
2753
  val == .true
2754
+ else if (@hasDecl(@TypeOf(battle.rng), "confused"))
2755
+ battle.rng.confused(player)
2744
2756
  else if (showdown)
2745
2757
  !battle.rng.chance(u8, 128, 256)
2746
2758
  else
@@ -2753,6 +2765,8 @@ pub const Rolls = struct {
2753
2765
  fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2754
2766
  const par = if (options.calc.overridden(player, .paralyzed)) |val|
2755
2767
  val == .true
2768
+ else if (@hasDecl(@TypeOf(battle.rng), "paralyzed"))
2769
+ battle.rng.paralyzed(player)
2756
2770
  else if (showdown)
2757
2771
  battle.rng.chance(u8, 63, 256)
2758
2772
  else
@@ -2765,6 +2779,8 @@ pub const Rolls = struct {
2765
2779
  fn confusionChance(battle: anytype, player: Player, options: anytype) !bool {
2766
2780
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2767
2781
  val == .true
2782
+ else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
2783
+ battle.rng.secondaryChance(player, 25)
2768
2784
  else if (showdown)
2769
2785
  battle.rng.chance(u8, 25, 256)
2770
2786
  else
@@ -2779,6 +2795,8 @@ pub const Rolls = struct {
2779
2795
 
2780
2796
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2781
2797
  val == .true
2798
+ else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
2799
+ battle.rng.secondaryChance(player, rate)
2782
2800
  else if (showdown)
2783
2801
  battle.rng.chance(u8, rate, 256)
2784
2802
  else
@@ -2793,6 +2811,8 @@ pub const Rolls = struct {
2793
2811
 
2794
2812
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2795
2813
  val == .true
2814
+ else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
2815
+ battle.rng.secondaryChance(player, rate)
2796
2816
  else if (showdown)
2797
2817
  battle.rng.chance(u8, rate, 256)
2798
2818
  else
@@ -2805,6 +2825,8 @@ pub const Rolls = struct {
2805
2825
  fn unboost(battle: anytype, player: Player, options: anytype) !bool {
2806
2826
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2807
2827
  val == .true
2828
+ else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
2829
+ battle.rng.secondaryChance(player, 85)
2808
2830
  else if (showdown)
2809
2831
  battle.rng.chance(u8, 85, 256)
2810
2832
  else
@@ -2817,6 +2839,8 @@ pub const Rolls = struct {
2817
2839
  fn metronome(battle: anytype, player: Player, options: anytype) !Move {
2818
2840
  const move: Move = if (options.calc.overridden(player, .metronome)) |val|
2819
2841
  val
2842
+ else if (@hasDecl(@TypeOf(battle.rng), "metronome"))
2843
+ battle.rng.metronome(player)
2820
2844
  else if (showdown)
2821
2845
  Move.METRONOME[battle.rng.range(u8, 0, Move.METRONOME.len)]
2822
2846
  else move: {
@@ -2835,6 +2859,8 @@ pub const Rolls = struct {
2835
2859
  fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
2836
2860
  const power = if (options.calc.overridden(player, .psywave)) |val|
2837
2861
  val - 1
2862
+ else if (@hasDecl(@TypeOf(battle.rng), "psywave"))
2863
+ battle.rng.psywave(player)
2838
2864
  else if (showdown)
2839
2865
  battle.rng.range(u8, 0, max)
2840
2866
  else power: {
@@ -2852,6 +2878,8 @@ pub const Rolls = struct {
2852
2878
  fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
2853
2879
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2854
2880
  @intCast(val)
2881
+ else if (@hasDecl(@TypeOf(battle.rng), "sleepDuration"))
2882
+ battle.rng.sleepDuration(player)
2855
2883
  else if (showdown)
2856
2884
  battle.rng.range(u3, 1, 8)
2857
2885
  else duration: {
@@ -2869,6 +2897,8 @@ pub const Rolls = struct {
2869
2897
  fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
2870
2898
  const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
2871
2899
  val
2900
+ else if (@hasDecl(@TypeOf(battle.rng), "disableDuration"))
2901
+ battle.rng.disableDuration(player)
2872
2902
  else if (showdown)
2873
2903
  battle.rng.range(u4, 1, 9)
2874
2904
  else
@@ -2882,6 +2912,8 @@ pub const Rolls = struct {
2882
2912
  fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
2883
2913
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2884
2914
  @intCast(val)
2915
+ else if (@hasDecl(@TypeOf(battle.rng), "confusionDuration"))
2916
+ battle.rng.confusionDuration(player, self)
2885
2917
  else if (showdown)
2886
2918
  battle.rng.range(u3, 2, 6)
2887
2919
  else
@@ -2895,6 +2927,8 @@ pub const Rolls = struct {
2895
2927
  fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
2896
2928
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2897
2929
  @intCast(val)
2930
+ else if (@hasDecl(@TypeOf(battle.rng), "attackingDuration"))
2931
+ battle.rng.attackingDuration(player)
2898
2932
  else if (showdown)
2899
2933
  battle.rng.range(u3, 2, 4)
2900
2934
  else
@@ -2916,6 +2950,8 @@ pub const Rolls = struct {
2916
2950
  const roll = if (effect == .Binding) .duration else .multi_hit;
2917
2951
  const n: u3 = if (options.calc.overridden(player, roll)) |val|
2918
2952
  @intCast(val)
2953
+ else if (@hasDecl(@TypeOf(battle.rng), "distribution"))
2954
+ battle.rng.distribution(player)
2919
2955
  else if (showdown)
2920
2956
  DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
2921
2957
  else n: {
@@ -2948,7 +2984,9 @@ pub const Rolls = struct {
2948
2984
  else
2949
2985
  null;
2950
2986
 
2951
- const slot: u4 = overridden orelse slot: {
2987
+ const slot: u4 = overridden orelse if (@hasDecl(@TypeOf(battle.rng), "moveSlot"))
2988
+ battle.rng.moveSlot(player, moves, check_pp)
2989
+ else slot: {
2952
2990
  if (showdown) {
2953
2991
  if (check_pp == 0) {
2954
2992
  var i: usize = moves.len;
@@ -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;
@@ -97,16 +97,17 @@ pub const Actions = extern struct {
97
97
  }
98
98
  };
99
99
 
100
+ // TODO: ziglang/zig#19730
100
101
  test Actions {
101
102
  const a: Actions = .{ .p1 = .{ .hit = .true, .critical_hit = .false, .damage = 245 } };
102
103
  const b: Actions = .{ .p1 = .{ .hit = .false, .critical_hit = .true, .damage = 246 } };
103
104
  const c: Actions = .{ .p1 = .{ .hit = .true } };
104
- const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
105
- const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
106
- const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
107
- const g: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1 } } };
108
- const h: Actions =
109
- .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1, .thrash = true } } };
105
+ // const d: Actions = .{ .p2 = .{ .hit = .true, .durations = .{ .sleep = 2 } } };
106
+ // const e: Actions = .{ .p2 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
107
+ // const f: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .sleep = 4 } } };
108
+ // const g: Actions = .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1 } } };
109
+ // const h: Actions =
110
+ // .{ .p1 = .{ .hit = .false, .durations = .{ .confusion = 1, .thrash = true } } };
110
111
 
111
112
  try expect(a.eql(a));
112
113
  try expect(!a.eql(b));
@@ -119,10 +120,10 @@ test Actions {
119
120
  try expect(b.matches(a));
120
121
  try expect(!a.matches(c));
121
122
  try expect(!c.matches(a));
122
- try expect(d.matches(e));
123
- try expect(!d.matches(f));
124
- try expect(!g.matches(h));
125
- try expect(h.matches(h));
123
+ // try expect(d.matches(e));
124
+ // try expect(!d.matches(f));
125
+ // try expect(!g.matches(h));
126
+ // try expect(h.matches(h));
126
127
  }
127
128
 
128
129
  /// Information about the RNG that was observed during a Generation II battle `update` for a
@@ -699,7 +700,7 @@ test "Chance.damage" {
699
700
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
700
701
 
701
702
  try chance.damage(.P1, 217);
702
- try expectValue(@as(u8, 217), chance.actions.p1.damage);
703
+ try expectValue(217, chance.actions.p1.damage);
703
704
  try expectProbability(&chance.probability, 1, 39);
704
705
  }
705
706
 
@@ -824,7 +825,7 @@ test "Chance.present" {
824
825
  } else {
825
826
  try expectProbability(&chance.probability, 25, 256);
826
827
  }
827
- try expectValue(@as(u3, 4), chance.actions.p2.present);
828
+ try expectValue(4, chance.actions.p2.present);
828
829
 
829
830
  chance.reset();
830
831
 
@@ -834,7 +835,7 @@ test "Chance.present" {
834
835
  } else {
835
836
  try expectProbability(&chance.probability, 51, 256);
836
837
  }
837
- try expectValue(@as(u3, 1), chance.actions.p1.present);
838
+ try expectValue(1, chance.actions.p1.present);
838
839
  }
839
840
 
840
841
  test "Chance.magnitude" {
@@ -846,7 +847,7 @@ test "Chance.magnitude" {
846
847
  } else {
847
848
  try expectProbability(&chance.probability, 13, 256);
848
849
  }
849
- try expectValue(@as(u3, 7), chance.actions.p2.magnitude);
850
+ try expectValue(7, chance.actions.p2.magnitude);
850
851
 
851
852
  chance.reset();
852
853
 
@@ -856,7 +857,7 @@ test "Chance.magnitude" {
856
857
  } else {
857
858
  try expectProbability(&chance.probability, 51, 256);
858
859
  }
859
- try expectValue(@as(u3, 5), chance.actions.p1.magnitude);
860
+ try expectValue(5, chance.actions.p1.magnitude);
860
861
  }
861
862
 
862
863
  test "Chance.tripleKick" {
@@ -864,7 +865,7 @@ test "Chance.tripleKick" {
864
865
 
865
866
  try chance.tripleKick(.P1, 2);
866
867
  try expectProbability(&chance.probability, 1, 3);
867
- try expectValue(@as(u2, 2), chance.actions.p1.triple_kick);
868
+ try expectValue(2, chance.actions.p1.triple_kick);
868
869
  }
869
870
 
870
871
  test "Chance.spite" {
@@ -872,7 +873,7 @@ test "Chance.spite" {
872
873
 
873
874
  try chance.spite(.P1, 3);
874
875
  try expectProbability(&chance.probability, 1, 4);
875
- try expectValue(@as(u3, 3), chance.actions.p1.spite);
876
+ try expectValue(3, chance.actions.p1.spite);
876
877
  }
877
878
 
878
879
  test "Chance.conversion2" {
@@ -899,7 +900,7 @@ test "Chance.psywave" {
899
900
 
900
901
  try chance.psywave(.P2, 100, 150);
901
902
  try expectProbability(&chance.probability, 1, 150);
902
- try expectValue(@as(u8, 100), chance.actions.p2.psywave);
903
+ try expectValue(100, chance.actions.p2.psywave);
903
904
  }
904
905
 
905
906
  test "Chance.metronome" {
@@ -915,19 +916,19 @@ test "Chance.moveSlot" {
915
916
 
916
917
  try chance.moveSlot(.P2, 2, 2);
917
918
  try expectProbability(&chance.probability, 1, 2);
918
- try expectValue(@as(u4, 2), chance.actions.p2.move_slot);
919
+ try expectValue(2, chance.actions.p2.move_slot);
919
920
 
920
921
  chance.reset();
921
922
 
922
923
  try chance.moveSlot(.P1, 1, 3);
923
924
  try expectProbability(&chance.probability, 1, 3);
924
- try expectValue(@as(u4, 1), chance.actions.p1.move_slot);
925
+ try expectValue(1, chance.actions.p1.move_slot);
925
926
 
926
927
  chance.reset();
927
928
 
928
929
  try chance.moveSlot(.P1, 4, 1);
929
930
  try expectProbability(&chance.probability, 1, 1);
930
- try expectValue(@as(u4, 4), chance.actions.p1.move_slot);
931
+ try expectValue(4, chance.actions.p1.move_slot);
931
932
  }
932
933
 
933
934
  test "Chance.forceSwitch" {
@@ -935,19 +936,19 @@ test "Chance.forceSwitch" {
935
936
 
936
937
  try chance.forceSwitch(.P2, 2, 2);
937
938
  try expectProbability(&chance.probability, 1, 2);
938
- try expectValue(@as(u4, 2), chance.actions.p2.force_switch);
939
+ try expectValue(2, chance.actions.p2.force_switch);
939
940
 
940
941
  chance.reset();
941
942
 
942
943
  try chance.forceSwitch(.P1, 1, 3);
943
944
  try expectProbability(&chance.probability, 1, 3);
944
- try expectValue(@as(u4, 1), chance.actions.p1.force_switch);
945
+ try expectValue(1, chance.actions.p1.force_switch);
945
946
 
946
947
  chance.reset();
947
948
 
948
949
  try chance.forceSwitch(.P1, 4, 1);
949
950
  try expectProbability(&chance.probability, 1, 1);
950
- try expectValue(@as(u4, 4), chance.actions.p1.force_switch);
951
+ try expectValue(4, chance.actions.p1.force_switch);
951
952
  }
952
953
 
953
954
  test "Chance.multiHit" {
@@ -955,21 +956,21 @@ test "Chance.multiHit" {
955
956
 
956
957
  try chance.multiHit(.P1, 3);
957
958
  try expectProbability(&chance.probability, 3, 8);
958
- try expectValue(@as(u4, 3), chance.actions.p1.multi_hit);
959
+ try expectValue(3, chance.actions.p1.multi_hit);
959
960
 
960
961
  chance.reset();
961
962
 
962
963
  try chance.multiHit(.P2, 5);
963
964
  try expectProbability(&chance.probability, 1, 8);
964
- try expectValue(@as(u4, 5), chance.actions.p2.multi_hit);
965
+ try expectValue(5, chance.actions.p2.multi_hit);
965
966
  }
966
967
 
967
968
  test "Chance.duration" {
968
969
  var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
969
970
 
970
971
  chance.duration(.sleep, .P1, .P2, 2);
971
- try expectValue(@as(u4, 2), chance.actions.p1.duration);
972
- try expectValue(@as(u3, 1), chance.actions.p2.durations.sleep);
972
+ try expectValue(2, chance.actions.p1.duration);
973
+ try expectValue(1, chance.actions.p2.durations.sleep);
973
974
  }
974
975
 
975
976
  test "Chance.sleep" {
@@ -979,7 +980,7 @@ test "Chance.sleep" {
979
980
  chance.actions.p1.durations.sleep = @intCast(i);
980
981
  try chance.sleep(.P1, 0);
981
982
  try expectProbability(&chance.probability, 1, d);
982
- try expectValue(@as(u3, 0), chance.actions.p1.durations.sleep);
983
+ try expectValue(0, chance.actions.p1.durations.sleep);
983
984
 
984
985
  chance.reset();
985
986
 
@@ -1003,7 +1004,7 @@ test "Chance.confusion" {
1003
1004
  chance.actions.p2.durations.confusion = @intCast(i);
1004
1005
  try chance.confusion(.P2, 0);
1005
1006
  try expectProbability(&chance.probability, 1, d);
1006
- try expectValue(@as(u3, 0), chance.actions.p2.durations.confusion);
1007
+ try expectValue(0, chance.actions.p2.durations.confusion);
1007
1008
  try expectValue(false, chance.actions.p2.durations.thrash);
1008
1009
 
1009
1010
  chance.reset();
@@ -1028,7 +1029,7 @@ test "Chance.confusion" {
1028
1029
  chance.actions.p2.durations.confusion = @intCast(i);
1029
1030
  try chance.confusion(.P2, 0);
1030
1031
  try expectProbability(&chance.probability, 1, d);
1031
- try expectValue(@as(u3, 0), chance.actions.p2.durations.confusion);
1032
+ try expectValue(0, chance.actions.p2.durations.confusion);
1032
1033
  try expectValue(true, chance.actions.p2.durations.thrash);
1033
1034
 
1034
1035
  chance.reset();
@@ -1054,7 +1055,7 @@ test "Chance.disable" {
1054
1055
  chance.actions.p2.durations.disable = @intCast(i);
1055
1056
  try chance.disable(.P2, 0);
1056
1057
  try expectProbability(&chance.probability, 1, d);
1057
- try expectValue(@as(u4, 0), chance.actions.p2.durations.disable);
1058
+ try expectValue(0, chance.actions.p2.durations.disable);
1058
1059
 
1059
1060
  chance.reset();
1060
1061
  }
@@ -1074,7 +1075,7 @@ test "Chance.disable" {
1074
1075
  chance.actions.p2.durations.disable = @intCast(i);
1075
1076
  try chance.disable(.P2, 0);
1076
1077
  try expectProbability(&chance.probability, 1, d);
1077
- try expectValue(@as(u4, 0), chance.actions.p2.durations.disable);
1078
+ try expectValue(0, chance.actions.p2.durations.disable);
1078
1079
 
1079
1080
  chance.reset();
1080
1081
  }
@@ -1099,7 +1100,7 @@ test "Chance.attacking" {
1099
1100
  chance.actions.p2.durations.attacking = @intCast(i);
1100
1101
  try chance.attacking(.P2, 0);
1101
1102
  try expectProbability(&chance.probability, 1, d);
1102
- try expectValue(@as(u3, 0), chance.actions.p2.durations.attacking);
1103
+ try expectValue(0, chance.actions.p2.durations.attacking);
1103
1104
 
1104
1105
  chance.reset();
1105
1106
  }
@@ -1123,7 +1124,7 @@ test "Chance.binding" {
1123
1124
  chance.actions.p2.durations.binding = @intCast(i);
1124
1125
  try chance.binding(.P2, 0);
1125
1126
  try expectProbability(&chance.probability, 1, d);
1126
- try expectValue(@as(u3, 0), chance.actions.p2.durations.binding);
1127
+ try expectValue(0, chance.actions.p2.durations.binding);
1127
1128
 
1128
1129
  chance.reset();
1129
1130
  }
@@ -1147,7 +1148,7 @@ test "Chance.encore" {
1147
1148
  chance.actions.p2.durations.encore = @intCast(i);
1148
1149
  try chance.encore(.P2, 0);
1149
1150
  try expectProbability(&chance.probability, 1, d);
1150
- try expectValue(@as(u3, 0), chance.actions.p2.durations.encore);
1151
+ try expectValue(0, chance.actions.p2.durations.encore);
1151
1152
 
1152
1153
  chance.reset();
1153
1154
  }
@@ -248,12 +248,12 @@ test DVs {
248
248
  var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
249
249
  try expectEqual(Gender.Unknown, dvs.gender);
250
250
  try expectEqual(Type.Ice, dvs.type);
251
- try expectEqual(@as(u8, 70), dvs.power());
251
+ try expectEqual(70, dvs.power());
252
252
 
253
253
  dvs = DVs.from(.Pikachu, .{ .spc = 14 });
254
254
  try expectEqual(Gender.Male, dvs.gender);
255
255
  try expectEqual(Type.Dark, dvs.type);
256
- try expectEqual(@as(u8, 69), dvs.power());
256
+ try expectEqual(69, dvs.power());
257
257
 
258
258
  dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
259
259
  try expectEqual(Gender.Female, dvs.gender);
@@ -380,9 +380,9 @@ test Stats {
380
380
  try expectEqual(0, stats.def);
381
381
 
382
382
  var base = Species.get(.Gyarados).stats;
383
- try expectEqual(@as(u16, 127), Stats(u16).calc("hp", base.hp, 0, 5120, 38));
383
+ try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
384
384
  base = Species.get(.Pidgeot).stats;
385
- try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
385
+ try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
386
386
  }
387
387
 
388
388
  pub const Boosts = packed struct(u32) {
@@ -428,8 +428,8 @@ test Move {
428
428
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
429
429
  try expectEqual(rng.Gen12.percent(50), move.accuracy);
430
430
  try expectEqual(rng.Gen12.percent(100), move.chance);
431
- try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
432
- try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
431
+ try expectEqual(5, Move.pp(.DynamicPunch));
432
+ try expectEqual(-1, Move.get(.Counter).priority);
433
433
  try expect(!move.effect.isHighCritical());
434
434
  try expect(Move.get(.KarateChop).effect.isHighCritical());
435
435
  try expect(Move.get(.RazorWind).effect.isHighCritical());
@@ -439,7 +439,7 @@ pub const Species = species.Species;
439
439
 
440
440
  test Species {
441
441
  try expectEqual(152, @intFromEnum(Species.Chikorita));
442
- try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
442
+ try expectEqual(100, Species.get(.Celebi).stats.spd);
443
443
  }
444
444
 
445
445
  pub const Type = types.Type;
@@ -452,7 +452,7 @@ test Types {
452
452
  try expect(!Type.Steel.special());
453
453
  try expect(Type.Dark.special());
454
454
 
455
- try expectEqual(@as(u8, 3), Type.Water.precedence());
455
+ try expectEqual(3, Type.Water.precedence());
456
456
  try expect(Type.Bug.precedence() > Type.Poison.precedence());
457
457
 
458
458
  try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
@@ -65,17 +65,47 @@ pub const Battle = struct {
65
65
  };
66
66
 
67
67
  pub fn prng(rand: *PSRNG) data.PRNG {
68
- // FIXME: what is max range in gen2?
69
68
  return .{
70
69
  .src = .{
71
70
  .seed = if (showdown)
72
71
  rand.newSeed()
73
- else
74
- .{
75
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
76
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
77
- rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
78
- },
72
+ else seed: {
73
+ // GLITCH: The initial RNG seed in Gen II link battles is heavily constrained
74
+ // https://www.smogon.com/forums/threads/rng-in-rby-link-battles.3746779
75
+ var seed = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
76
+
77
+ // Bytes in seed can only range from 0-252, not 0-255
78
+ const max: u8 = 253;
79
+ // Instructions take multiples of 4 cycles to execute
80
+ var timer: u16 = 4 * rand.range(u16, 0, 16384);
81
+ var add: u8 = rand.range(u8, 0, 256);
82
+ var sub: u8 = rand.range(u8, 0, 256);
83
+ var carry: u8 = 0;
84
+ var div: u8 = 0;
85
+
86
+ var i: u8 = 0;
87
+ while (i < seed.len) {
88
+ div = @intCast(timer >> 8);
89
+ // This is effectively @addWithOverflow but with three operands
90
+ const result = @as(u16, add) + @as(u16, div) + @as(u16, carry);
91
+ add = @intCast(result & 255);
92
+ carry = @intFromBool(result > 255);
93
+ timer +%= 44;
94
+ div = @intCast(timer >> 8);
95
+ sub -%= div +% carry;
96
+
97
+ if (sub < max) {
98
+ seed[i] = sub;
99
+ i += 1;
100
+ timer +%= 152;
101
+ carry = 1;
102
+ } else {
103
+ timer +%= 132;
104
+ carry = 0;
105
+ }
106
+ }
107
+ break :seed seed;
108
+ },
79
109
  },
80
110
  };
81
111
  }