@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.
- package/LICENSE +1 -3
- package/README.md +13 -11
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +2 -3
- package/build/pkg/protocol.js +4 -4
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +2 -2
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +2 -3
- package/build/tools/debug.js.map +1 -1
- package/build.zig +67 -86
- package/package.json +26 -19
- package/src/bin/install-pkmn-engine +13 -5
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +15 -20
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/gen1/calc.zig +65 -0
- package/src/lib/gen1/chance.zig +33 -30
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +18 -18
- package/src/lib/gen1/helpers.zig +55 -15
- package/src/lib/gen1/mechanics.zig +46 -8
- package/src/lib/gen2/chance.zig +38 -37
- package/src/lib/gen2/data.zig +8 -8
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +112 -27
- package/src/lib/{binding/node.zig → node.zig} +3 -2
- package/src/lib/pkmn.zig +27 -12
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
|
@@ -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 =
|
|
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 =
|
|
1113
|
+
const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
|
|
1114
1114
|
|
|
1115
|
-
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
|
|
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(
|
|
2743
|
+
battle.rng.chance(uN, acc, 256)
|
|
2734
2744
|
else
|
|
2735
|
-
battle.rng.next() <
|
|
2745
|
+
battle.rng.next() < acc;
|
|
2736
2746
|
|
|
2737
|
-
options.chance.hit(ok,
|
|
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
|
|
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;
|
package/src/lib/gen2/chance.zig
CHANGED
|
@@ -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("
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
972
|
-
try expectValue(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1151
|
+
try expectValue(0, chance.actions.p2.durations.encore);
|
|
1151
1152
|
|
|
1152
1153
|
chance.reset();
|
|
1153
1154
|
}
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
383
|
+
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
384
384
|
base = Species.get(.Pidgeot).stats;
|
|
385
|
-
try expectEqual(
|
|
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(
|
|
432
|
-
try expectEqual(
|
|
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(
|
|
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(
|
|
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));
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
}
|