@pkmn/engine 0.1.0-dev.c5fe6d07 → 0.1.0-dev.cdc837af
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 +46 -19
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +44 -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 +97 -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 +21 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +22 -0
- package/build/pkg/addon.js +35 -58
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +2 -2
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +3 -3
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +24 -10
- package/build/pkg/index.js +15 -10
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +123 -102
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +36 -68
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +141 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +259 -107
- package/package.json +72 -32
- package/src/bin/install-pkmn-engine +29 -19
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +12 -24
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +21 -10
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +808 -332
- package/src/lib/common/rational.zig +132 -36
- package/src/lib/common/rng.zig +22 -15
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +688 -162
- package/src/lib/gen1/chance.zig +436 -295
- 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 +8 -9
- package/src/lib/gen1/data.zig +55 -70
- package/src/lib/gen1/helpers.zig +91 -562
- package/src/lib/gen1/mechanics.zig +610 -561
- package/src/lib/gen2/calc.zig +153 -0
- package/src/lib/gen2/chance.zig +902 -0
- package/src/lib/gen2/data/items.zig +13 -21
- package/src/lib/gen2/data/moves.zig +664 -572
- package/src/lib/gen2/data/species.zig +2 -4
- package/src/lib/gen2/data/types.zig +135 -33
- package/src/lib/gen2/data.zig +115 -91
- package/src/lib/gen2/generated.zig +801 -0
- package/src/lib/gen2/helpers.zig +54 -37
- package/src/lib/gen2/mechanics.zig +3569 -29
- package/src/lib/{binding/node.zig → node.zig} +9 -14
- package/src/lib/pkmn.zig +36 -15
- package/src/lib/{binding/wasm.zig → wasm.zig} +1 -1
- package/src/tools/display/dom.ts +96 -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/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,29 +74,126 @@ 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
|
|
175
|
+
/// The precedence order of Type `type`.
|
|
176
|
+
pub fn precedence(self: Type) u8 {
|
|
99
177
|
return PRECEDENCE[@intFromEnum(self)];
|
|
100
178
|
}
|
|
179
|
+
|
|
180
|
+
/// The internal index of this Type used by Present.
|
|
181
|
+
pub fn present(self: Type) u8 {
|
|
182
|
+
return INDEXES[@intFromEnum(self)];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// The Type corresponding to a random roll of `num` for Conversion 2.
|
|
186
|
+
pub fn conversion2(num: u8) Type {
|
|
187
|
+
assert(num != 6);
|
|
188
|
+
assert(num < 10 or num >= 20);
|
|
189
|
+
assert(num <= 27);
|
|
190
|
+
return if (num < 10) CONVERSION_2[num] else @enumFromInt(num - 10);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/// The Type corresponding to a Hidden Power `index`.
|
|
194
|
+
pub fn hiddenPower(index: u8) Type {
|
|
195
|
+
return HIDDEN_POWER[index];
|
|
196
|
+
}
|
|
101
197
|
};
|
|
102
198
|
|
|
103
199
|
/// Representation of a Pokémon's typing.
|
|
@@ -112,12 +208,18 @@ pub const Types = extern struct {
|
|
|
112
208
|
}
|
|
113
209
|
|
|
114
210
|
/// Whether this typing is immune to type `t`.
|
|
115
|
-
pub
|
|
211
|
+
pub fn immune(self: Types, t: Type) bool {
|
|
116
212
|
return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
|
|
117
213
|
}
|
|
118
214
|
|
|
119
215
|
/// Whether this typing includes type `t`.
|
|
120
|
-
pub
|
|
216
|
+
pub fn includes(self: Types, t: Type) bool {
|
|
121
217
|
return self.type1 == t or self.type2 == t;
|
|
122
218
|
}
|
|
219
|
+
|
|
220
|
+
/// Whether this typing is immune to damage from Sandstorm.
|
|
221
|
+
pub fn sandstormImmune(self: Types) bool {
|
|
222
|
+
return @intFromEnum(self.type1) <= @intFromEnum(Type.Steel) or
|
|
223
|
+
@intFromEnum(self.type2) <= @intFromEnum(Type.Steel);
|
|
224
|
+
}
|
|
123
225
|
};
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -1,21 +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 rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
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");
|
|
11
8
|
const items = @import("data/items.zig");
|
|
9
|
+
const mechanics = @import("mechanics.zig");
|
|
12
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");
|
|
13
14
|
const species = @import("data/species.zig");
|
|
15
|
+
const std = @import("std");
|
|
14
16
|
const types = @import("data/types.zig");
|
|
15
|
-
|
|
16
|
-
const gen1 = @import("../gen1/data.zig");
|
|
17
|
-
|
|
18
|
-
const mechanics = @import("mechanics.zig");
|
|
17
|
+
const util = @import("../common/util.zig");
|
|
19
18
|
|
|
20
19
|
const assert = std.debug.assert;
|
|
21
20
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -33,18 +32,23 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
|
33
32
|
else
|
|
34
33
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
35
34
|
|
|
35
|
+
/// Null object pattern implementation of pkmn.battle.Options for Generation II.
|
|
36
|
+
pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
|
|
37
|
+
|
|
36
38
|
const Choice = data.Choice;
|
|
37
39
|
const ID = data.ID;
|
|
38
40
|
const Player = data.Player;
|
|
39
41
|
const Result = data.Result;
|
|
40
42
|
|
|
41
43
|
const PointerType = util.PointerType;
|
|
44
|
+
const isPointerTo = util.isPointerTo;
|
|
42
45
|
|
|
43
46
|
const showdown = pkmn.options.showdown;
|
|
44
47
|
|
|
45
48
|
pub const PRNG = rng.PRNG(2);
|
|
46
49
|
|
|
47
50
|
pub fn Battle(comptime RNG: anytype) type {
|
|
51
|
+
const padding = if (!showdown) 3 else 5;
|
|
48
52
|
return extern struct {
|
|
49
53
|
const Self = @This();
|
|
50
54
|
|
|
@@ -53,17 +57,19 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
53
57
|
turn: u16 = 0,
|
|
54
58
|
field: Field = .{},
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
_: [padding]u8 = .{0} ** padding,
|
|
61
|
+
|
|
62
|
+
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
63
|
+
assert(isPointerTo(self, Self));
|
|
58
64
|
return &self.sides[@intFromEnum(player)];
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
pub
|
|
62
|
-
assert(
|
|
67
|
+
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
68
|
+
assert(isPointerTo(self, Self));
|
|
63
69
|
return &self.sides[@intFromEnum(player.foe())];
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
pub
|
|
72
|
+
pub fn active(self: *const Self, player: Player) ID {
|
|
67
73
|
return player.ident(@intCast(self.side(player).pokemon[0].position));
|
|
68
74
|
}
|
|
69
75
|
|
|
@@ -77,35 +83,32 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
77
83
|
};
|
|
78
84
|
}
|
|
79
85
|
|
|
80
|
-
// TODO
|
|
81
86
|
test Battle {
|
|
82
|
-
try expectEqual(
|
|
87
|
+
try expectEqual(512, @sizeOf(Battle(PRNG)));
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
const Field = packed struct {
|
|
90
|
+
pub const Field = packed struct(u8) {
|
|
86
91
|
weather: Weather = .None,
|
|
87
92
|
weather_duration: u4 = 0,
|
|
88
|
-
|
|
89
|
-
comptime {
|
|
90
|
-
assert(@sizeOf(Field) == 1);
|
|
91
|
-
}
|
|
92
93
|
};
|
|
93
94
|
|
|
94
|
-
const Weather = enum(u4) {
|
|
95
|
+
pub const Weather = enum(u4) {
|
|
95
96
|
None,
|
|
96
97
|
Rain,
|
|
97
98
|
Sun,
|
|
98
99
|
Sandstorm,
|
|
99
100
|
};
|
|
100
101
|
|
|
101
|
-
const Side = extern struct {
|
|
102
|
+
pub const Side = extern struct {
|
|
102
103
|
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
103
|
-
active: ActivePokemon = .{},
|
|
104
|
-
conditions: Conditions,
|
|
104
|
+
active: ActivePokemon align(8) = .{},
|
|
105
|
+
conditions: Conditions = .{},
|
|
106
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
105
107
|
last_used_move: Move = .None,
|
|
106
|
-
_: u8 = 0,
|
|
107
108
|
|
|
108
|
-
|
|
109
|
+
_: [7]u8 = .{0} ** 7,
|
|
110
|
+
|
|
111
|
+
const Conditions = packed struct(u32) {
|
|
109
112
|
Spikes: bool = false,
|
|
110
113
|
Safeguard: bool = false,
|
|
111
114
|
LightScreen: bool = false,
|
|
@@ -115,53 +118,69 @@ const Side = extern struct {
|
|
|
115
118
|
light_screen: u4 = 0,
|
|
116
119
|
reflect: u4 = 0,
|
|
117
120
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
future_sight: FutureSight = .{},
|
|
122
|
+
|
|
123
|
+
const FutureSight = packed struct(u16) {
|
|
124
|
+
damage: u12 = 0,
|
|
125
|
+
count: u4 = 0,
|
|
126
|
+
};
|
|
121
127
|
};
|
|
122
128
|
|
|
123
129
|
comptime {
|
|
124
|
-
assert(@sizeOf(Side) ==
|
|
130
|
+
assert(@sizeOf(Side) == 248);
|
|
125
131
|
}
|
|
126
132
|
|
|
127
|
-
pub
|
|
128
|
-
assert(
|
|
133
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
134
|
+
assert(isPointerTo(self, Side));
|
|
129
135
|
assert(slot > 0 and slot <= 6);
|
|
130
136
|
const id = self.pokemon[slot - 1].position;
|
|
131
137
|
assert(id > 0 and id <= 6);
|
|
132
138
|
return &self.pokemon[id - 1];
|
|
133
139
|
}
|
|
134
140
|
|
|
135
|
-
pub
|
|
136
|
-
assert(
|
|
141
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
142
|
+
assert(isPointerTo(self, Side));
|
|
137
143
|
return self.get(1);
|
|
138
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
|
+
// }
|
|
139
158
|
};
|
|
140
159
|
|
|
141
160
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
142
|
-
const ActivePokemon = extern struct {
|
|
143
|
-
volatiles:
|
|
161
|
+
pub const ActivePokemon = extern struct {
|
|
162
|
+
volatiles: Volatiles align(4) = .{},
|
|
144
163
|
stats: Stats(u16) = .{},
|
|
145
164
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
146
165
|
boosts: Boosts = .{},
|
|
166
|
+
types: Types = .{},
|
|
147
167
|
species: Species = .None,
|
|
148
|
-
|
|
149
|
-
last_move: Move = .None,
|
|
168
|
+
|
|
150
169
|
_: u8 = 0,
|
|
151
170
|
|
|
152
171
|
comptime {
|
|
153
172
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
154
173
|
}
|
|
155
174
|
|
|
156
|
-
pub
|
|
157
|
-
assert(
|
|
175
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
176
|
+
assert(isPointerTo(self, Pokemon));
|
|
158
177
|
assert(mslot > 0 and mslot <= 4);
|
|
159
178
|
assert(self.moves[mslot - 1].id != .None);
|
|
160
179
|
return &self.moves[mslot - 1];
|
|
161
180
|
}
|
|
162
181
|
};
|
|
163
182
|
|
|
164
|
-
const Pokemon = extern struct {
|
|
183
|
+
pub const Pokemon = extern struct {
|
|
165
184
|
stats: Stats(u16) = .{},
|
|
166
185
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
167
186
|
types: Types = .{},
|
|
@@ -178,8 +197,8 @@ const Pokemon = extern struct {
|
|
|
178
197
|
assert(@sizeOf(Pokemon) == 32);
|
|
179
198
|
}
|
|
180
199
|
|
|
181
|
-
pub
|
|
182
|
-
assert(
|
|
200
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
201
|
+
assert(isPointerTo(self, Pokemon));
|
|
183
202
|
assert(mslot > 0 and mslot <= 4);
|
|
184
203
|
assert(self.moves[mslot - 1].id != .None);
|
|
185
204
|
return &self.moves[mslot - 1];
|
|
@@ -192,7 +211,7 @@ pub const Gender = enum(u2) {
|
|
|
192
211
|
Unknown,
|
|
193
212
|
};
|
|
194
213
|
|
|
195
|
-
const DVs = packed struct {
|
|
214
|
+
pub const DVs = packed struct(u16) {
|
|
196
215
|
gender: Gender = .Male,
|
|
197
216
|
pow: u6 = 40,
|
|
198
217
|
type: Type = .Dark,
|
|
@@ -200,10 +219,10 @@ const DVs = packed struct {
|
|
|
200
219
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
201
220
|
assert(p >= 1 and p <= 40);
|
|
202
221
|
assert(t != .Normal and t != .@"???");
|
|
203
|
-
return
|
|
222
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
204
223
|
}
|
|
205
224
|
|
|
206
|
-
pub
|
|
225
|
+
pub fn power(dvs: DVs) u8 {
|
|
207
226
|
return @as(u8, dvs.pow) + 30;
|
|
208
227
|
}
|
|
209
228
|
|
|
@@ -217,13 +236,8 @@ const DVs = packed struct {
|
|
|
217
236
|
};
|
|
218
237
|
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
219
238
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
comptime {
|
|
226
|
-
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) };
|
|
227
241
|
}
|
|
228
242
|
};
|
|
229
243
|
|
|
@@ -248,10 +262,12 @@ test DVs {
|
|
|
248
262
|
try expectEqual(Gender.Female, dvs.gender);
|
|
249
263
|
}
|
|
250
264
|
|
|
251
|
-
|
|
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.
|
|
252
268
|
id: Move = .None,
|
|
253
|
-
|
|
254
|
-
pp:
|
|
269
|
+
/// The remaining PP of the move.
|
|
270
|
+
pp: u8 = 0,
|
|
255
271
|
|
|
256
272
|
comptime {
|
|
257
273
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -260,14 +276,19 @@ const MoveSlot = packed struct {
|
|
|
260
276
|
|
|
261
277
|
pub const Status = gen1.Status;
|
|
262
278
|
|
|
263
|
-
|
|
279
|
+
/// TODO
|
|
280
|
+
pub const TriAttack = enum(u2) { PAR, FRZ, BRN };
|
|
281
|
+
|
|
282
|
+
pub const Volatiles = packed struct(u128) {
|
|
264
283
|
Bide: bool = false,
|
|
265
284
|
Thrashing: bool = false,
|
|
285
|
+
BeatUp: bool = false,
|
|
266
286
|
Flinch: bool = false,
|
|
267
287
|
Charging: bool = false,
|
|
268
288
|
Underground: bool = false,
|
|
269
289
|
Flying: bool = false,
|
|
270
290
|
Confusion: bool = false,
|
|
291
|
+
|
|
271
292
|
Mist: bool = false,
|
|
272
293
|
FocusEnergy: bool = false,
|
|
273
294
|
Substitute: bool = false,
|
|
@@ -278,52 +299,55 @@ const Volatile = packed struct {
|
|
|
278
299
|
Transform: bool = false,
|
|
279
300
|
|
|
280
301
|
Nightmare: bool = false,
|
|
281
|
-
|
|
302
|
+
Curse: bool = false,
|
|
282
303
|
Protect: bool = false,
|
|
283
304
|
Foresight: bool = false,
|
|
284
305
|
PerishSong: bool = false,
|
|
285
306
|
Endure: bool = false,
|
|
286
307
|
Rollout: bool = false,
|
|
287
308
|
Attract: bool = false,
|
|
309
|
+
|
|
288
310
|
DefenseCurl: bool = false,
|
|
289
311
|
Encore: bool = false,
|
|
290
312
|
LockOn: bool = false,
|
|
291
313
|
DestinyBond: bool = false,
|
|
292
|
-
|
|
314
|
+
Trapped: bool = false,
|
|
293
315
|
|
|
294
|
-
trapped: bool = false,
|
|
295
316
|
switched: bool = false,
|
|
317
|
+
// TODO: replace with simply looking at Choice on Battle
|
|
296
318
|
switching: bool = false,
|
|
319
|
+
// TODO: simply store both last moves on side
|
|
320
|
+
dirty: bool = false,
|
|
297
321
|
|
|
298
|
-
|
|
322
|
+
frozen: bool = false,
|
|
323
|
+
minimized: bool = false,
|
|
324
|
+
_: u10 = 0,
|
|
325
|
+
// TODO: is this needed for unmodified stats?
|
|
326
|
+
transform: u4 = 0,
|
|
299
327
|
|
|
300
|
-
bind: u4 = 0,
|
|
301
|
-
future_sight: FutureSight = .{},
|
|
302
328
|
bide: u16 = 0,
|
|
303
|
-
|
|
329
|
+
bind: Bind = .{},
|
|
330
|
+
disable: Disable = .{},
|
|
304
331
|
rage: u8 = 0,
|
|
305
332
|
substitute: u8 = 0,
|
|
306
333
|
toxic: u8 = 0,
|
|
307
334
|
confusion: u4 = 0,
|
|
308
335
|
encore: u4 = 0,
|
|
336
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
309
337
|
fury_cutter: u4 = 0,
|
|
310
338
|
perish_song: u4 = 0,
|
|
311
339
|
protect: u4 = 0,
|
|
312
|
-
|
|
340
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
313
341
|
|
|
314
|
-
const
|
|
315
|
-
move: u4 = 0,
|
|
342
|
+
const Bind = packed struct(u8) {
|
|
316
343
|
duration: u4 = 0,
|
|
344
|
+
reason: u4 = 0,
|
|
317
345
|
};
|
|
318
346
|
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
347
|
+
const Disable = packed struct(u8) {
|
|
348
|
+
move: u4 = 0,
|
|
349
|
+
duration: u4 = 0,
|
|
322
350
|
};
|
|
323
|
-
|
|
324
|
-
comptime {
|
|
325
|
-
assert(@sizeOf(Volatile) == 16);
|
|
326
|
-
}
|
|
327
351
|
};
|
|
328
352
|
|
|
329
353
|
pub fn Stats(comptime T: type) type {
|
|
@@ -358,7 +382,7 @@ test Stats {
|
|
|
358
382
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
359
383
|
}
|
|
360
384
|
|
|
361
|
-
pub const Boosts = packed struct {
|
|
385
|
+
pub const Boosts = packed struct(u32) {
|
|
362
386
|
atk: i4 = 0,
|
|
363
387
|
def: i4 = 0,
|
|
364
388
|
spe: i4 = 0,
|
|
@@ -374,7 +398,7 @@ pub const Boosts = packed struct {
|
|
|
374
398
|
};
|
|
375
399
|
|
|
376
400
|
test Boosts {
|
|
377
|
-
const boosts =
|
|
401
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
378
402
|
try expectEqual(0, boosts.atk);
|
|
379
403
|
try expectEqual(-6, boosts.spd);
|
|
380
404
|
}
|
|
@@ -387,15 +411,10 @@ test Item {
|
|
|
387
411
|
try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
|
|
388
412
|
try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
|
|
389
413
|
|
|
390
|
-
try expect(!Item.
|
|
391
|
-
try expect(Item.
|
|
392
|
-
try expect(Item.
|
|
393
|
-
try expect(!Item.
|
|
394
|
-
|
|
395
|
-
try expect(!Item.berry(.LightBall));
|
|
396
|
-
try expect(Item.berry(.PSNCureBerry));
|
|
397
|
-
try expect(Item.berry(.GoldBerry));
|
|
398
|
-
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));
|
|
399
418
|
}
|
|
400
419
|
|
|
401
420
|
pub const Move = moves.Move;
|
|
@@ -404,8 +423,13 @@ test Move {
|
|
|
404
423
|
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
405
424
|
const move = Move.get(.DynamicPunch);
|
|
406
425
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
407
|
-
try expectEqual(
|
|
408
|
-
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());
|
|
409
433
|
}
|
|
410
434
|
|
|
411
435
|
pub const Species = species.Species;
|