@pkmn/engine 0.1.0-dev.c5fe6d07 → 0.1.0-dev.d1b39ff0
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 +44 -19
- package/build/pkg/addon.js +6 -7
- 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 +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +2 -2
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +3 -2
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +61 -42
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +8 -3
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +5 -4
- package/build/tools/debug.js.map +1 -1
- package/build.zig +105 -107
- package/package.json +43 -23
- package/src/bin/install-pkmn-engine +26 -18
- package/src/lib/common/data.zig +12 -24
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/optional.zig +9 -2
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +802 -327
- package/src/lib/common/rational.zig +120 -19
- package/src/lib/common/rng.zig +26 -17
- package/src/lib/gen1/calc.zig +507 -132
- package/src/lib/gen1/chance.zig +401 -286
- 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 +50 -62
- package/src/lib/gen1/helpers.zig +89 -548
- package/src/lib/gen1/mechanics.zig +569 -514
- package/src/lib/gen2/calc.zig +135 -0
- package/src/lib/gen2/chance.zig +895 -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 +106 -79
- package/src/lib/gen2/generated.zig +806 -0
- package/src/lib/gen2/helpers.zig +45 -19
- package/src/lib/gen2/mechanics.zig +3564 -9
- 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/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -892
- package/src/lib/gen2/README.md +0 -308
|
@@ -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,6 +36,9 @@ 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;
|
|
@@ -45,6 +51,7 @@ const showdown = pkmn.options.showdown;
|
|
|
45
51
|
pub const PRNG = rng.PRNG(2);
|
|
46
52
|
|
|
47
53
|
pub fn Battle(comptime RNG: anytype) type {
|
|
54
|
+
const padding = if (!showdown) 3 else 5;
|
|
48
55
|
return extern struct {
|
|
49
56
|
const Self = @This();
|
|
50
57
|
|
|
@@ -53,17 +60,19 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
53
60
|
turn: u16 = 0,
|
|
54
61
|
field: Field = .{},
|
|
55
62
|
|
|
56
|
-
|
|
63
|
+
_: [padding]u8 = .{0} ** padding,
|
|
64
|
+
|
|
65
|
+
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
57
66
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
58
67
|
return &self.sides[@intFromEnum(player)];
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
pub
|
|
70
|
+
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
62
71
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
63
72
|
return &self.sides[@intFromEnum(player.foe())];
|
|
64
73
|
}
|
|
65
74
|
|
|
66
|
-
pub
|
|
75
|
+
pub fn active(self: *const Self, player: Player) ID {
|
|
67
76
|
return player.ident(@intCast(self.side(player).pokemon[0].position));
|
|
68
77
|
}
|
|
69
78
|
|
|
@@ -77,35 +86,32 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
77
86
|
};
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
// TODO
|
|
81
89
|
test Battle {
|
|
82
|
-
try expectEqual(
|
|
90
|
+
try expectEqual(512, @sizeOf(Battle(PRNG)));
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
const Field = packed struct {
|
|
93
|
+
pub const Field = packed struct(u8) {
|
|
86
94
|
weather: Weather = .None,
|
|
87
95
|
weather_duration: u4 = 0,
|
|
88
|
-
|
|
89
|
-
comptime {
|
|
90
|
-
assert(@sizeOf(Field) == 1);
|
|
91
|
-
}
|
|
92
96
|
};
|
|
93
97
|
|
|
94
|
-
const Weather = enum(u4) {
|
|
98
|
+
pub const Weather = enum(u4) {
|
|
95
99
|
None,
|
|
96
100
|
Rain,
|
|
97
101
|
Sun,
|
|
98
102
|
Sandstorm,
|
|
99
103
|
};
|
|
100
104
|
|
|
101
|
-
const Side = extern struct {
|
|
105
|
+
pub const Side = extern struct {
|
|
102
106
|
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
103
|
-
active: ActivePokemon = .{},
|
|
104
|
-
conditions: Conditions,
|
|
107
|
+
active: ActivePokemon align(8) = .{},
|
|
108
|
+
conditions: Conditions = .{},
|
|
109
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
105
110
|
last_used_move: Move = .None,
|
|
106
|
-
_: u8 = 0,
|
|
107
111
|
|
|
108
|
-
|
|
112
|
+
_: [7]u8 = .{0} ** 7,
|
|
113
|
+
|
|
114
|
+
const Conditions = packed struct(u32) {
|
|
109
115
|
Spikes: bool = false,
|
|
110
116
|
Safeguard: bool = false,
|
|
111
117
|
LightScreen: bool = false,
|
|
@@ -115,16 +121,19 @@ const Side = extern struct {
|
|
|
115
121
|
light_screen: u4 = 0,
|
|
116
122
|
reflect: u4 = 0,
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
future_sight: FutureSight = .{},
|
|
125
|
+
|
|
126
|
+
const FutureSight = packed struct(u16) {
|
|
127
|
+
damage: u12 = 0,
|
|
128
|
+
count: u4 = 0,
|
|
129
|
+
};
|
|
121
130
|
};
|
|
122
131
|
|
|
123
132
|
comptime {
|
|
124
|
-
assert(@sizeOf(Side) ==
|
|
133
|
+
assert(@sizeOf(Side) == 248);
|
|
125
134
|
}
|
|
126
135
|
|
|
127
|
-
pub
|
|
136
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
128
137
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
129
138
|
assert(slot > 0 and slot <= 6);
|
|
130
139
|
const id = self.pokemon[slot - 1].position;
|
|
@@ -132,28 +141,41 @@ const Side = extern struct {
|
|
|
132
141
|
return &self.pokemon[id - 1];
|
|
133
142
|
}
|
|
134
143
|
|
|
135
|
-
pub
|
|
144
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
136
145
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
137
146
|
return self.get(1);
|
|
138
147
|
}
|
|
148
|
+
|
|
149
|
+
pub fn lastMove(self: anytype, encore: bool) Move {
|
|
150
|
+
assert(self.last_used_move != .None or !self.active.volatiles.dirty);
|
|
151
|
+
if (encore) return self.last_used_move;
|
|
152
|
+
return if (self.active.volatiles.dirty) .None else self.last_used_move;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// TODO
|
|
156
|
+
// pub fn useMove(self: anytype) void {
|
|
157
|
+
// assert(side.last_selected_move != .None);
|
|
158
|
+
// self.last_used_move = side.last_selected_move;
|
|
159
|
+
// self.active.volatiles.dirty = false;
|
|
160
|
+
// }
|
|
139
161
|
};
|
|
140
162
|
|
|
141
163
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
142
|
-
const ActivePokemon = extern struct {
|
|
143
|
-
volatiles:
|
|
164
|
+
pub const ActivePokemon = extern struct {
|
|
165
|
+
volatiles: Volatiles align(4) = .{},
|
|
144
166
|
stats: Stats(u16) = .{},
|
|
145
167
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
146
168
|
boosts: Boosts = .{},
|
|
169
|
+
types: Types = .{},
|
|
147
170
|
species: Species = .None,
|
|
148
|
-
|
|
149
|
-
last_move: Move = .None,
|
|
171
|
+
|
|
150
172
|
_: u8 = 0,
|
|
151
173
|
|
|
152
174
|
comptime {
|
|
153
175
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
154
176
|
}
|
|
155
177
|
|
|
156
|
-
pub
|
|
178
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
157
179
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
158
180
|
assert(mslot > 0 and mslot <= 4);
|
|
159
181
|
assert(self.moves[mslot - 1].id != .None);
|
|
@@ -161,7 +183,7 @@ const ActivePokemon = extern struct {
|
|
|
161
183
|
}
|
|
162
184
|
};
|
|
163
185
|
|
|
164
|
-
const Pokemon = extern struct {
|
|
186
|
+
pub const Pokemon = extern struct {
|
|
165
187
|
stats: Stats(u16) = .{},
|
|
166
188
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
167
189
|
types: Types = .{},
|
|
@@ -178,7 +200,7 @@ const Pokemon = extern struct {
|
|
|
178
200
|
assert(@sizeOf(Pokemon) == 32);
|
|
179
201
|
}
|
|
180
202
|
|
|
181
|
-
pub
|
|
203
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
182
204
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
183
205
|
assert(mslot > 0 and mslot <= 4);
|
|
184
206
|
assert(self.moves[mslot - 1].id != .None);
|
|
@@ -192,7 +214,7 @@ pub const Gender = enum(u2) {
|
|
|
192
214
|
Unknown,
|
|
193
215
|
};
|
|
194
216
|
|
|
195
|
-
const DVs = packed struct {
|
|
217
|
+
pub const DVs = packed struct(u16) {
|
|
196
218
|
gender: Gender = .Male,
|
|
197
219
|
pow: u6 = 40,
|
|
198
220
|
type: Type = .Dark,
|
|
@@ -200,10 +222,10 @@ const DVs = packed struct {
|
|
|
200
222
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
201
223
|
assert(p >= 1 and p <= 40);
|
|
202
224
|
assert(t != .Normal and t != .@"???");
|
|
203
|
-
return
|
|
225
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
204
226
|
}
|
|
205
227
|
|
|
206
|
-
pub
|
|
228
|
+
pub fn power(dvs: DVs) u8 {
|
|
207
229
|
return @as(u8, dvs.pow) + 30;
|
|
208
230
|
}
|
|
209
231
|
|
|
@@ -217,13 +239,8 @@ const DVs = packed struct {
|
|
|
217
239
|
};
|
|
218
240
|
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
219
241
|
((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);
|
|
242
|
+
const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
|
|
243
|
+
return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
|
|
227
244
|
}
|
|
228
245
|
};
|
|
229
246
|
|
|
@@ -231,12 +248,12 @@ test DVs {
|
|
|
231
248
|
var dvs = DVs.from(.Mewtwo, .{ .def = 13 });
|
|
232
249
|
try expectEqual(Gender.Unknown, dvs.gender);
|
|
233
250
|
try expectEqual(Type.Ice, dvs.type);
|
|
234
|
-
try expectEqual(
|
|
251
|
+
try expectEqual(70, dvs.power());
|
|
235
252
|
|
|
236
253
|
dvs = DVs.from(.Pikachu, .{ .spc = 14 });
|
|
237
254
|
try expectEqual(Gender.Male, dvs.gender);
|
|
238
255
|
try expectEqual(Type.Dark, dvs.type);
|
|
239
|
-
try expectEqual(
|
|
256
|
+
try expectEqual(69, dvs.power());
|
|
240
257
|
|
|
241
258
|
dvs = DVs.from(.Cyndaquil, .{ .atk = 1, .def = 3, .spe = 10, .spc = 9 });
|
|
242
259
|
try expectEqual(Gender.Female, dvs.gender);
|
|
@@ -248,10 +265,12 @@ test DVs {
|
|
|
248
265
|
try expectEqual(Gender.Female, dvs.gender);
|
|
249
266
|
}
|
|
250
267
|
|
|
251
|
-
|
|
268
|
+
/// Representation of a Generation II Pokémon's move slot in a battle.
|
|
269
|
+
pub const MoveSlot = extern struct {
|
|
270
|
+
/// The identifier for the move.
|
|
252
271
|
id: Move = .None,
|
|
253
|
-
|
|
254
|
-
pp:
|
|
272
|
+
/// The remaining PP of the move.
|
|
273
|
+
pp: u8 = 0,
|
|
255
274
|
|
|
256
275
|
comptime {
|
|
257
276
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -260,14 +279,19 @@ const MoveSlot = packed struct {
|
|
|
260
279
|
|
|
261
280
|
pub const Status = gen1.Status;
|
|
262
281
|
|
|
263
|
-
|
|
282
|
+
/// TODO
|
|
283
|
+
pub const TriAttack = enum(u2) { PAR, FRZ, BRN };
|
|
284
|
+
|
|
285
|
+
pub const Volatiles = packed struct(u128) {
|
|
264
286
|
Bide: bool = false,
|
|
265
287
|
Thrashing: bool = false,
|
|
288
|
+
BeatUp: bool = false,
|
|
266
289
|
Flinch: bool = false,
|
|
267
290
|
Charging: bool = false,
|
|
268
291
|
Underground: bool = false,
|
|
269
292
|
Flying: bool = false,
|
|
270
293
|
Confusion: bool = false,
|
|
294
|
+
|
|
271
295
|
Mist: bool = false,
|
|
272
296
|
FocusEnergy: bool = false,
|
|
273
297
|
Substitute: bool = false,
|
|
@@ -278,52 +302,55 @@ const Volatile = packed struct {
|
|
|
278
302
|
Transform: bool = false,
|
|
279
303
|
|
|
280
304
|
Nightmare: bool = false,
|
|
281
|
-
|
|
305
|
+
Curse: bool = false,
|
|
282
306
|
Protect: bool = false,
|
|
283
307
|
Foresight: bool = false,
|
|
284
308
|
PerishSong: bool = false,
|
|
285
309
|
Endure: bool = false,
|
|
286
310
|
Rollout: bool = false,
|
|
287
311
|
Attract: bool = false,
|
|
312
|
+
|
|
288
313
|
DefenseCurl: bool = false,
|
|
289
314
|
Encore: bool = false,
|
|
290
315
|
LockOn: bool = false,
|
|
291
316
|
DestinyBond: bool = false,
|
|
292
|
-
|
|
317
|
+
Trapped: bool = false,
|
|
293
318
|
|
|
294
|
-
trapped: bool = false,
|
|
295
319
|
switched: bool = false,
|
|
320
|
+
// TODO: replace with simply looking at Choice on Battle
|
|
296
321
|
switching: bool = false,
|
|
322
|
+
// TODO: simply store both last moves on side
|
|
323
|
+
dirty: bool = false,
|
|
297
324
|
|
|
298
|
-
|
|
325
|
+
frozen: bool = false,
|
|
326
|
+
minimized: bool = false,
|
|
327
|
+
_: u10 = 0,
|
|
328
|
+
// TODO: is this needed for unmodified stats?
|
|
329
|
+
transform: u4 = 0,
|
|
299
330
|
|
|
300
|
-
bind: u4 = 0,
|
|
301
|
-
future_sight: FutureSight = .{},
|
|
302
331
|
bide: u16 = 0,
|
|
303
|
-
|
|
332
|
+
bind: Bind = .{},
|
|
333
|
+
disable: Disable = .{},
|
|
304
334
|
rage: u8 = 0,
|
|
305
335
|
substitute: u8 = 0,
|
|
306
336
|
toxic: u8 = 0,
|
|
307
337
|
confusion: u4 = 0,
|
|
308
338
|
encore: u4 = 0,
|
|
339
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
309
340
|
fury_cutter: u4 = 0,
|
|
310
341
|
perish_song: u4 = 0,
|
|
311
342
|
protect: u4 = 0,
|
|
312
|
-
|
|
343
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
313
344
|
|
|
314
|
-
const
|
|
315
|
-
move: u4 = 0,
|
|
345
|
+
const Bind = packed struct(u8) {
|
|
316
346
|
duration: u4 = 0,
|
|
347
|
+
reason: u4 = 0,
|
|
317
348
|
};
|
|
318
349
|
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
350
|
+
const Disable = packed struct(u8) {
|
|
351
|
+
move: u4 = 0,
|
|
352
|
+
duration: u4 = 0,
|
|
322
353
|
};
|
|
323
|
-
|
|
324
|
-
comptime {
|
|
325
|
-
assert(@sizeOf(Volatile) == 16);
|
|
326
|
-
}
|
|
327
354
|
};
|
|
328
355
|
|
|
329
356
|
pub fn Stats(comptime T: type) type {
|
|
@@ -353,12 +380,12 @@ test Stats {
|
|
|
353
380
|
try expectEqual(0, stats.def);
|
|
354
381
|
|
|
355
382
|
var base = Species.get(.Gyarados).stats;
|
|
356
|
-
try expectEqual(
|
|
383
|
+
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
357
384
|
base = Species.get(.Pidgeot).stats;
|
|
358
|
-
try expectEqual(
|
|
385
|
+
try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
359
386
|
}
|
|
360
387
|
|
|
361
|
-
pub const Boosts = packed struct {
|
|
388
|
+
pub const Boosts = packed struct(u32) {
|
|
362
389
|
atk: i4 = 0,
|
|
363
390
|
def: i4 = 0,
|
|
364
391
|
spe: i4 = 0,
|
|
@@ -374,7 +401,7 @@ pub const Boosts = packed struct {
|
|
|
374
401
|
};
|
|
375
402
|
|
|
376
403
|
test Boosts {
|
|
377
|
-
const boosts =
|
|
404
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
378
405
|
try expectEqual(0, boosts.atk);
|
|
379
406
|
try expectEqual(-6, boosts.spd);
|
|
380
407
|
}
|
|
@@ -387,15 +414,10 @@ test Item {
|
|
|
387
414
|
try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
|
|
388
415
|
try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
|
|
389
416
|
|
|
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));
|
|
417
|
+
try expect(!Item.isMail(.UpGrade));
|
|
418
|
+
try expect(Item.isMail(.FlowerMail));
|
|
419
|
+
try expect(Item.isMail(.MirageMail));
|
|
420
|
+
try expect(!Item.isMail(.TownMap));
|
|
399
421
|
}
|
|
400
422
|
|
|
401
423
|
pub const Move = moves.Move;
|
|
@@ -404,15 +426,20 @@ test Move {
|
|
|
404
426
|
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
405
427
|
const move = Move.get(.DynamicPunch);
|
|
406
428
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
407
|
-
try expectEqual(
|
|
408
|
-
try expectEqual(
|
|
429
|
+
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
430
|
+
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
431
|
+
try expectEqual(5, Move.pp(.DynamicPunch));
|
|
432
|
+
try expectEqual(-1, Move.get(.Counter).priority);
|
|
433
|
+
try expect(!move.effect.isHighCritical());
|
|
434
|
+
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
435
|
+
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
409
436
|
}
|
|
410
437
|
|
|
411
438
|
pub const Species = species.Species;
|
|
412
439
|
|
|
413
440
|
test Species {
|
|
414
441
|
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
415
|
-
try expectEqual(
|
|
442
|
+
try expectEqual(100, Species.get(.Celebi).stats.spd);
|
|
416
443
|
}
|
|
417
444
|
|
|
418
445
|
pub const Type = types.Type;
|
|
@@ -425,7 +452,7 @@ test Types {
|
|
|
425
452
|
try expect(!Type.Steel.special());
|
|
426
453
|
try expect(Type.Dark.special());
|
|
427
454
|
|
|
428
|
-
try expectEqual(
|
|
455
|
+
try expectEqual(3, Type.Water.precedence());
|
|
429
456
|
try expect(Type.Bug.precedence() > Type.Poison.precedence());
|
|
430
457
|
|
|
431
458
|
try expectEqual(Effectiveness.Super, Type.effectiveness(.Ghost, .Psychic));
|