@pkmn/engine 0.1.0-dev.e5b5a998 → 0.1.0-dev.f6c1a379
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/README.md +53 -42
- package/build/pkg/addon.js +3 -2
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +24 -2
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +2 -2
- package/build/pkg/gen1.js +13 -16
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/protocol.js +56 -37
- package/build/pkg/protocol.js.map +1 -1
- package/build.zig +82 -57
- package/package.json +33 -15
- package/src/bin/install-pkmn-engine +10 -5
- package/src/lib/binding/node.zig +46 -141
- package/src/lib/binding/wasm.zig +4 -2
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +15 -27
- package/src/lib/common/debug.zig +20 -11
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +60 -0
- package/src/lib/common/options.zig +9 -0
- package/src/lib/common/protocol.zig +646 -138
- package/src/lib/common/rational.zig +353 -0
- package/src/lib/common/rng.zig +15 -15
- package/src/lib/common/util.zig +12 -0
- package/src/lib/gen1/README.md +257 -177
- package/src/lib/gen1/calc.zig +426 -0
- package/src/lib/gen1/chance.zig +1058 -0
- package/src/lib/gen1/data/moves.zig +168 -170
- package/src/lib/gen1/data/types.zig +3 -3
- package/src/lib/gen1/data.zig +61 -51
- package/src/lib/gen1/helpers.zig +588 -17
- package/src/lib/gen1/mechanics.zig +776 -502
- package/src/lib/gen2/README.md +106 -85
- package/src/lib/gen2/data/items.zig +11 -17
- package/src/lib/gen2/data/moves.zig +639 -562
- package/src/lib/gen2/data/types.zig +127 -25
- package/src/lib/gen2/data.zig +104 -78
- package/src/lib/gen2/helpers.zig +15 -21
- package/src/lib/gen2/mechanics.zig +1444 -11
- package/src/lib/pkmn.zig +61 -2
|
@@ -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,10 +75,89 @@ 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 CONVERSION2 = [_]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(CONVERSION2)) == 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.
|
|
@@ -94,10 +173,28 @@ pub const Type = enum(u8) {
|
|
|
94
173
|
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
95
174
|
}
|
|
96
175
|
|
|
97
|
-
/// The precedence order of
|
|
176
|
+
/// The precedence order of Type `type`.
|
|
98
177
|
pub inline 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 inline 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 inline 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) CONVERSION2[num] else @enumFromInt(num - 10);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// The Type corresponding to a Hidden Power `index`.
|
|
195
|
+
pub inline fn hiddenPower(index: u8) Type {
|
|
196
|
+
return HIDDEN_POWER[index];
|
|
197
|
+
}
|
|
101
198
|
};
|
|
102
199
|
|
|
103
200
|
/// Representation of a Pokémon's typing.
|
|
@@ -120,4 +217,9 @@ pub const Types = extern struct {
|
|
|
120
217
|
pub inline 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 inline fn sandstormImmune(self: Types) u8 {
|
|
223
|
+
return self.type1 <= @intFromEnum(Type.Steel) or self.type2 <= @intFromEnum(Type.Steel);
|
|
224
|
+
}
|
|
123
225
|
};
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const builtin = @import("builtin");
|
|
3
3
|
|
|
4
|
+
const pkmn = @import("../pkmn.zig");
|
|
5
|
+
|
|
4
6
|
const data = @import("../common/data.zig");
|
|
5
7
|
const DEBUG = @import("../common/debug.zig").print;
|
|
6
|
-
const
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
7
9
|
const rng = @import("../common/rng.zig");
|
|
10
|
+
const util = @import("../common/util.zig");
|
|
8
11
|
|
|
9
12
|
const items = @import("data/items.zig");
|
|
10
13
|
const moves = @import("data/moves.zig");
|
|
@@ -13,6 +16,8 @@ const types = @import("data/types.zig");
|
|
|
13
16
|
|
|
14
17
|
const gen1 = @import("../gen1/data.zig");
|
|
15
18
|
|
|
19
|
+
const calc = @import("calc.zig");
|
|
20
|
+
const chance = @import("chance.zig");
|
|
16
21
|
const mechanics = @import("mechanics.zig");
|
|
17
22
|
|
|
18
23
|
const assert = std.debug.assert;
|
|
@@ -31,16 +36,22 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
|
31
36
|
else
|
|
32
37
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
33
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
|
+
|
|
34
42
|
const Choice = data.Choice;
|
|
35
43
|
const ID = data.ID;
|
|
36
44
|
const Player = data.Player;
|
|
37
45
|
const Result = data.Result;
|
|
38
46
|
|
|
39
|
-
const
|
|
47
|
+
const PointerType = util.PointerType;
|
|
48
|
+
|
|
49
|
+
const showdown = pkmn.options.showdown;
|
|
40
50
|
|
|
41
51
|
pub const PRNG = rng.PRNG(2);
|
|
42
52
|
|
|
43
53
|
pub fn Battle(comptime RNG: anytype) type {
|
|
54
|
+
const padding = if (!showdown) 3 else 5;
|
|
44
55
|
return extern struct {
|
|
45
56
|
const Self = @This();
|
|
46
57
|
|
|
@@ -49,20 +60,24 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
49
60
|
turn: u16 = 0,
|
|
50
61
|
field: Field = .{},
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
_: [padding]u8 = .{0} ** padding,
|
|
64
|
+
|
|
65
|
+
pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
66
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
53
67
|
return &self.sides[@intFromEnum(player)];
|
|
54
68
|
}
|
|
55
69
|
|
|
56
|
-
pub inline fn foe(self:
|
|
70
|
+
pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
71
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
57
72
|
return &self.sides[@intFromEnum(player.foe())];
|
|
58
73
|
}
|
|
59
74
|
|
|
60
|
-
pub inline fn active(self: *Self, player: Player) ID {
|
|
61
|
-
return player.ident(@intCast(
|
|
75
|
+
pub inline fn active(self: *const Self, player: Player) ID {
|
|
76
|
+
return player.ident(@intCast(self.side(player).pokemon[0].position));
|
|
62
77
|
}
|
|
63
78
|
|
|
64
|
-
pub fn update(self: *Self, c1: Choice, c2: Choice,
|
|
65
|
-
return mechanics.update(self, c1, c2,
|
|
79
|
+
pub fn update(self: *Self, c1: Choice, c2: Choice, options: anytype) !Result {
|
|
80
|
+
return mechanics.update(self, c1, c2, options);
|
|
66
81
|
}
|
|
67
82
|
|
|
68
83
|
pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
@@ -71,35 +86,32 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
71
86
|
};
|
|
72
87
|
}
|
|
73
88
|
|
|
74
|
-
// TODO
|
|
75
89
|
test Battle {
|
|
76
|
-
try expectEqual(
|
|
90
|
+
try expectEqual(512, @sizeOf(Battle(PRNG)));
|
|
77
91
|
}
|
|
78
92
|
|
|
79
|
-
const Field = packed struct {
|
|
93
|
+
pub const Field = packed struct(u8) {
|
|
80
94
|
weather: Weather = .None,
|
|
81
95
|
weather_duration: u4 = 0,
|
|
82
|
-
|
|
83
|
-
comptime {
|
|
84
|
-
assert(@sizeOf(Field) == 1);
|
|
85
|
-
}
|
|
86
96
|
};
|
|
87
97
|
|
|
88
|
-
const Weather = enum(u4) {
|
|
98
|
+
pub const Weather = enum(u4) {
|
|
89
99
|
None,
|
|
90
100
|
Rain,
|
|
91
101
|
Sun,
|
|
92
102
|
Sandstorm,
|
|
93
103
|
};
|
|
94
104
|
|
|
95
|
-
const Side = extern struct {
|
|
105
|
+
pub const Side = extern struct {
|
|
96
106
|
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
97
|
-
active: ActivePokemon = .{},
|
|
98
|
-
conditions: Conditions,
|
|
107
|
+
active: ActivePokemon align(8) = .{},
|
|
108
|
+
conditions: Conditions = .{},
|
|
109
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
99
110
|
last_used_move: Move = .None,
|
|
100
|
-
_: u8 = 0,
|
|
101
111
|
|
|
102
|
-
|
|
112
|
+
_: [7]u8 = .{0} ** 7,
|
|
113
|
+
|
|
114
|
+
const Conditions = packed struct(u32) {
|
|
103
115
|
Spikes: bool = false,
|
|
104
116
|
Safeguard: bool = false,
|
|
105
117
|
LightScreen: bool = false,
|
|
@@ -109,50 +121,69 @@ const Side = extern struct {
|
|
|
109
121
|
light_screen: u4 = 0,
|
|
110
122
|
reflect: u4 = 0,
|
|
111
123
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
124
|
+
future_sight: FutureSight = .{},
|
|
125
|
+
|
|
126
|
+
const FutureSight = packed struct(u16) {
|
|
127
|
+
damage: u12 = 0,
|
|
128
|
+
count: u4 = 0,
|
|
129
|
+
};
|
|
115
130
|
};
|
|
116
131
|
|
|
117
132
|
comptime {
|
|
118
|
-
assert(@sizeOf(Side) ==
|
|
133
|
+
assert(@sizeOf(Side) == 248);
|
|
119
134
|
}
|
|
120
135
|
|
|
121
|
-
pub inline fn get(self:
|
|
136
|
+
pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
137
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
122
138
|
assert(slot > 0 and slot <= 6);
|
|
123
139
|
const id = self.pokemon[slot - 1].position;
|
|
124
140
|
assert(id > 0 and id <= 6);
|
|
125
141
|
return &self.pokemon[id - 1];
|
|
126
142
|
}
|
|
127
143
|
|
|
128
|
-
pub inline fn stored(self:
|
|
144
|
+
pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
145
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
129
146
|
return self.get(1);
|
|
130
147
|
}
|
|
148
|
+
|
|
149
|
+
pub inline 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 inline 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
|
+
// }
|
|
131
161
|
};
|
|
132
162
|
|
|
133
163
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
134
|
-
const ActivePokemon = extern struct {
|
|
135
|
-
volatiles:
|
|
164
|
+
pub const ActivePokemon = extern struct {
|
|
165
|
+
volatiles: Volatiles align(4) = .{},
|
|
136
166
|
stats: Stats(u16) = .{},
|
|
137
167
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
138
168
|
boosts: Boosts = .{},
|
|
169
|
+
types: Types = .{},
|
|
139
170
|
species: Species = .None,
|
|
140
|
-
|
|
141
|
-
last_move: Move = .None,
|
|
171
|
+
|
|
142
172
|
_: u8 = 0,
|
|
143
173
|
|
|
144
174
|
comptime {
|
|
145
175
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
146
176
|
}
|
|
147
177
|
|
|
148
|
-
pub inline fn move(self:
|
|
178
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
179
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
149
180
|
assert(mslot > 0 and mslot <= 4);
|
|
150
181
|
assert(self.moves[mslot - 1].id != .None);
|
|
151
182
|
return &self.moves[mslot - 1];
|
|
152
183
|
}
|
|
153
184
|
};
|
|
154
185
|
|
|
155
|
-
const Pokemon = extern struct {
|
|
186
|
+
pub const Pokemon = extern struct {
|
|
156
187
|
stats: Stats(u16) = .{},
|
|
157
188
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
158
189
|
types: Types = .{},
|
|
@@ -169,7 +200,8 @@ const Pokemon = extern struct {
|
|
|
169
200
|
assert(@sizeOf(Pokemon) == 32);
|
|
170
201
|
}
|
|
171
202
|
|
|
172
|
-
pub inline fn move(self:
|
|
203
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
204
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
173
205
|
assert(mslot > 0 and mslot <= 4);
|
|
174
206
|
assert(self.moves[mslot - 1].id != .None);
|
|
175
207
|
return &self.moves[mslot - 1];
|
|
@@ -182,7 +214,7 @@ pub const Gender = enum(u2) {
|
|
|
182
214
|
Unknown,
|
|
183
215
|
};
|
|
184
216
|
|
|
185
|
-
const DVs = packed struct {
|
|
217
|
+
pub const DVs = packed struct(u16) {
|
|
186
218
|
gender: Gender = .Male,
|
|
187
219
|
pow: u6 = 40,
|
|
188
220
|
type: Type = .Dark,
|
|
@@ -190,7 +222,7 @@ const DVs = packed struct {
|
|
|
190
222
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
191
223
|
assert(p >= 1 and p <= 40);
|
|
192
224
|
assert(t != .Normal and t != .@"???");
|
|
193
|
-
return
|
|
225
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
194
226
|
}
|
|
195
227
|
|
|
196
228
|
pub inline fn power(dvs: DVs) u8 {
|
|
@@ -205,15 +237,10 @@ const DVs = packed struct {
|
|
|
205
237
|
0xFF => .Unknown,
|
|
206
238
|
else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
|
|
207
239
|
};
|
|
208
|
-
const p = @intCast(
|
|
240
|
+
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
209
241
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
210
|
-
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2)
|
|
211
|
-
|
|
212
|
-
return DVs{ .gender = g, .pow = p, .type = @enumFromInt(Type, t) };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
comptime {
|
|
216
|
-
assert(@sizeOf(DVs) == 2);
|
|
242
|
+
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
|
|
243
|
+
return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
|
|
217
244
|
}
|
|
218
245
|
};
|
|
219
246
|
|
|
@@ -238,10 +265,12 @@ test DVs {
|
|
|
238
265
|
try expectEqual(Gender.Female, dvs.gender);
|
|
239
266
|
}
|
|
240
267
|
|
|
241
|
-
|
|
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.
|
|
242
271
|
id: Move = .None,
|
|
243
|
-
|
|
244
|
-
pp:
|
|
272
|
+
/// The remaining PP of the move.
|
|
273
|
+
pp: u8 = 0,
|
|
245
274
|
|
|
246
275
|
comptime {
|
|
247
276
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -250,14 +279,16 @@ const MoveSlot = packed struct {
|
|
|
250
279
|
|
|
251
280
|
pub const Status = gen1.Status;
|
|
252
281
|
|
|
253
|
-
const
|
|
282
|
+
pub const Volatiles = packed struct(u128) {
|
|
254
283
|
Bide: bool = false,
|
|
255
284
|
Thrashing: bool = false,
|
|
285
|
+
BeatUp: bool = false,
|
|
256
286
|
Flinch: bool = false,
|
|
257
287
|
Charging: bool = false,
|
|
258
288
|
Underground: bool = false,
|
|
259
289
|
Flying: bool = false,
|
|
260
290
|
Confusion: bool = false,
|
|
291
|
+
|
|
261
292
|
Mist: bool = false,
|
|
262
293
|
FocusEnergy: bool = false,
|
|
263
294
|
Substitute: bool = false,
|
|
@@ -268,27 +299,29 @@ const Volatile = packed struct {
|
|
|
268
299
|
Transform: bool = false,
|
|
269
300
|
|
|
270
301
|
Nightmare: bool = false,
|
|
271
|
-
|
|
302
|
+
Curse: bool = false,
|
|
272
303
|
Protect: bool = false,
|
|
273
304
|
Foresight: bool = false,
|
|
274
305
|
PerishSong: bool = false,
|
|
275
306
|
Endure: bool = false,
|
|
276
307
|
Rollout: bool = false,
|
|
277
308
|
Attract: bool = false,
|
|
309
|
+
|
|
278
310
|
DefenseCurl: bool = false,
|
|
279
311
|
Encore: bool = false,
|
|
280
312
|
LockOn: bool = false,
|
|
281
313
|
DestinyBond: bool = false,
|
|
282
|
-
BeatUp: bool = false,
|
|
283
314
|
|
|
284
315
|
trapped: bool = false,
|
|
285
316
|
switched: bool = false,
|
|
286
317
|
switching: bool = false,
|
|
318
|
+
dirty: bool = false,
|
|
287
319
|
|
|
288
|
-
|
|
320
|
+
frozen: bool = false,
|
|
321
|
+
_: u15 = 0,
|
|
289
322
|
|
|
323
|
+
bind_reason: u4 = 0,
|
|
290
324
|
bind: u4 = 0,
|
|
291
|
-
future_sight: FutureSight = .{},
|
|
292
325
|
bide: u16 = 0,
|
|
293
326
|
disabled: Disabled = .{},
|
|
294
327
|
rage: u8 = 0,
|
|
@@ -296,24 +329,16 @@ const Volatile = packed struct {
|
|
|
296
329
|
toxic: u8 = 0,
|
|
297
330
|
confusion: u4 = 0,
|
|
298
331
|
encore: u4 = 0,
|
|
332
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
299
333
|
fury_cutter: u4 = 0,
|
|
300
334
|
perish_song: u4 = 0,
|
|
301
335
|
protect: u4 = 0,
|
|
302
|
-
|
|
336
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
303
337
|
|
|
304
|
-
const Disabled = packed struct {
|
|
338
|
+
const Disabled = packed struct(u8) {
|
|
305
339
|
move: u4 = 0,
|
|
306
340
|
duration: u4 = 0,
|
|
307
341
|
};
|
|
308
|
-
|
|
309
|
-
const FutureSight = packed struct {
|
|
310
|
-
damage: u12 = 0,
|
|
311
|
-
count: u4 = 0,
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
comptime {
|
|
315
|
-
assert(@sizeOf(Volatile) == 16);
|
|
316
|
-
}
|
|
317
342
|
};
|
|
318
343
|
|
|
319
344
|
pub fn Stats(comptime T: type) type {
|
|
@@ -327,10 +352,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
327
352
|
|
|
328
353
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
354
|
assert(level > 0 and level <= 100);
|
|
330
|
-
const
|
|
355
|
+
const float: f32 = @floatFromInt(exp);
|
|
356
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
331
357
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
358
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
|
-
return @intCast(
|
|
359
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
334
360
|
}
|
|
335
361
|
};
|
|
336
362
|
}
|
|
@@ -347,7 +373,7 @@ test Stats {
|
|
|
347
373
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
348
374
|
}
|
|
349
375
|
|
|
350
|
-
pub const Boosts = packed struct {
|
|
376
|
+
pub const Boosts = packed struct(u32) {
|
|
351
377
|
atk: i4 = 0,
|
|
352
378
|
def: i4 = 0,
|
|
353
379
|
spe: i4 = 0,
|
|
@@ -363,7 +389,7 @@ pub const Boosts = packed struct {
|
|
|
363
389
|
};
|
|
364
390
|
|
|
365
391
|
test Boosts {
|
|
366
|
-
const boosts =
|
|
392
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
367
393
|
try expectEqual(0, boosts.atk);
|
|
368
394
|
try expectEqual(-6, boosts.spd);
|
|
369
395
|
}
|
|
@@ -376,15 +402,10 @@ test Item {
|
|
|
376
402
|
try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
|
|
377
403
|
try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
|
|
378
404
|
|
|
379
|
-
try expect(!Item.
|
|
380
|
-
try expect(Item.
|
|
381
|
-
try expect(Item.
|
|
382
|
-
try expect(!Item.
|
|
383
|
-
|
|
384
|
-
try expect(!Item.berry(.LightBall));
|
|
385
|
-
try expect(Item.berry(.PSNCureBerry));
|
|
386
|
-
try expect(Item.berry(.GoldBerry));
|
|
387
|
-
try expect(!Item.berry(.MasterBall));
|
|
405
|
+
try expect(!Item.isMail(.UpGrade));
|
|
406
|
+
try expect(Item.isMail(.FlowerMail));
|
|
407
|
+
try expect(Item.isMail(.MirageMail));
|
|
408
|
+
try expect(!Item.isMail(.TownMap));
|
|
388
409
|
}
|
|
389
410
|
|
|
390
411
|
pub const Move = moves.Move;
|
|
@@ -393,8 +414,13 @@ test Move {
|
|
|
393
414
|
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
394
415
|
const move = Move.get(.DynamicPunch);
|
|
395
416
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
|
-
try expectEqual(
|
|
397
|
-
try expectEqual(
|
|
417
|
+
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
418
|
+
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
419
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
420
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
421
|
+
try expect(!move.effect.isHighCritical());
|
|
422
|
+
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
423
|
+
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
398
424
|
}
|
|
399
425
|
|
|
400
426
|
pub const Species = species.Species;
|