@pkmn/engine 0.1.0-dev.6c5fdce2 → 0.1.0-dev.6cc9e067
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 +34 -11
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +4 -5
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.js.map +1 -1
- 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 +57 -60
- package/package.json +29 -22
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/protocol.zig +16 -21
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/gen1/chance.zig +27 -26
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +18 -18
- package/src/lib/gen1/helpers.zig +35 -15
- package/src/lib/gen1/mechanics.zig +41 -14
- package/src/lib/gen2/chance.zig +38 -37
- package/src/lib/gen2/data.zig +8 -8
- package/src/lib/gen2/generated.zig +0 -2
- package/src/lib/gen2/helpers.zig +37 -7
- package/src/lib/gen2/mechanics.zig +112 -32
- package/src/lib/{binding/node.zig → node.zig} +3 -6
- 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
|
@@ -635,8 +635,6 @@ fn beforeMove(
|
|
|
635
635
|
volatiles.state +%= battle.last_damage;
|
|
636
636
|
}
|
|
637
637
|
|
|
638
|
-
assert(volatiles.attacks > 0);
|
|
639
|
-
|
|
640
638
|
if (options.calc.modify(player, .attacking)) |extend| {
|
|
641
639
|
if (!extend) volatiles.attacks = 0;
|
|
642
640
|
} else {
|
|
@@ -676,8 +674,6 @@ fn beforeMove(
|
|
|
676
674
|
}
|
|
677
675
|
|
|
678
676
|
if (volatiles.Thrashing) {
|
|
679
|
-
assert(volatiles.attacks > 0);
|
|
680
|
-
|
|
681
677
|
if (options.calc.modify(player, .attacking)) |extend| {
|
|
682
678
|
if (!extend) volatiles.attacks = 0;
|
|
683
679
|
} else {
|
|
@@ -701,8 +697,6 @@ fn beforeMove(
|
|
|
701
697
|
}
|
|
702
698
|
|
|
703
699
|
if (volatiles.Binding) {
|
|
704
|
-
assert(volatiles.attacks > 0);
|
|
705
|
-
|
|
706
700
|
if (options.calc.modify(player, .binding)) |extend| {
|
|
707
701
|
if (!extend) volatiles.attacks = 0;
|
|
708
702
|
} else {
|
|
@@ -1059,7 +1053,7 @@ fn checkCriticalHit(battle: anytype, player: Player, move: Move.Data, options: a
|
|
|
1059
1053
|
const side = battle.side(player);
|
|
1060
1054
|
|
|
1061
1055
|
// Base speed is used for the critical hit calculation, even when Transform-ed
|
|
1062
|
-
var rate =
|
|
1056
|
+
var rate: u16 = Species.chance(side.stored().species);
|
|
1063
1057
|
// GLITCH: Focus Energy reduces critical hit rate instead of increasing it
|
|
1064
1058
|
rate = if (side.active.volatiles.FocusEnergy) rate / 2 else @min(rate * 2, 255);
|
|
1065
1059
|
rate = if (move.effect == .HighCritical) @min(rate * 4, 255) else rate / 2;
|
|
@@ -1116,9 +1110,9 @@ fn calcDamage(
|
|
|
1116
1110
|
def = @max((def / 4) & 255, if (showdown) 1 else 0);
|
|
1117
1111
|
}
|
|
1118
1112
|
|
|
1119
|
-
const lvl =
|
|
1113
|
+
const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
|
|
1120
1114
|
|
|
1121
|
-
def =
|
|
1115
|
+
def = if (move.effect == .Explode) @max(def / 2, 1) else def;
|
|
1122
1116
|
|
|
1123
1117
|
if (def == 0) return false;
|
|
1124
1118
|
|
|
@@ -1401,7 +1395,7 @@ fn moveHit(
|
|
|
1401
1395
|
const overwritten = overwrite and state > 0;
|
|
1402
1396
|
assert(!overwritten or (0 < state and state <= 255 and !side.active.volatiles.Bide));
|
|
1403
1397
|
|
|
1404
|
-
var accuracy = if (overwritten) state else
|
|
1398
|
+
var accuracy: u16 = if (overwritten) state else move.accuracy;
|
|
1405
1399
|
var boost = BOOSTS[@as(u4, @intCast(@as(i8, side.active.boosts.accuracy) + 6))];
|
|
1406
1400
|
accuracy = accuracy * boost[0] / boost[1];
|
|
1407
1401
|
boost = BOOSTS[@as(u4, @intCast(@as(i8, -foe.active.boosts.evasion) + 6))];
|
|
@@ -1678,7 +1672,6 @@ fn onBegin(
|
|
|
1678
1672
|
residual: *bool,
|
|
1679
1673
|
options: anytype,
|
|
1680
1674
|
) !void {
|
|
1681
|
-
assert(move.effect.onBegin());
|
|
1682
1675
|
return switch (move.effect) {
|
|
1683
1676
|
.Confusion => Effects.confusion(battle, player, move, options),
|
|
1684
1677
|
.Conversion => Effects.conversion(battle, player, options),
|
|
@@ -1701,7 +1694,6 @@ fn onBegin(
|
|
|
1701
1694
|
}
|
|
1702
1695
|
|
|
1703
1696
|
fn onEnd(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1704
|
-
assert(move.effect.onEnd());
|
|
1705
1697
|
return switch (move.effect) {
|
|
1706
1698
|
.Bide => Effects.bide(battle, player, options),
|
|
1707
1699
|
// zig fmt: off
|
|
@@ -1723,7 +1715,6 @@ fn alwaysHappens(
|
|
|
1723
1715
|
residual: *bool,
|
|
1724
1716
|
options: anytype,
|
|
1725
1717
|
) !void {
|
|
1726
|
-
assert(move.effect.alwaysHappens());
|
|
1727
1718
|
return switch (move.effect) {
|
|
1728
1719
|
.DrainHP, .DreamEater => Effects.drainHP(battle, player, options),
|
|
1729
1720
|
.Explode => Effects.explode(battle, player),
|
|
@@ -2698,6 +2689,8 @@ pub const Rolls = struct {
|
|
|
2698
2689
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2699
2690
|
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2700
2691
|
player == .P1
|
|
2692
|
+
else if (@hasDecl(@TypeOf(battle.rng), "speedTie"))
|
|
2693
|
+
battle.rng.speedTie()
|
|
2701
2694
|
else if (showdown)
|
|
2702
2695
|
battle.rng.range(u8, 0, 2) == 0
|
|
2703
2696
|
else
|
|
@@ -2710,6 +2703,8 @@ pub const Rolls = struct {
|
|
|
2710
2703
|
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2711
2704
|
const crit = if (options.calc.overridden(player, .critical_hit)) |val|
|
|
2712
2705
|
val == .true
|
|
2706
|
+
else if (@hasDecl(@TypeOf(battle.rng), "criticalHit"))
|
|
2707
|
+
battle.rng.criticalHit(player, rate)
|
|
2713
2708
|
else if (showdown)
|
|
2714
2709
|
battle.rng.chance(u8, rate, 256)
|
|
2715
2710
|
else
|
|
@@ -2722,6 +2717,8 @@ pub const Rolls = struct {
|
|
|
2722
2717
|
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2723
2718
|
const roll = if (options.calc.overridden(player, .damage)) |val|
|
|
2724
2719
|
val
|
|
2720
|
+
else if (@hasDecl(@TypeOf(battle.rng), "damage"))
|
|
2721
|
+
battle.rng.damage(player)
|
|
2725
2722
|
else roll: {
|
|
2726
2723
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
2727
2724
|
while (true) {
|
|
@@ -2738,6 +2735,8 @@ pub const Rolls = struct {
|
|
|
2738
2735
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) bool {
|
|
2739
2736
|
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2740
2737
|
val == .true
|
|
2738
|
+
else if (@hasDecl(@TypeOf(battle.rng), "hit"))
|
|
2739
|
+
battle.rng.hit(player, accuracy)
|
|
2741
2740
|
else if (showdown)
|
|
2742
2741
|
battle.rng.chance(u8, accuracy, 256)
|
|
2743
2742
|
else
|
|
@@ -2750,6 +2749,8 @@ pub const Rolls = struct {
|
|
|
2750
2749
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2751
2750
|
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2752
2751
|
val == .true
|
|
2752
|
+
else if (@hasDecl(@TypeOf(battle.rng), "confused"))
|
|
2753
|
+
battle.rng.confused(player)
|
|
2753
2754
|
else if (showdown)
|
|
2754
2755
|
!battle.rng.chance(u8, 128, 256)
|
|
2755
2756
|
else
|
|
@@ -2762,6 +2763,8 @@ pub const Rolls = struct {
|
|
|
2762
2763
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2763
2764
|
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2764
2765
|
val == .true
|
|
2766
|
+
else if (@hasDecl(@TypeOf(battle.rng), "paralyzed"))
|
|
2767
|
+
battle.rng.paralyzed(player)
|
|
2765
2768
|
else if (showdown)
|
|
2766
2769
|
battle.rng.chance(u8, 63, 256)
|
|
2767
2770
|
else
|
|
@@ -2774,6 +2777,8 @@ pub const Rolls = struct {
|
|
|
2774
2777
|
fn confusionChance(battle: anytype, player: Player, options: anytype) !bool {
|
|
2775
2778
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2776
2779
|
val == .true
|
|
2780
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2781
|
+
battle.rng.secondaryChance(player, 25)
|
|
2777
2782
|
else if (showdown)
|
|
2778
2783
|
battle.rng.chance(u8, 25, 256)
|
|
2779
2784
|
else
|
|
@@ -2788,6 +2793,8 @@ pub const Rolls = struct {
|
|
|
2788
2793
|
|
|
2789
2794
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2790
2795
|
val == .true
|
|
2796
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2797
|
+
battle.rng.secondaryChance(player, rate)
|
|
2791
2798
|
else if (showdown)
|
|
2792
2799
|
battle.rng.chance(u8, rate, 256)
|
|
2793
2800
|
else
|
|
@@ -2802,6 +2809,8 @@ pub const Rolls = struct {
|
|
|
2802
2809
|
|
|
2803
2810
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2804
2811
|
val == .true
|
|
2812
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2813
|
+
battle.rng.secondaryChance(player, rate)
|
|
2805
2814
|
else if (showdown)
|
|
2806
2815
|
battle.rng.chance(u8, rate, 256)
|
|
2807
2816
|
else
|
|
@@ -2814,6 +2823,8 @@ pub const Rolls = struct {
|
|
|
2814
2823
|
fn unboost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2815
2824
|
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2816
2825
|
val == .true
|
|
2826
|
+
else if (@hasDecl(@TypeOf(battle.rng), "secondaryChance"))
|
|
2827
|
+
battle.rng.secondaryChance(player, 85)
|
|
2817
2828
|
else if (showdown)
|
|
2818
2829
|
battle.rng.chance(u8, 85, 256)
|
|
2819
2830
|
else
|
|
@@ -2826,6 +2837,8 @@ pub const Rolls = struct {
|
|
|
2826
2837
|
fn metronome(battle: anytype, player: Player, options: anytype) !Move {
|
|
2827
2838
|
const move: Move = if (options.calc.overridden(player, .metronome)) |val|
|
|
2828
2839
|
val
|
|
2840
|
+
else if (@hasDecl(@TypeOf(battle.rng), "metronome"))
|
|
2841
|
+
battle.rng.metronome(player)
|
|
2829
2842
|
else if (showdown)
|
|
2830
2843
|
Move.METRONOME[battle.rng.range(u8, 0, Move.METRONOME.len)]
|
|
2831
2844
|
else move: {
|
|
@@ -2844,6 +2857,8 @@ pub const Rolls = struct {
|
|
|
2844
2857
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
2845
2858
|
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
2846
2859
|
val - 1
|
|
2860
|
+
else if (@hasDecl(@TypeOf(battle.rng), "psywave"))
|
|
2861
|
+
battle.rng.psywave(player)
|
|
2847
2862
|
else if (showdown)
|
|
2848
2863
|
battle.rng.range(u8, 0, max)
|
|
2849
2864
|
else power: {
|
|
@@ -2861,6 +2876,8 @@ pub const Rolls = struct {
|
|
|
2861
2876
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2862
2877
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2863
2878
|
@intCast(val)
|
|
2879
|
+
else if (@hasDecl(@TypeOf(battle.rng), "sleepDuration"))
|
|
2880
|
+
battle.rng.sleepDuration(player)
|
|
2864
2881
|
else if (showdown)
|
|
2865
2882
|
battle.rng.range(u3, 1, 8)
|
|
2866
2883
|
else duration: {
|
|
@@ -2878,6 +2895,8 @@ pub const Rolls = struct {
|
|
|
2878
2895
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
2879
2896
|
const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
|
|
2880
2897
|
val
|
|
2898
|
+
else if (@hasDecl(@TypeOf(battle.rng), "disableDuration"))
|
|
2899
|
+
battle.rng.disableDuration(player)
|
|
2881
2900
|
else if (showdown)
|
|
2882
2901
|
battle.rng.range(u4, 1, 9)
|
|
2883
2902
|
else
|
|
@@ -2891,6 +2910,8 @@ pub const Rolls = struct {
|
|
|
2891
2910
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
2892
2911
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2893
2912
|
@intCast(val)
|
|
2913
|
+
else if (@hasDecl(@TypeOf(battle.rng), "confusionDuration"))
|
|
2914
|
+
battle.rng.confusionDuration(player, self)
|
|
2894
2915
|
else if (showdown)
|
|
2895
2916
|
battle.rng.range(u3, 2, 6)
|
|
2896
2917
|
else
|
|
@@ -2904,6 +2925,8 @@ pub const Rolls = struct {
|
|
|
2904
2925
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2905
2926
|
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2906
2927
|
@intCast(val)
|
|
2928
|
+
else if (@hasDecl(@TypeOf(battle.rng), "attackingDuration"))
|
|
2929
|
+
battle.rng.attackingDuration(player)
|
|
2907
2930
|
else if (showdown)
|
|
2908
2931
|
battle.rng.range(u3, 2, 4)
|
|
2909
2932
|
else
|
|
@@ -2925,6 +2948,8 @@ pub const Rolls = struct {
|
|
|
2925
2948
|
const roll = if (effect == .Binding) .duration else .multi_hit;
|
|
2926
2949
|
const n: u3 = if (options.calc.overridden(player, roll)) |val|
|
|
2927
2950
|
@intCast(val)
|
|
2951
|
+
else if (@hasDecl(@TypeOf(battle.rng), "distribution"))
|
|
2952
|
+
battle.rng.distribution(player)
|
|
2928
2953
|
else if (showdown)
|
|
2929
2954
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
2930
2955
|
else n: {
|
|
@@ -2957,7 +2982,9 @@ pub const Rolls = struct {
|
|
|
2957
2982
|
else
|
|
2958
2983
|
null;
|
|
2959
2984
|
|
|
2960
|
-
const slot: u4 = overridden orelse
|
|
2985
|
+
const slot: u4 = overridden orelse if (@hasDecl(@TypeOf(battle.rng), "moveSlot"))
|
|
2986
|
+
battle.rng.moveSlot(player, moves, check_pp)
|
|
2987
|
+
else slot: {
|
|
2961
2988
|
if (showdown) {
|
|
2962
2989
|
if (check_pp == 0) {
|
|
2963
2990
|
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
|
}
|