@pkmn/engine 0.1.0-dev.683fba11 → 0.1.0-dev.6b383c80
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 -1
- package/README.md +22 -16
- package/build/pkg/addon/browser.js +6 -3
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +23 -10
- package/build/pkg/addon/node.js.map +1 -1
- package/build/pkg/addon/wasm.js +23 -14
- package/build/pkg/addon/wasm.js.map +1 -1
- package/build/pkg/addon.d.ts +5 -5
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +2 -1
- package/build/pkg/gen1.js +22 -9
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +2 -0
- package/build/pkg/index.js +17 -7
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +11 -2
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +54 -11
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +19 -94
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/index.js +18 -7
- package/build/tools/display/index.js.map +1 -1
- package/build/tools/display/ui.jsx +20 -9
- package/build/tools/display/ui.jsx.map +1 -1
- package/build/tools/display/util.js +140 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +255 -313
- package/package.json +29 -131
- package/src/bin/install-pkmn-engine +24 -22
- package/src/lib/bindings/c.zig +201 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +51 -0
- package/src/lib/common/array.zig +9 -16
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +46 -61
- package/src/lib/common/js.zig +3 -9
- package/src/lib/common/optional.zig +20 -25
- package/src/lib/common/protocol.zig +224 -189
- package/src/lib/common/rational.zig +275 -284
- package/src/lib/common/rng.zig +4 -4
- package/src/lib/common/util.zig +34 -31
- package/src/lib/gen1/calc.zig +141 -119
- package/src/lib/gen1/chance.zig +162 -101
- package/src/lib/gen1/data/types.zig +2 -2
- package/src/lib/gen1/data.zig +27 -24
- package/src/lib/gen1/helpers.zig +36 -26
- package/src/lib/gen1/mechanics.zig +45 -49
- package/src/lib/gen2/calc.zig +3 -6
- package/src/lib/gen2/chance.zig +55 -43
- package/src/lib/gen2/data/types.zig +6 -3
- package/src/lib/gen2/data.zig +20 -16
- package/src/lib/gen2/helpers.zig +27 -19
- package/src/lib/gen2/mechanics.zig +17 -17
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +3 -118
- package/src/lib/pkmn.zig +94 -19
- package/src/lib/wasm.zig +9 -44
- package/src/tools/display/dom.ts +43 -31
package/src/lib/gen2/chance.zig
CHANGED
|
@@ -27,10 +27,6 @@ const Type = data.Type;
|
|
|
27
27
|
const Effectiveness = data.Effectiveness;
|
|
28
28
|
const TriAttack = data.TriAttack;
|
|
29
29
|
|
|
30
|
-
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
31
|
-
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
32
|
-
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
33
|
-
|
|
34
30
|
/// Actions taken by a hypothetical "chance player" that convey information about which RNG events
|
|
35
31
|
/// were observed during a Generation II battle `update`. This can additionally be provided as input
|
|
36
32
|
/// to the `update` call to override the normal behavior of the RNG in order to force specific
|
|
@@ -57,15 +53,28 @@ pub const Actions = extern struct {
|
|
|
57
53
|
/// same shape if they have the same fields set (though those fields need not necessarily be
|
|
58
54
|
/// set to the same value).
|
|
59
55
|
pub fn matches(a: Actions, b: Actions) bool {
|
|
60
|
-
inline for (@
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Actions).@"struct"), "fields"))
|
|
57
|
+
@typeInfo(Actions).@"struct".fields
|
|
58
|
+
else
|
|
59
|
+
@typeInfo(Actions).@"struct".field_names) |player|
|
|
60
|
+
{
|
|
61
|
+
const player_name = if (@hasField(@TypeOf(player), "name"))
|
|
62
|
+
player.name
|
|
63
|
+
else
|
|
64
|
+
player;
|
|
65
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
66
|
+
@typeInfo(Action).@"struct".fields
|
|
67
|
+
else
|
|
68
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
69
|
+
{
|
|
70
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
71
|
+
const a_val = @field(@field(a, player_name), field_name);
|
|
72
|
+
const b_val = @field(@field(b, player_name), field_name);
|
|
64
73
|
|
|
65
74
|
switch (@typeInfo(@TypeOf(a_val))) {
|
|
66
|
-
|
|
75
|
+
.@"enum" => if ((@intFromEnum(a_val) > 0) != (@intFromEnum(b_val) > 0))
|
|
67
76
|
return false,
|
|
68
|
-
|
|
77
|
+
.int => if ((a_val > 0) != (b_val > 0)) return false,
|
|
69
78
|
else => unreachable,
|
|
70
79
|
}
|
|
71
80
|
}
|
|
@@ -73,7 +82,7 @@ pub const Actions = extern struct {
|
|
|
73
82
|
return true;
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
pub fn fmt(self: Actions, writer:
|
|
85
|
+
pub fn fmt(self: Actions, writer: *std.Io.Writer, shape: bool) !void {
|
|
77
86
|
try writer.writeAll("<P1 = ");
|
|
78
87
|
try self.p1.fmt(writer, shape);
|
|
79
88
|
try writer.writeAll(", P2 = ");
|
|
@@ -81,8 +90,7 @@ pub const Actions = extern struct {
|
|
|
81
90
|
try writer.writeAll(">");
|
|
82
91
|
}
|
|
83
92
|
|
|
84
|
-
pub fn format(a: Actions,
|
|
85
|
-
_ = .{ f, o };
|
|
93
|
+
pub fn format(a: Actions, w: *std.Io.Writer) !void {
|
|
86
94
|
try fmt(a, w, false);
|
|
87
95
|
}
|
|
88
96
|
};
|
|
@@ -186,37 +194,41 @@ pub const Action = packed struct(u128) {
|
|
|
186
194
|
|
|
187
195
|
pub const Field = std.meta.FieldEnum(Action);
|
|
188
196
|
|
|
189
|
-
pub fn format(a: Action,
|
|
190
|
-
_ = .{ f, o };
|
|
197
|
+
pub fn format(a: Action, w: *std.Io.Writer) !void {
|
|
191
198
|
try fmt(a, w, false);
|
|
192
199
|
}
|
|
193
200
|
|
|
194
|
-
pub fn fmt(self: Action, writer:
|
|
201
|
+
pub fn fmt(self: Action, writer: *std.Io.Writer, shape: bool) !void {
|
|
195
202
|
try writer.writeByte('(');
|
|
196
203
|
var printed = false;
|
|
197
|
-
inline for (@
|
|
198
|
-
|
|
204
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(Action).@"struct"), "fields"))
|
|
205
|
+
@typeInfo(Action).@"struct".fields
|
|
206
|
+
else
|
|
207
|
+
@typeInfo(Action).@"struct".field_names) |field|
|
|
208
|
+
{
|
|
209
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
210
|
+
const val = @field(self, field_name);
|
|
199
211
|
switch (@typeInfo(@TypeOf(val))) {
|
|
200
|
-
|
|
212
|
+
.@"enum" => if (val != .None) {
|
|
201
213
|
if (printed) try writer.writeAll(", ");
|
|
202
214
|
if (shape) {
|
|
203
|
-
try writer.print("{s}:?", .{
|
|
215
|
+
try writer.print("{s}:?", .{field_name});
|
|
204
216
|
} else if (@TypeOf(val) == Optional(bool)) {
|
|
205
217
|
try writer.print("{s}{s}", .{
|
|
206
218
|
if (val == .false) "!" else "",
|
|
207
|
-
|
|
219
|
+
field_name,
|
|
208
220
|
});
|
|
209
221
|
} else {
|
|
210
|
-
try writer.print("{s}:{s}", .{
|
|
222
|
+
try writer.print("{s}:{s}", .{ field_name, @tagName(val) });
|
|
211
223
|
}
|
|
212
224
|
printed = true;
|
|
213
225
|
},
|
|
214
|
-
|
|
226
|
+
.int => if (val != 0) {
|
|
215
227
|
if (printed) try writer.writeAll(", ");
|
|
216
228
|
if (shape) {
|
|
217
|
-
try writer.print("{s}:?", .{
|
|
229
|
+
try writer.print("{s}:?", .{field_name});
|
|
218
230
|
} else {
|
|
219
|
-
try writer.print("{s}:{d}", .{
|
|
231
|
+
try writer.print("{s}:{d}", .{ field_name, val });
|
|
220
232
|
}
|
|
221
233
|
printed = true;
|
|
222
234
|
},
|
|
@@ -509,7 +521,7 @@ test "Chance.damage" {
|
|
|
509
521
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
510
522
|
|
|
511
523
|
try chance.damage(.P1, 0, 219);
|
|
512
|
-
try expectValue(
|
|
524
|
+
try expectValue(219, Damages.get(chance.actions.p1.damages, 0));
|
|
513
525
|
try expectProbability(&chance.probability, 1, 39);
|
|
514
526
|
}
|
|
515
527
|
|
|
@@ -634,7 +646,7 @@ test "Chance.present" {
|
|
|
634
646
|
} else {
|
|
635
647
|
try expectProbability(&chance.probability, 25, 256);
|
|
636
648
|
}
|
|
637
|
-
try expectValue(
|
|
649
|
+
try expectValue(4, chance.actions.p2.present);
|
|
638
650
|
|
|
639
651
|
chance.reset();
|
|
640
652
|
|
|
@@ -644,7 +656,7 @@ test "Chance.present" {
|
|
|
644
656
|
} else {
|
|
645
657
|
try expectProbability(&chance.probability, 51, 256);
|
|
646
658
|
}
|
|
647
|
-
try expectValue(
|
|
659
|
+
try expectValue(1, chance.actions.p1.present);
|
|
648
660
|
}
|
|
649
661
|
|
|
650
662
|
test "Chance.magnitude" {
|
|
@@ -656,7 +668,7 @@ test "Chance.magnitude" {
|
|
|
656
668
|
} else {
|
|
657
669
|
try expectProbability(&chance.probability, 13, 256);
|
|
658
670
|
}
|
|
659
|
-
try expectValue(
|
|
671
|
+
try expectValue(7, chance.actions.p2.magnitude);
|
|
660
672
|
|
|
661
673
|
chance.reset();
|
|
662
674
|
|
|
@@ -666,7 +678,7 @@ test "Chance.magnitude" {
|
|
|
666
678
|
} else {
|
|
667
679
|
try expectProbability(&chance.probability, 51, 256);
|
|
668
680
|
}
|
|
669
|
-
try expectValue(
|
|
681
|
+
try expectValue(5, chance.actions.p1.magnitude);
|
|
670
682
|
}
|
|
671
683
|
|
|
672
684
|
test "Chance.tripleKick" {
|
|
@@ -674,7 +686,7 @@ test "Chance.tripleKick" {
|
|
|
674
686
|
|
|
675
687
|
try chance.tripleKick(.P1, 2);
|
|
676
688
|
try expectProbability(&chance.probability, 1, 3);
|
|
677
|
-
try expectValue(
|
|
689
|
+
try expectValue(2, chance.actions.p1.triple_kick);
|
|
678
690
|
}
|
|
679
691
|
|
|
680
692
|
test "Chance.spite" {
|
|
@@ -682,7 +694,7 @@ test "Chance.spite" {
|
|
|
682
694
|
|
|
683
695
|
try chance.spite(.P1, 3);
|
|
684
696
|
try expectProbability(&chance.probability, 1, 4);
|
|
685
|
-
try expectValue(
|
|
697
|
+
try expectValue(3, chance.actions.p1.spite);
|
|
686
698
|
}
|
|
687
699
|
|
|
688
700
|
test "Chance.conversion2" {
|
|
@@ -709,7 +721,7 @@ test "Chance.psywave" {
|
|
|
709
721
|
|
|
710
722
|
try chance.psywave(.P2, 100, 150);
|
|
711
723
|
try expectProbability(&chance.probability, 1, 150);
|
|
712
|
-
try expectValue(
|
|
724
|
+
try expectValue(100, chance.actions.p2.psywave);
|
|
713
725
|
}
|
|
714
726
|
|
|
715
727
|
test "Chance.metronome" {
|
|
@@ -725,19 +737,19 @@ test "Chance.moveSlot" {
|
|
|
725
737
|
|
|
726
738
|
try chance.moveSlot(.P2, 2, 2);
|
|
727
739
|
try expectProbability(&chance.probability, 1, 2);
|
|
728
|
-
try expectValue(
|
|
740
|
+
try expectValue(2, chance.actions.p2.move_slot);
|
|
729
741
|
|
|
730
742
|
chance.reset();
|
|
731
743
|
|
|
732
744
|
try chance.moveSlot(.P1, 1, 3);
|
|
733
745
|
try expectProbability(&chance.probability, 1, 3);
|
|
734
|
-
try expectValue(
|
|
746
|
+
try expectValue(1, chance.actions.p1.move_slot);
|
|
735
747
|
|
|
736
748
|
chance.reset();
|
|
737
749
|
|
|
738
750
|
try chance.moveSlot(.P1, 4, 1);
|
|
739
751
|
try expectProbability(&chance.probability, 1, 1);
|
|
740
|
-
try expectValue(
|
|
752
|
+
try expectValue(4, chance.actions.p1.move_slot);
|
|
741
753
|
}
|
|
742
754
|
|
|
743
755
|
test "Chance.forceSwitch" {
|
|
@@ -745,19 +757,19 @@ test "Chance.forceSwitch" {
|
|
|
745
757
|
|
|
746
758
|
try chance.forceSwitch(.P2, 2, 2);
|
|
747
759
|
try expectProbability(&chance.probability, 1, 2);
|
|
748
|
-
try expectValue(
|
|
760
|
+
try expectValue(2, chance.actions.p2.force_switch);
|
|
749
761
|
|
|
750
762
|
chance.reset();
|
|
751
763
|
|
|
752
764
|
try chance.forceSwitch(.P1, 1, 3);
|
|
753
765
|
try expectProbability(&chance.probability, 1, 3);
|
|
754
|
-
try expectValue(
|
|
766
|
+
try expectValue(1, chance.actions.p1.force_switch);
|
|
755
767
|
|
|
756
768
|
chance.reset();
|
|
757
769
|
|
|
758
770
|
try chance.forceSwitch(.P1, 4, 1);
|
|
759
771
|
try expectProbability(&chance.probability, 1, 1);
|
|
760
|
-
try expectValue(
|
|
772
|
+
try expectValue(4, chance.actions.p1.force_switch);
|
|
761
773
|
}
|
|
762
774
|
|
|
763
775
|
test "Chance.multiHit" {
|
|
@@ -765,20 +777,20 @@ test "Chance.multiHit" {
|
|
|
765
777
|
|
|
766
778
|
try chance.multiHit(.P1, 3);
|
|
767
779
|
try expectProbability(&chance.probability, 3, 8);
|
|
768
|
-
try expectValue(
|
|
780
|
+
try expectValue(3, chance.actions.p1.multi_hit);
|
|
769
781
|
|
|
770
782
|
chance.reset();
|
|
771
783
|
|
|
772
784
|
try chance.multiHit(.P2, 5);
|
|
773
785
|
try expectProbability(&chance.probability, 1, 8);
|
|
774
|
-
try expectValue(
|
|
786
|
+
try expectValue(5, chance.actions.p2.multi_hit);
|
|
775
787
|
}
|
|
776
788
|
|
|
777
789
|
test "Chance.duration" {
|
|
778
790
|
var chance: Chance(rational.Rational(u64)) = .{ .probability = .{} };
|
|
779
791
|
|
|
780
792
|
chance.duration(.P1, 2);
|
|
781
|
-
try expectValue(
|
|
793
|
+
try expectValue(2, chance.actions.p1.duration);
|
|
782
794
|
}
|
|
783
795
|
|
|
784
796
|
pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
@@ -786,7 +798,7 @@ pub fn expectProbability(r: anytype, p: u64, q: u64) !void {
|
|
|
786
798
|
|
|
787
799
|
r.reduce();
|
|
788
800
|
if (r.p != p or r.q != q) {
|
|
789
|
-
print("expected {d}/{d}, found {}\n", .{ p, q, r });
|
|
801
|
+
print("expected {d}/{d}, found {f}\n", .{ p, q, r });
|
|
790
802
|
return error.TestExpectedEqual;
|
|
791
803
|
}
|
|
792
804
|
}
|
|
@@ -172,9 +172,12 @@ pub const Type = enum(u8) {
|
|
|
172
172
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
/// The precedence order of Type `
|
|
176
|
-
pub fn precedence(
|
|
177
|
-
return
|
|
175
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
176
|
+
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
177
|
+
// The Ice vs. Fire matchup is out of order - return a higher
|
|
178
|
+
// number than anything else so that it sorts last correctly
|
|
179
|
+
if (t1 == .Ice and t2 == .Fire) return 18;
|
|
180
|
+
return PRECEDENCE[@intFromEnum(t2)];
|
|
178
181
|
}
|
|
179
182
|
|
|
180
183
|
/// The internal index of this Type used by Present.
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -23,11 +23,14 @@ const expect = std.testing.expect;
|
|
|
23
23
|
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
24
24
|
pub const MAX_LOGS: usize = 180; // TODO
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
const ReleaseSmall: std.builtin.OptimizeMode =
|
|
27
|
+
if (@hasDecl(std.builtin.OptimizeMode, "ReleaseSmall")) .small else .ReleaseSmall;
|
|
28
|
+
|
|
29
|
+
pub const CHOICES_SIZE = if (builtin.mode == ReleaseSmall)
|
|
27
30
|
MAX_CHOICES
|
|
28
31
|
else
|
|
29
32
|
std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
|
|
30
|
-
pub const LOGS_SIZE = if (builtin.mode ==
|
|
33
|
+
pub const LOGS_SIZE = if (builtin.mode == ReleaseSmall)
|
|
31
34
|
MAX_LOGS
|
|
32
35
|
else
|
|
33
36
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
@@ -57,7 +60,7 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
57
60
|
turn: u16 = 0,
|
|
58
61
|
field: Field = .{},
|
|
59
62
|
|
|
60
|
-
_: [padding]u8 =
|
|
63
|
+
_: [padding]u8 = @splat(0),
|
|
61
64
|
|
|
62
65
|
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
63
66
|
assert(isPointerTo(self, Self));
|
|
@@ -100,13 +103,13 @@ pub const Weather = enum(u4) {
|
|
|
100
103
|
};
|
|
101
104
|
|
|
102
105
|
pub const Side = extern struct {
|
|
103
|
-
pokemon: [6]Pokemon =
|
|
106
|
+
pokemon: [6]Pokemon = @splat(.{}),
|
|
104
107
|
active: ActivePokemon align(8) = .{},
|
|
105
108
|
conditions: Conditions = .{},
|
|
106
109
|
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
107
110
|
last_used_move: Move = .None,
|
|
108
111
|
|
|
109
|
-
_: [7]u8 =
|
|
112
|
+
_: [7]u8 = @splat(0),
|
|
110
113
|
|
|
111
114
|
const Conditions = packed struct(u32) {
|
|
112
115
|
Spikes: bool = false,
|
|
@@ -161,7 +164,7 @@ pub const Side = extern struct {
|
|
|
161
164
|
pub const ActivePokemon = extern struct {
|
|
162
165
|
volatiles: Volatiles align(4) = .{},
|
|
163
166
|
stats: Stats(u16) = .{},
|
|
164
|
-
moves: [4]MoveSlot =
|
|
167
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
165
168
|
boosts: Boosts = .{},
|
|
166
169
|
types: Types = .{},
|
|
167
170
|
species: Species = .None,
|
|
@@ -182,7 +185,7 @@ pub const ActivePokemon = extern struct {
|
|
|
182
185
|
|
|
183
186
|
pub const Pokemon = extern struct {
|
|
184
187
|
stats: Stats(u16) = .{},
|
|
185
|
-
moves: [4]MoveSlot =
|
|
188
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
186
189
|
types: Types = .{},
|
|
187
190
|
dvs: DVs = .{},
|
|
188
191
|
hp: u16 = 0,
|
|
@@ -245,12 +248,12 @@ test DVs {
|
|
|
245
248
|
var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
|
|
246
249
|
try expectEqual(Gender.Unknown, dvs.gender);
|
|
247
250
|
try expectEqual(Type.Ice, dvs.type);
|
|
248
|
-
try expectEqual(
|
|
251
|
+
try expectEqual(70, dvs.power());
|
|
249
252
|
|
|
250
253
|
dvs = DVs.from(.Pikachu, .{ .spc = 14 });
|
|
251
254
|
try expectEqual(Gender.Male, dvs.gender);
|
|
252
255
|
try expectEqual(Type.Dark, dvs.type);
|
|
253
|
-
try expectEqual(
|
|
256
|
+
try expectEqual(69, dvs.power());
|
|
254
257
|
|
|
255
258
|
dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
|
|
256
259
|
try expectEqual(Gender.Female, dvs.gender);
|
|
@@ -377,9 +380,9 @@ test Stats {
|
|
|
377
380
|
try expectEqual(0, stats.def);
|
|
378
381
|
|
|
379
382
|
var base = Species.get(.Gyarados).stats;
|
|
380
|
-
try expectEqual(
|
|
383
|
+
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
381
384
|
base = Species.get(.Pidgeot).stats;
|
|
382
|
-
try expectEqual(
|
|
385
|
+
try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
383
386
|
}
|
|
384
387
|
|
|
385
388
|
pub const Boosts = packed struct(u32) {
|
|
@@ -425,8 +428,8 @@ test Move {
|
|
|
425
428
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
426
429
|
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
427
430
|
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
428
|
-
try expectEqual(
|
|
429
|
-
try expectEqual(
|
|
431
|
+
try expectEqual(5, Move.pp(.DynamicPunch));
|
|
432
|
+
try expectEqual(-1, Move.get(.Counter).priority);
|
|
430
433
|
try expect(!move.effect.isHighCritical());
|
|
431
434
|
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
432
435
|
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
@@ -436,7 +439,7 @@ pub const Species = species.Species;
|
|
|
436
439
|
|
|
437
440
|
test Species {
|
|
438
441
|
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
439
|
-
try expectEqual(
|
|
442
|
+
try expectEqual(100, Species.get(.Celebi).stats.spd);
|
|
440
443
|
}
|
|
441
444
|
|
|
442
445
|
pub const Type = types.Type;
|
|
@@ -449,8 +452,9 @@ test Types {
|
|
|
449
452
|
try expect(!Type.Steel.special());
|
|
450
453
|
try expect(Type.Dark.special());
|
|
451
454
|
|
|
452
|
-
try expectEqual(
|
|
453
|
-
try expect(Type.
|
|
455
|
+
try expectEqual(3, Type.precedence(.Water, .Water));
|
|
456
|
+
try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
|
|
457
|
+
try expect(Type.precedence(.Ice, .Fire) > Type.precedence(.Ice, .Water));
|
|
454
458
|
|
|
455
459
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
|
456
460
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -16,8 +16,6 @@ const showdown = options.showdown;
|
|
|
16
16
|
const Species = data.Species;
|
|
17
17
|
const Stats = data.Stats;
|
|
18
18
|
|
|
19
|
-
const Struct = if (@hasField(std.builtin.Type, "struct")) .@"struct" else .Struct;
|
|
20
|
-
|
|
21
19
|
pub const Options = struct {
|
|
22
20
|
cleric: bool = showdown,
|
|
23
21
|
};
|
|
@@ -74,7 +72,7 @@ pub fn prng(rand: *PSRNG) data.PRNG {
|
|
|
74
72
|
var carry: u8 = 0;
|
|
75
73
|
var div: u8 = 0;
|
|
76
74
|
|
|
77
|
-
var i:
|
|
75
|
+
var i: usize = 0;
|
|
78
76
|
while (i < seed.len) {
|
|
79
77
|
div = @intCast(timer >> 8);
|
|
80
78
|
// This is effectively @addWithOverflow but with three operands
|
|
@@ -142,15 +140,20 @@ pub const Pokemon = struct {
|
|
|
142
140
|
var pokemon = data.Pokemon{};
|
|
143
141
|
pokemon.species = p.species;
|
|
144
142
|
const species = Species.get(p.species);
|
|
145
|
-
inline for (@
|
|
146
|
-
|
|
143
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(pokemon.stats)).@"struct"), "fields"))
|
|
144
|
+
@typeInfo(@TypeOf(pokemon.stats)).@"struct".fields
|
|
145
|
+
else
|
|
146
|
+
@typeInfo(@TypeOf(pokemon.stats)).@"struct".field_names) |field|
|
|
147
|
+
{
|
|
148
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
149
|
+
const hp = comptime std.mem.eql(u8, field_name, "hp");
|
|
147
150
|
const spc =
|
|
148
|
-
comptime std.mem.eql(u8,
|
|
149
|
-
@field(pokemon.stats,
|
|
150
|
-
|
|
151
|
-
@field(species.stats,
|
|
152
|
-
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs,
|
|
153
|
-
@field(p.stats,
|
|
151
|
+
comptime std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
|
|
152
|
+
@field(pokemon.stats, field_name) = Stats(u16).calc(
|
|
153
|
+
field_name,
|
|
154
|
+
@field(species.stats, field_name),
|
|
155
|
+
if (hp) p.dvs.hp() else if (spc) p.dvs.spc else @field(p.dvs, field_name),
|
|
156
|
+
@field(p.stats, field_name),
|
|
154
157
|
p.level,
|
|
155
158
|
);
|
|
156
159
|
}
|
|
@@ -183,19 +186,24 @@ pub const Pokemon = struct {
|
|
|
183
186
|
|
|
184
187
|
var stats: Stats(u16) = .{};
|
|
185
188
|
const dvs = gen1.DVs.random(rand);
|
|
186
|
-
inline for (@
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
@
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
189
|
+
inline for (if (@hasField(@TypeOf(@typeInfo(@TypeOf(stats)).@"struct"), "fields"))
|
|
190
|
+
@typeInfo(@TypeOf(stats)).@"struct".fields
|
|
191
|
+
else
|
|
192
|
+
@typeInfo(@TypeOf(stats)).@"struct".field_names) |field|
|
|
193
|
+
{
|
|
194
|
+
const field_name = if (@hasField(@TypeOf(field), "name")) field.name else field;
|
|
195
|
+
const hp = std.mem.eql(u8, field_name, "hp");
|
|
196
|
+
const spc = std.mem.eql(u8, field_name, "spa") or std.mem.eql(u8, field_name, "spd");
|
|
197
|
+
@field(stats, field_name) = Stats(u16).calc(
|
|
198
|
+
field_name,
|
|
199
|
+
@field(species.stats, field_name),
|
|
200
|
+
if (hp) dvs.hp() else if (spc) dvs.spc else @field(dvs, field_name),
|
|
193
201
|
if (rand.chance(u8, 1, 20)) rand.range(u8, 0, 255 + 1) else 255,
|
|
194
202
|
lvl,
|
|
195
203
|
);
|
|
196
204
|
}
|
|
197
205
|
|
|
198
|
-
var ms
|
|
206
|
+
var ms: [4]MoveSlot = @splat(.{});
|
|
199
207
|
const n = if (rand.chance(u8, 1, 100)) rand.range(u4, 1, 3 + 1) else 4;
|
|
200
208
|
for (0..n) |i| {
|
|
201
209
|
var m: Move = .None;
|
|
@@ -129,11 +129,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
129
129
|
|
|
130
130
|
const p1_slot = findFirstAlive(p1);
|
|
131
131
|
assert(!showdown or p1_slot == 1);
|
|
132
|
-
if (p1_slot == 0) return if (findFirstAlive(p2) == 0)
|
|
132
|
+
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) .Tie else .Lose;
|
|
133
133
|
|
|
134
134
|
const p2_slot = findFirstAlive(p2);
|
|
135
135
|
assert(!showdown or p2_slot == 1);
|
|
136
|
-
if (p2_slot == 0) return
|
|
136
|
+
if (p2_slot == 0) return .Win;
|
|
137
137
|
|
|
138
138
|
try switchIn(battle, .P1, p1_slot, .Initial, options);
|
|
139
139
|
try switchIn(battle, .P2, p2_slot, .Initial, options);
|
|
@@ -681,7 +681,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
681
681
|
|
|
682
682
|
// Type effectiveness matchup precedence only matters with (NVE, SE)
|
|
683
683
|
if (!showdown and (eff1 + eff2) == Effectiveness.mismatch and
|
|
684
|
-
types.type1.precedence(
|
|
684
|
+
Type.precedence(state.type, types.type1) > Type.precedence(state.type, types.type2))
|
|
685
685
|
{
|
|
686
686
|
assert(eff2 != neutral);
|
|
687
687
|
d = d *% eff2 / 10;
|
|
@@ -818,13 +818,13 @@ fn checkFaint(
|
|
|
818
818
|
|
|
819
819
|
if (player_out and foe_out) {
|
|
820
820
|
try options.log.tie(.{});
|
|
821
|
-
return
|
|
821
|
+
return .Tie;
|
|
822
822
|
} else if (player_out) {
|
|
823
823
|
try options.log.win(.{player.foe()});
|
|
824
|
-
return if (player == .P1)
|
|
824
|
+
return if (player == .P1) .Lose else .Win;
|
|
825
825
|
} else if (foe_out) {
|
|
826
826
|
try options.log.win(.{player});
|
|
827
|
-
return if (player == .P1)
|
|
827
|
+
return if (player == .P1) .Win else .Lose;
|
|
828
828
|
}
|
|
829
829
|
|
|
830
830
|
const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
|
|
@@ -1194,14 +1194,14 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
|
|
|
1194
1194
|
|
|
1195
1195
|
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1196
1196
|
try options.log.tie(.{});
|
|
1197
|
-
return
|
|
1197
|
+
return .Tie;
|
|
1198
1198
|
} else if (battle.turn >= 65535) {
|
|
1199
|
-
return
|
|
1199
|
+
return .Error;
|
|
1200
1200
|
}
|
|
1201
1201
|
|
|
1202
1202
|
try options.log.turn(.{battle.turn});
|
|
1203
1203
|
|
|
1204
|
-
return
|
|
1204
|
+
return .Default;
|
|
1205
1205
|
}
|
|
1206
1206
|
|
|
1207
1207
|
pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1553,7 +1553,7 @@ pub const Effects = struct {
|
|
|
1553
1553
|
const last = battle.foe(player).lastMove(true);
|
|
1554
1554
|
const failed = volatiles.Encore or
|
|
1555
1555
|
(last == .None or last == .Struggle or
|
|
1556
|
-
|
|
1556
|
+
last == .Encore or .last == .MirrorMove);
|
|
1557
1557
|
if (failed) return options.log.fail(.{ foe_ident, .None });
|
|
1558
1558
|
|
|
1559
1559
|
var slot: u3 = 0;
|
|
@@ -3070,7 +3070,7 @@ pub const Rolls = struct {
|
|
|
3070
3070
|
}
|
|
3071
3071
|
|
|
3072
3072
|
// Conversion 2 can at most choose between 7 types (vs. a Grass-type attack)
|
|
3073
|
-
var CONVERSION_2: [7]Type =
|
|
3073
|
+
var CONVERSION_2: [7]Type = @splat(.@"???");
|
|
3074
3074
|
|
|
3075
3075
|
fn conversion2(battle: anytype, player: Player, mtype: Type, options: anytype) !Type {
|
|
3076
3076
|
var i: u8 = 0;
|
|
@@ -3495,11 +3495,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3495
3495
|
},
|
|
3496
3496
|
.Switch => {
|
|
3497
3497
|
const side = battle.side(player);
|
|
3498
|
-
var slot:
|
|
3498
|
+
var slot: usize = 2;
|
|
3499
3499
|
while (slot <= 6) : (slot += 1) {
|
|
3500
3500
|
const id = side.order[slot - 1];
|
|
3501
3501
|
if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
|
|
3502
|
-
out[n] = .{ .type = .Switch, .data = slot };
|
|
3502
|
+
out[n] = .{ .type = .Switch, .data = @intCast(slot) };
|
|
3503
3503
|
n += 1;
|
|
3504
3504
|
}
|
|
3505
3505
|
if (n == 0) {
|
|
@@ -3529,11 +3529,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3529
3529
|
return n;
|
|
3530
3530
|
}
|
|
3531
3531
|
|
|
3532
|
-
var slot:
|
|
3532
|
+
var slot: usize = 2;
|
|
3533
3533
|
while (!active.volatiles.Trapped and slot <= 6) : (slot += 1) {
|
|
3534
3534
|
const id = side.order[slot - 1];
|
|
3535
3535
|
if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
|
|
3536
|
-
out[n] = .{ .type = .Switch, .data = slot };
|
|
3536
|
+
out[n] = .{ .type = .Switch, .data = @intCast(slot) };
|
|
3537
3537
|
n += 1;
|
|
3538
3538
|
}
|
|
3539
3539
|
|
|
@@ -3566,7 +3566,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3566
3566
|
const struggle =
|
|
3567
3567
|
m.id == .Bide and (m.pp == 0 or active.volatiles.disable.move == slot);
|
|
3568
3568
|
const s = if (struggle) 0 else slot;
|
|
3569
|
-
out[n] = .{ .type = .Move, .data = s };
|
|
3569
|
+
out[n] = .{ .type = .Move, .data = @intCast(s) };
|
|
3570
3570
|
n += 1;
|
|
3571
3571
|
return n;
|
|
3572
3572
|
}
|
|
@@ -3580,7 +3580,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3580
3580
|
if (m.id == .None) break;
|
|
3581
3581
|
if (m.pp == 0) continue;
|
|
3582
3582
|
if (active.volatiles.disable.move == slot) continue;
|
|
3583
|
-
out[n] = .{ .type = .Move, .data = slot };
|
|
3583
|
+
out[n] = .{ .type = .Move, .data = @intCast(slot) };
|
|
3584
3584
|
n += 1;
|
|
3585
3585
|
}
|
|
3586
3586
|
// Struggle (Pokémon Showdown would use 'move 1' here)
|
package/src/lib/napi.h
ADDED