@pkmn/engine 0.1.0-dev.a02611a4 → 0.1.0-dev.a15b6c17
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 +63 -26
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +110 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +26 -4
- package/build/pkg/addon.js +39 -71
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +17 -4
- package/build/pkg/data/protocol.json +84 -13
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +15 -18
- package/build/pkg/gen1.js +30 -21
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +42 -91
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +29 -1
- package/build/pkg/protocol.js +123 -102
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +84 -73
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +66 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +360 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +157 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +275 -122
- package/package.json +64 -123
- package/src/bin/install-pkmn-engine +47 -21
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +200 -0
- package/src/lib/{binding → bindings}/node.zig +15 -32
- package/src/lib/bindings/wasm.zig +50 -0
- package/src/lib/common/array.zig +70 -0
- package/src/lib/common/data.zig +12 -24
- package/src/lib/common/debug.zig +33 -28
- package/src/lib/common/js.zig +3 -7
- package/src/lib/common/optional.zig +19 -18
- package/src/lib/common/options.zig +6 -0
- package/src/lib/common/protocol.zig +927 -478
- package/src/lib/common/rational.zig +341 -254
- package/src/lib/common/rng.zig +22 -15
- package/src/lib/common/util.zig +33 -1
- package/src/lib/gen1/calc.zig +726 -177
- package/src/lib/gen1/chance.zig +484 -306
- package/src/lib/gen1/data/moves.zig +193 -183
- package/src/lib/gen1/data/species.zig +4 -6
- package/src/lib/gen1/data/types.zig +9 -10
- package/src/lib/gen1/data.zig +60 -75
- package/src/lib/gen1/helpers.zig +90 -563
- package/src/lib/gen1/mechanics.zig +696 -614
- package/src/lib/gen2/calc.zig +150 -0
- package/src/lib/gen2/chance.zig +896 -0
- package/src/lib/gen2/data/items.zig +13 -21
- package/src/lib/gen2/data/moves.zig +662 -583
- package/src/lib/gen2/data/species.zig +2 -4
- package/src/lib/gen2/data/types.zig +139 -34
- package/src/lib/gen2/data.zig +114 -96
- package/src/lib/gen2/generated.zig +801 -0
- package/src/lib/gen2/helpers.zig +49 -35
- package/src/lib/gen2/mechanics.zig +3324 -115
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +14 -0
- package/src/lib/pkmn.zig +125 -29
- package/src/lib/wasm.zig +13 -0
- package/src/tools/display/dom.ts +108 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/binding/wasm.zig +0 -12
- package/src/lib/gen1/README.md +0 -892
- package/src/lib/gen2/README.md +0 -308
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
//! Code generated by `tools/generate` - manual edits will be overwritten
|
|
2
|
-
|
|
3
|
-
const std = @import("std");
|
|
1
|
+
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
4
2
|
|
|
5
3
|
const gen2 = @import("../../gen2/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Stats = gen2.Stats;
|
|
10
8
|
const Types = gen2.Types;
|
|
11
9
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
//! Code generated by `tools/generate` - manual edits will be overwritten
|
|
2
|
-
|
|
3
|
-
const std = @import("std");
|
|
1
|
+
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
4
2
|
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
7
|
const Effectiveness = gen1.Effectiveness;
|
|
@@ -14,15 +13,15 @@ const I = Effectiveness.Immune;
|
|
|
14
13
|
|
|
15
14
|
/// Representation of a Generation II type in Pokémon.
|
|
16
15
|
pub const Type = enum(u8) {
|
|
16
|
+
Ground,
|
|
17
|
+
Rock,
|
|
18
|
+
Steel,
|
|
17
19
|
Normal,
|
|
18
20
|
Fighting,
|
|
19
21
|
Flying,
|
|
20
22
|
Poison,
|
|
21
|
-
Ground,
|
|
22
|
-
Rock,
|
|
23
23
|
Bug,
|
|
24
24
|
Ghost,
|
|
25
|
-
Steel,
|
|
26
25
|
@"???",
|
|
27
26
|
Fire,
|
|
28
27
|
Water,
|
|
@@ -33,37 +32,37 @@ pub const Type = enum(u8) {
|
|
|
33
32
|
Dragon,
|
|
34
33
|
Dark,
|
|
35
34
|
|
|
36
|
-
const CHART
|
|
37
|
-
[_]Effectiveness{ N,
|
|
38
|
-
[_]Effectiveness{
|
|
39
|
-
[_]Effectiveness{ N, S,
|
|
40
|
-
[_]Effectiveness{ N,
|
|
41
|
-
[_]Effectiveness{ N,
|
|
42
|
-
[_]Effectiveness{ N, R,
|
|
43
|
-
[_]Effectiveness{
|
|
44
|
-
[_]Effectiveness{
|
|
45
|
-
[_]Effectiveness{ N, N,
|
|
35
|
+
const CHART: [18][18]Effectiveness = .{
|
|
36
|
+
[_]Effectiveness{ N, S, S, N, N, I, S, R, N, N, S, N, R, S, N, N, N, N }, // Ground
|
|
37
|
+
[_]Effectiveness{ R, N, R, N, R, S, N, S, N, N, S, N, N, N, N, S, N, N }, // Rock
|
|
38
|
+
[_]Effectiveness{ N, S, R, N, N, N, N, N, N, N, R, R, N, R, N, S, N, N }, // Steel
|
|
39
|
+
[_]Effectiveness{ N, R, R, N, N, N, N, N, I, N, N, N, N, N, N, N, N, N }, // Normal
|
|
40
|
+
[_]Effectiveness{ N, S, S, S, N, R, R, R, I, N, N, N, N, N, R, S, N, S }, // Fighting
|
|
41
|
+
[_]Effectiveness{ N, R, R, N, S, N, N, S, N, N, N, N, S, R, N, N, N, N }, // Flying
|
|
42
|
+
[_]Effectiveness{ R, R, I, N, N, N, R, N, R, N, N, N, S, N, N, N, N, N }, // Poison
|
|
43
|
+
[_]Effectiveness{ N, N, R, N, R, R, R, N, R, N, R, N, S, N, S, N, N, S }, // Bug
|
|
44
|
+
[_]Effectiveness{ N, N, R, I, N, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Ghost
|
|
46
45
|
[_]Effectiveness{ N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N }, // ???
|
|
47
|
-
[_]Effectiveness{ N,
|
|
48
|
-
[_]Effectiveness{
|
|
49
|
-
[_]Effectiveness{
|
|
50
|
-
[_]Effectiveness{
|
|
51
|
-
[_]Effectiveness{ N,
|
|
52
|
-
[_]Effectiveness{
|
|
53
|
-
[_]Effectiveness{ N, N,
|
|
54
|
-
[_]Effectiveness{ N, R, N,
|
|
46
|
+
[_]Effectiveness{ N, R, S, N, N, N, N, S, N, N, R, R, S, N, N, S, R, N }, // Fire
|
|
47
|
+
[_]Effectiveness{ S, S, N, N, N, N, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
|
|
48
|
+
[_]Effectiveness{ S, S, R, N, N, R, R, R, N, N, R, S, R, N, N, N, R, N }, // Grass
|
|
49
|
+
[_]Effectiveness{ I, N, N, N, N, S, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
|
|
50
|
+
[_]Effectiveness{ N, N, R, N, S, N, S, N, N, N, N, N, N, N, R, N, N, I }, // Psychic
|
|
51
|
+
[_]Effectiveness{ S, N, R, N, N, S, N, N, N, N, R, R, S, N, N, R, S, N }, // Ice
|
|
52
|
+
[_]Effectiveness{ N, N, R, N, N, N, N, N, N, N, N, N, N, N, N, N, S, N }, // Dragon
|
|
53
|
+
[_]Effectiveness{ N, N, R, N, R, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Dark
|
|
55
54
|
};
|
|
56
55
|
|
|
57
56
|
const PRECEDENCE = [_]u8{
|
|
57
|
+
9, // Ground
|
|
58
|
+
13, // Rock
|
|
59
|
+
17, // Steel
|
|
58
60
|
1, // Normal
|
|
59
61
|
7, // Fighting
|
|
60
62
|
10, // Flying
|
|
61
63
|
8, // Poison
|
|
62
|
-
9, // Ground
|
|
63
|
-
13, // Rock
|
|
64
64
|
12, // Bug
|
|
65
65
|
14, // Ghost
|
|
66
|
-
17, // Steel
|
|
67
66
|
0, // ???
|
|
68
67
|
2, // Fire
|
|
69
68
|
3, // Water
|
|
@@ -75,28 +74,128 @@ pub const Type = enum(u8) {
|
|
|
75
74
|
16, // Dark
|
|
76
75
|
};
|
|
77
76
|
|
|
77
|
+
const INDEXES = [_]u8{
|
|
78
|
+
4, // Ground
|
|
79
|
+
5, // Rock
|
|
80
|
+
9, // Steel
|
|
81
|
+
0, // Normal
|
|
82
|
+
1, // Fighting
|
|
83
|
+
2, // Flying
|
|
84
|
+
3, // Poison
|
|
85
|
+
7, // Bug
|
|
86
|
+
8, // Ghost
|
|
87
|
+
19, // ???
|
|
88
|
+
20, // Fire
|
|
89
|
+
21, // Water
|
|
90
|
+
22, // Grass
|
|
91
|
+
23, // Electric
|
|
92
|
+
24, // Psychic
|
|
93
|
+
25, // Ice
|
|
94
|
+
26, // Dragon
|
|
95
|
+
27, // Dark
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const CONVERSION_2 = [_]Type{
|
|
99
|
+
.Normal,
|
|
100
|
+
.Fighting,
|
|
101
|
+
.Flying,
|
|
102
|
+
.Poison,
|
|
103
|
+
.Ground,
|
|
104
|
+
.Rock,
|
|
105
|
+
.@"???", // placeholder
|
|
106
|
+
.Bug,
|
|
107
|
+
.Ghost,
|
|
108
|
+
.Steel,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const HIDDEN_POWER = [_]Type{
|
|
112
|
+
.Fighting,
|
|
113
|
+
.Flying,
|
|
114
|
+
.Poison,
|
|
115
|
+
.Ground,
|
|
116
|
+
.Rock,
|
|
117
|
+
.Bug,
|
|
118
|
+
.Ghost,
|
|
119
|
+
.Steel,
|
|
120
|
+
.Fire,
|
|
121
|
+
.Water,
|
|
122
|
+
.Grass,
|
|
123
|
+
.Electric,
|
|
124
|
+
.Psychic,
|
|
125
|
+
.Ice,
|
|
126
|
+
.Dragon,
|
|
127
|
+
.Dark,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/// Order of Pokémon Showdown's types.
|
|
131
|
+
pub const SHOWDOWN = [_]Type{
|
|
132
|
+
.Fire,
|
|
133
|
+
.Ice,
|
|
134
|
+
.Steel,
|
|
135
|
+
.Electric,
|
|
136
|
+
.Ghost,
|
|
137
|
+
.Grass,
|
|
138
|
+
.Dark,
|
|
139
|
+
.Bug,
|
|
140
|
+
.Dragon,
|
|
141
|
+
.Fighting,
|
|
142
|
+
.Flying,
|
|
143
|
+
.Ground,
|
|
144
|
+
.Normal,
|
|
145
|
+
.Poison,
|
|
146
|
+
.Psychic,
|
|
147
|
+
.Rock,
|
|
148
|
+
.Water,
|
|
149
|
+
.@"???",
|
|
150
|
+
};
|
|
151
|
+
|
|
78
152
|
comptime {
|
|
79
153
|
assert(@bitSizeOf(Type) == 8);
|
|
80
154
|
assert(@sizeOf(@TypeOf(CHART)) == 324);
|
|
81
155
|
assert(@sizeOf(@TypeOf(PRECEDENCE)) == 18);
|
|
156
|
+
assert(@sizeOf(@TypeOf(INDEXES)) == 18);
|
|
157
|
+
assert(@sizeOf(@TypeOf(CONVERSION_2)) == 10);
|
|
158
|
+
assert(@sizeOf(@TypeOf(HIDDEN_POWER)) == 16);
|
|
159
|
+
assert(@sizeOf(@TypeOf(SHOWDOWN)) == 18);
|
|
82
160
|
}
|
|
83
161
|
|
|
84
162
|
/// The number of types in this generation.
|
|
85
163
|
pub const size = 18;
|
|
86
164
|
|
|
87
165
|
/// Whether or not this type is considered to be special as opposed to physical.
|
|
88
|
-
pub
|
|
166
|
+
pub fn special(self: Type) bool {
|
|
89
167
|
return @intFromEnum(self) >= @intFromEnum(Type.Fire);
|
|
90
168
|
}
|
|
91
169
|
|
|
92
170
|
/// The `Effectiveness` of type `t2` vs. type `t1`.
|
|
93
|
-
pub
|
|
171
|
+
pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
|
|
94
172
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
95
173
|
}
|
|
96
174
|
|
|
97
|
-
/// The precedence order of
|
|
98
|
-
pub
|
|
99
|
-
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)];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// The internal index of this Type used by Present.
|
|
184
|
+
pub fn present(self: Type) u8 {
|
|
185
|
+
return INDEXES[@intFromEnum(self)];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// The Type corresponding to a random roll of `num` for Conversion 2.
|
|
189
|
+
pub fn conversion2(num: u8) Type {
|
|
190
|
+
assert(num != 6);
|
|
191
|
+
assert(num < 10 or num >= 20);
|
|
192
|
+
assert(num <= 27);
|
|
193
|
+
return if (num < 10) CONVERSION_2[num] else @enumFromInt(num - 10);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/// The Type corresponding to a Hidden Power `index`.
|
|
197
|
+
pub fn hiddenPower(index: u8) Type {
|
|
198
|
+
return HIDDEN_POWER[index];
|
|
100
199
|
}
|
|
101
200
|
};
|
|
102
201
|
|
|
@@ -112,12 +211,18 @@ pub const Types = extern struct {
|
|
|
112
211
|
}
|
|
113
212
|
|
|
114
213
|
/// Whether this typing is immune to type `t`.
|
|
115
|
-
pub
|
|
214
|
+
pub fn immune(self: Types, t: Type) bool {
|
|
116
215
|
return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
|
|
117
216
|
}
|
|
118
217
|
|
|
119
218
|
/// Whether this typing includes type `t`.
|
|
120
|
-
pub
|
|
219
|
+
pub fn includes(self: Types, t: Type) bool {
|
|
121
220
|
return self.type1 == t or self.type2 == t;
|
|
122
221
|
}
|
|
222
|
+
|
|
223
|
+
/// Whether this typing is immune to damage from Sandstorm.
|
|
224
|
+
pub fn sandstormImmune(self: Types) bool {
|
|
225
|
+
return @intFromEnum(self.type1) <= @intFromEnum(Type.Steel) or
|
|
226
|
+
@intFromEnum(self.type2) <= @intFromEnum(Type.Steel);
|
|
227
|
+
}
|
|
123
228
|
};
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const pkmn = @import("../pkmn.zig");
|
|
5
|
-
|
|
6
|
-
const data = @import("../common/data.zig");
|
|
7
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
8
|
-
const protocol = @import("../common/protocol.zig");
|
|
9
|
-
const rng = @import("../common/rng.zig");
|
|
10
|
-
const util = @import("../common/util.zig");
|
|
11
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const calc = @import("calc.zig");
|
|
5
|
+
const chance = @import("chance.zig");
|
|
6
|
+
const data = @import("../common/data.zig");
|
|
7
|
+
const gen1 = @import("../gen1/data.zig");
|
|
12
8
|
const items = @import("data/items.zig");
|
|
9
|
+
const mechanics = @import("mechanics.zig");
|
|
13
10
|
const moves = @import("data/moves.zig");
|
|
11
|
+
const pkmn = @import("../pkmn.zig");
|
|
12
|
+
const protocol = @import("../common/protocol.zig");
|
|
13
|
+
const rng = @import("../common/rng.zig");
|
|
14
14
|
const species = @import("data/species.zig");
|
|
15
|
+
const std = @import("std");
|
|
15
16
|
const types = @import("data/types.zig");
|
|
16
|
-
|
|
17
|
-
const gen1 = @import("../gen1/data.zig");
|
|
18
|
-
|
|
19
|
-
const calc = @import("calc.zig");
|
|
20
|
-
const chance = @import("chance.zig");
|
|
21
|
-
const mechanics = @import("mechanics.zig");
|
|
17
|
+
const util = @import("../common/util.zig");
|
|
22
18
|
|
|
23
19
|
const assert = std.debug.assert;
|
|
24
20
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -45,12 +41,14 @@ const Player = data.Player;
|
|
|
45
41
|
const Result = data.Result;
|
|
46
42
|
|
|
47
43
|
const PointerType = util.PointerType;
|
|
44
|
+
const isPointerTo = util.isPointerTo;
|
|
48
45
|
|
|
49
46
|
const showdown = pkmn.options.showdown;
|
|
50
47
|
|
|
51
48
|
pub const PRNG = rng.PRNG(2);
|
|
52
49
|
|
|
53
50
|
pub fn Battle(comptime RNG: anytype) type {
|
|
51
|
+
const padding = if (!showdown) 3 else 5;
|
|
54
52
|
return extern struct {
|
|
55
53
|
const Self = @This();
|
|
56
54
|
|
|
@@ -59,17 +57,19 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
59
57
|
turn: u16 = 0,
|
|
60
58
|
field: Field = .{},
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
_: [padding]u8 = @splat(0),
|
|
61
|
+
|
|
62
|
+
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
63
|
+
assert(isPointerTo(self, Self));
|
|
64
64
|
return &self.sides[@intFromEnum(player)];
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
pub
|
|
68
|
-
assert(
|
|
67
|
+
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
68
|
+
assert(isPointerTo(self, Self));
|
|
69
69
|
return &self.sides[@intFromEnum(player.foe())];
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
pub
|
|
72
|
+
pub fn active(self: *const Self, player: Player) ID {
|
|
73
73
|
return player.ident(@intCast(self.side(player).pokemon[0].position));
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -83,18 +83,13 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// TODO
|
|
87
86
|
test Battle {
|
|
88
|
-
try expectEqual(
|
|
87
|
+
try expectEqual(512, @sizeOf(Battle(PRNG)));
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
pub const Field = packed struct {
|
|
90
|
+
pub const Field = packed struct(u8) {
|
|
92
91
|
weather: Weather = .None,
|
|
93
92
|
weather_duration: u4 = 0,
|
|
94
|
-
|
|
95
|
-
comptime {
|
|
96
|
-
assert(@sizeOf(Field) == 1);
|
|
97
|
-
}
|
|
98
93
|
};
|
|
99
94
|
|
|
100
95
|
pub const Weather = enum(u4) {
|
|
@@ -105,15 +100,15 @@ pub const Weather = enum(u4) {
|
|
|
105
100
|
};
|
|
106
101
|
|
|
107
102
|
pub const Side = extern struct {
|
|
108
|
-
pokemon: [6]Pokemon =
|
|
109
|
-
active: ActivePokemon = .{},
|
|
103
|
+
pokemon: [6]Pokemon = @splat(.{}),
|
|
104
|
+
active: ActivePokemon align(8) = .{},
|
|
110
105
|
conditions: Conditions = .{},
|
|
111
|
-
// NB: wLastPlayerMove
|
|
112
|
-
|
|
113
|
-
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove
|
|
114
|
-
last_counter_move: Move = .None,
|
|
106
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
107
|
+
last_used_move: Move = .None,
|
|
115
108
|
|
|
116
|
-
|
|
109
|
+
_: [7]u8 = @splat(0),
|
|
110
|
+
|
|
111
|
+
const Conditions = packed struct(u32) {
|
|
117
112
|
Spikes: bool = false,
|
|
118
113
|
Safeguard: bool = false,
|
|
119
114
|
LightScreen: bool = false,
|
|
@@ -123,45 +118,62 @@ pub const Side = extern struct {
|
|
|
123
118
|
light_screen: u4 = 0,
|
|
124
119
|
reflect: u4 = 0,
|
|
125
120
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
future_sight: FutureSight = .{},
|
|
122
|
+
|
|
123
|
+
const FutureSight = packed struct(u16) {
|
|
124
|
+
damage: u12 = 0,
|
|
125
|
+
count: u4 = 0,
|
|
126
|
+
};
|
|
129
127
|
};
|
|
130
128
|
|
|
131
129
|
comptime {
|
|
132
|
-
assert(@sizeOf(Side) ==
|
|
130
|
+
assert(@sizeOf(Side) == 248);
|
|
133
131
|
}
|
|
134
132
|
|
|
135
|
-
pub
|
|
136
|
-
assert(
|
|
133
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
134
|
+
assert(isPointerTo(self, Side));
|
|
137
135
|
assert(slot > 0 and slot <= 6);
|
|
138
136
|
const id = self.pokemon[slot - 1].position;
|
|
139
137
|
assert(id > 0 and id <= 6);
|
|
140
138
|
return &self.pokemon[id - 1];
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
pub
|
|
144
|
-
assert(
|
|
141
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
142
|
+
assert(isPointerTo(self, Side));
|
|
145
143
|
return self.get(1);
|
|
146
144
|
}
|
|
145
|
+
|
|
146
|
+
pub fn lastMove(self: anytype, encore: bool) Move {
|
|
147
|
+
assert(self.last_used_move != .None or !self.active.volatiles.dirty);
|
|
148
|
+
if (encore) return self.last_used_move;
|
|
149
|
+
return if (self.active.volatiles.dirty) .None else self.last_used_move;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// TODO
|
|
153
|
+
// pub fn useMove(self: anytype) void {
|
|
154
|
+
// assert(side.last_selected_move != .None);
|
|
155
|
+
// self.last_used_move = side.last_selected_move;
|
|
156
|
+
// self.active.volatiles.dirty = false;
|
|
157
|
+
// }
|
|
147
158
|
};
|
|
148
159
|
|
|
149
160
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
150
161
|
pub const ActivePokemon = extern struct {
|
|
151
162
|
volatiles: Volatiles align(4) = .{},
|
|
152
163
|
stats: Stats(u16) = .{},
|
|
153
|
-
moves: [4]MoveSlot =
|
|
164
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
154
165
|
boosts: Boosts = .{},
|
|
155
|
-
species: Species = .None,
|
|
156
|
-
item: Item = .None,
|
|
157
166
|
types: Types = .{},
|
|
167
|
+
species: Species = .None,
|
|
168
|
+
|
|
169
|
+
_: u8 = 0,
|
|
158
170
|
|
|
159
171
|
comptime {
|
|
160
172
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
161
173
|
}
|
|
162
174
|
|
|
163
|
-
pub
|
|
164
|
-
assert(
|
|
175
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
176
|
+
assert(isPointerTo(self, Pokemon));
|
|
165
177
|
assert(mslot > 0 and mslot <= 4);
|
|
166
178
|
assert(self.moves[mslot - 1].id != .None);
|
|
167
179
|
return &self.moves[mslot - 1];
|
|
@@ -170,7 +182,7 @@ pub const ActivePokemon = extern struct {
|
|
|
170
182
|
|
|
171
183
|
pub const Pokemon = extern struct {
|
|
172
184
|
stats: Stats(u16) = .{},
|
|
173
|
-
moves: [4]MoveSlot =
|
|
185
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
174
186
|
types: Types = .{},
|
|
175
187
|
dvs: DVs = .{},
|
|
176
188
|
hp: u16 = 0,
|
|
@@ -185,8 +197,8 @@ pub const Pokemon = extern struct {
|
|
|
185
197
|
assert(@sizeOf(Pokemon) == 32);
|
|
186
198
|
}
|
|
187
199
|
|
|
188
|
-
pub
|
|
189
|
-
assert(
|
|
200
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
201
|
+
assert(isPointerTo(self, Pokemon));
|
|
190
202
|
assert(mslot > 0 and mslot <= 4);
|
|
191
203
|
assert(self.moves[mslot - 1].id != .None);
|
|
192
204
|
return &self.moves[mslot - 1];
|
|
@@ -199,7 +211,7 @@ pub const Gender = enum(u2) {
|
|
|
199
211
|
Unknown,
|
|
200
212
|
};
|
|
201
213
|
|
|
202
|
-
pub const DVs = packed struct {
|
|
214
|
+
pub const DVs = packed struct(u16) {
|
|
203
215
|
gender: Gender = .Male,
|
|
204
216
|
pow: u6 = 40,
|
|
205
217
|
type: Type = .Dark,
|
|
@@ -207,10 +219,10 @@ pub const DVs = packed struct {
|
|
|
207
219
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
208
220
|
assert(p >= 1 and p <= 40);
|
|
209
221
|
assert(t != .Normal and t != .@"???");
|
|
210
|
-
return
|
|
222
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
211
223
|
}
|
|
212
224
|
|
|
213
|
-
pub
|
|
225
|
+
pub fn power(dvs: DVs) u8 {
|
|
214
226
|
return @as(u8, dvs.pow) + 30;
|
|
215
227
|
}
|
|
216
228
|
|
|
@@ -224,13 +236,8 @@ pub const DVs = packed struct {
|
|
|
224
236
|
};
|
|
225
237
|
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
226
238
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
comptime {
|
|
233
|
-
assert(@sizeOf(DVs) == 2);
|
|
239
|
+
const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
|
|
240
|
+
return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
|
|
234
241
|
}
|
|
235
242
|
};
|
|
236
243
|
|
|
@@ -255,10 +262,12 @@ test DVs {
|
|
|
255
262
|
try expectEqual(Gender.Female, dvs.gender);
|
|
256
263
|
}
|
|
257
264
|
|
|
258
|
-
|
|
265
|
+
/// Representation of a Generation II Pokémon's move slot in a battle.
|
|
266
|
+
pub const MoveSlot = extern struct {
|
|
267
|
+
/// The identifier for the move.
|
|
259
268
|
id: Move = .None,
|
|
260
|
-
|
|
261
|
-
pp:
|
|
269
|
+
/// The remaining PP of the move.
|
|
270
|
+
pp: u8 = 0,
|
|
262
271
|
|
|
263
272
|
comptime {
|
|
264
273
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -267,14 +276,19 @@ pub const MoveSlot = packed struct {
|
|
|
267
276
|
|
|
268
277
|
pub const Status = gen1.Status;
|
|
269
278
|
|
|
270
|
-
|
|
279
|
+
/// TODO
|
|
280
|
+
pub const TriAttack = enum(u2) { PAR, FRZ, BRN };
|
|
281
|
+
|
|
282
|
+
pub const Volatiles = packed struct(u128) {
|
|
271
283
|
Bide: bool = false,
|
|
272
284
|
Thrashing: bool = false,
|
|
285
|
+
BeatUp: bool = false,
|
|
273
286
|
Flinch: bool = false,
|
|
274
287
|
Charging: bool = false,
|
|
275
288
|
Underground: bool = false,
|
|
276
289
|
Flying: bool = false,
|
|
277
290
|
Confusion: bool = false,
|
|
291
|
+
|
|
278
292
|
Mist: bool = false,
|
|
279
293
|
FocusEnergy: bool = false,
|
|
280
294
|
Substitute: bool = false,
|
|
@@ -285,52 +299,55 @@ pub const Volatiles = packed struct {
|
|
|
285
299
|
Transform: bool = false,
|
|
286
300
|
|
|
287
301
|
Nightmare: bool = false,
|
|
288
|
-
|
|
302
|
+
Curse: bool = false,
|
|
289
303
|
Protect: bool = false,
|
|
290
304
|
Foresight: bool = false,
|
|
291
305
|
PerishSong: bool = false,
|
|
292
306
|
Endure: bool = false,
|
|
293
307
|
Rollout: bool = false,
|
|
294
308
|
Attract: bool = false,
|
|
309
|
+
|
|
295
310
|
DefenseCurl: bool = false,
|
|
296
311
|
Encore: bool = false,
|
|
297
312
|
LockOn: bool = false,
|
|
298
313
|
DestinyBond: bool = false,
|
|
299
|
-
|
|
314
|
+
Trapped: bool = false,
|
|
300
315
|
|
|
301
|
-
trapped: bool = false,
|
|
302
316
|
switched: bool = false,
|
|
317
|
+
// TODO: replace with simply looking at Choice on Battle
|
|
303
318
|
switching: bool = false,
|
|
319
|
+
// TODO: simply store both last moves on side
|
|
320
|
+
dirty: bool = false,
|
|
304
321
|
|
|
305
|
-
|
|
322
|
+
frozen: bool = false,
|
|
323
|
+
minimized: bool = false,
|
|
324
|
+
_: u10 = 0,
|
|
325
|
+
// TODO: is this needed for unmodified stats?
|
|
326
|
+
transform: u4 = 0,
|
|
306
327
|
|
|
307
|
-
bind: u4 = 0,
|
|
308
|
-
future_sight: FutureSight = .{},
|
|
309
328
|
bide: u16 = 0,
|
|
310
|
-
|
|
329
|
+
bind: Bind = .{},
|
|
330
|
+
disable: Disable = .{},
|
|
311
331
|
rage: u8 = 0,
|
|
312
332
|
substitute: u8 = 0,
|
|
313
333
|
toxic: u8 = 0,
|
|
314
334
|
confusion: u4 = 0,
|
|
315
335
|
encore: u4 = 0,
|
|
336
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
316
337
|
fury_cutter: u4 = 0,
|
|
317
338
|
perish_song: u4 = 0,
|
|
318
339
|
protect: u4 = 0,
|
|
319
|
-
|
|
340
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
320
341
|
|
|
321
|
-
const
|
|
322
|
-
move: u4 = 0,
|
|
342
|
+
const Bind = packed struct(u8) {
|
|
323
343
|
duration: u4 = 0,
|
|
344
|
+
reason: u4 = 0,
|
|
324
345
|
};
|
|
325
346
|
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
347
|
+
const Disable = packed struct(u8) {
|
|
348
|
+
move: u4 = 0,
|
|
349
|
+
duration: u4 = 0,
|
|
329
350
|
};
|
|
330
|
-
|
|
331
|
-
comptime {
|
|
332
|
-
assert(@sizeOf(Volatiles) == 16);
|
|
333
|
-
}
|
|
334
351
|
};
|
|
335
352
|
|
|
336
353
|
pub fn Stats(comptime T: type) type {
|
|
@@ -365,7 +382,7 @@ test Stats {
|
|
|
365
382
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
366
383
|
}
|
|
367
384
|
|
|
368
|
-
pub const Boosts = packed struct {
|
|
385
|
+
pub const Boosts = packed struct(u32) {
|
|
369
386
|
atk: i4 = 0,
|
|
370
387
|
def: i4 = 0,
|
|
371
388
|
spe: i4 = 0,
|
|
@@ -381,7 +398,7 @@ pub const Boosts = packed struct {
|
|
|
381
398
|
};
|
|
382
399
|
|
|
383
400
|
test Boosts {
|
|
384
|
-
const boosts =
|
|
401
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
385
402
|
try expectEqual(0, boosts.atk);
|
|
386
403
|
try expectEqual(-6, boosts.spd);
|
|
387
404
|
}
|
|
@@ -394,15 +411,10 @@ test Item {
|
|
|
394
411
|
try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
|
|
395
412
|
try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
|
|
396
413
|
|
|
397
|
-
try expect(!Item.
|
|
398
|
-
try expect(Item.
|
|
399
|
-
try expect(Item.
|
|
400
|
-
try expect(!Item.
|
|
401
|
-
|
|
402
|
-
try expect(!Item.berry(.LightBall));
|
|
403
|
-
try expect(Item.berry(.PSNCureBerry));
|
|
404
|
-
try expect(Item.berry(.GoldBerry));
|
|
405
|
-
try expect(!Item.berry(.MasterBall));
|
|
414
|
+
try expect(!Item.isMail(.UpGrade));
|
|
415
|
+
try expect(Item.isMail(.FlowerMail));
|
|
416
|
+
try expect(Item.isMail(.MirageMail));
|
|
417
|
+
try expect(!Item.isMail(.TownMap));
|
|
406
418
|
}
|
|
407
419
|
|
|
408
420
|
pub const Move = moves.Move;
|
|
@@ -411,8 +423,13 @@ test Move {
|
|
|
411
423
|
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
412
424
|
const move = Move.get(.DynamicPunch);
|
|
413
425
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
414
|
-
try expectEqual(
|
|
415
|
-
try expectEqual(
|
|
426
|
+
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
427
|
+
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
428
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
429
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
430
|
+
try expect(!move.effect.isHighCritical());
|
|
431
|
+
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
432
|
+
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
416
433
|
}
|
|
417
434
|
|
|
418
435
|
pub const Species = species.Species;
|
|
@@ -432,8 +449,9 @@ test Types {
|
|
|
432
449
|
try expect(!Type.Steel.special());
|
|
433
450
|
try expect(Type.Dark.special());
|
|
434
451
|
|
|
435
|
-
try expectEqual(@as(u8, 3), Type.Water.
|
|
436
|
-
try expect(Type.
|
|
452
|
+
try expectEqual(@as(u8, 3), Type.precedence(.Water, .Water));
|
|
453
|
+
try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
|
|
454
|
+
try expect(Type.precedence(.Ice, .Fire) > Type.precedence(.Ice, .Water));
|
|
437
455
|
|
|
438
456
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|
|
439
457
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Water, .Fire));
|