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