@pkmn/engine 0.1.0-dev.5ea92492 → 0.1.0-dev.6140f48c
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 +160 -76
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +110 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +27 -5
- package/build/pkg/addon.js +42 -73
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +40 -5
- package/build/pkg/data/protocol.json +84 -13
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +18 -20
- package/build/pkg/gen1.js +76 -51
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +57 -103
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +24 -3
- package/build/pkg/protocol.js +127 -104
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +53 -76
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +152 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +359 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +33 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +413 -153
- package/package.json +69 -120
- package/src/bin/install-pkmn-engine +42 -26
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +255 -0
- package/src/lib/bindings/node.zig +121 -0
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +19 -31
- package/src/lib/common/debug.zig +58 -17
- package/src/lib/common/js.zig +120 -0
- package/src/lib/common/optional.zig +71 -0
- package/src/lib/common/options.zig +18 -3
- package/src/lib/common/protocol.zig +947 -407
- package/src/lib/common/rational.zig +597 -0
- package/src/lib/common/rng.zig +31 -24
- package/src/lib/common/util.zig +76 -0
- package/src/lib/gen1/calc.zig +962 -0
- package/src/lib/gen1/chance.zig +1185 -0
- package/src/lib/gen1/data/moves.zig +203 -193
- package/src/lib/gen1/data/species.zig +6 -8
- package/src/lib/gen1/data/types.zig +11 -12
- package/src/lib/gen1/data.zig +100 -90
- package/src/lib/gen1/helpers.zig +139 -50
- package/src/lib/gen1/mechanics.zig +2714 -2345
- package/src/lib/gen2/calc.zig +153 -0
- package/src/lib/gen2/chance.zig +902 -0
- package/src/lib/gen2/data/items.zig +15 -23
- package/src/lib/gen2/data/moves.zig +665 -573
- package/src/lib/gen2/data/species.zig +3 -5
- package/src/lib/gen2/data/types.zig +141 -36
- package/src/lib/gen2/data.zig +137 -101
- package/src/lib/gen2/generated.zig +801 -0
- package/src/lib/gen2/helpers.zig +59 -44
- package/src/lib/gen2/mechanics.zig +3582 -30
- package/src/lib/node.zig +18 -0
- package/src/lib/pkmn.zig +93 -12
- package/src/lib/wasm.zig +17 -0
- package/src/tools/display/dom.ts +93 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -382
- package/build/test/display.js.map +0 -1
- package/src/lib/binding/node.zig +0 -229
- package/src/lib/binding/wasm.zig +0 -10
- package/src/lib/gen1/README.md +0 -791
- package/src/lib/gen2/README.md +0 -309
- 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 gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Stats = gen1.Stats;
|
|
10
8
|
const Types = gen1.Types;
|
|
11
9
|
|
|
@@ -1091,15 +1089,15 @@ pub const Species = enum(u8) {
|
|
|
1091
1089
|
/// The number of Pokémon species in this generation.
|
|
1092
1090
|
pub const size = 151;
|
|
1093
1091
|
|
|
1094
|
-
/// The Pokémon's critical hit
|
|
1095
|
-
pub
|
|
1092
|
+
/// The Pokémon's critical hit ratio out of 256.
|
|
1093
|
+
pub fn chance(id: Species) u8 {
|
|
1096
1094
|
assert(id != .None);
|
|
1097
|
-
return CHANCES[@
|
|
1095
|
+
return CHANCES[@intFromEnum(id) - 1];
|
|
1098
1096
|
}
|
|
1099
1097
|
|
|
1100
1098
|
/// Returns the `Data` corresponding to the species.
|
|
1101
1099
|
pub fn get(id: Species) Data {
|
|
1102
1100
|
assert(id != .None);
|
|
1103
|
-
return DATA[@
|
|
1101
|
+
return DATA[@intFromEnum(id) - 1];
|
|
1104
1102
|
}
|
|
1105
1103
|
};
|
|
@@ -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;
|
|
@@ -30,7 +29,7 @@ pub const Type = enum(u4) {
|
|
|
30
29
|
Ice,
|
|
31
30
|
Dragon,
|
|
32
31
|
|
|
33
|
-
const CHART
|
|
32
|
+
const CHART: [15][15]Effectiveness = .{
|
|
34
33
|
[_]Effectiveness{ N, N, N, N, N, R, N, I, N, N, N, N, N, N, N }, // Normal
|
|
35
34
|
[_]Effectiveness{ S, N, R, R, N, S, R, I, N, N, N, N, R, S, N }, // Fighting
|
|
36
35
|
[_]Effectiveness{ N, S, N, N, N, R, S, N, N, N, S, R, N, N, N }, // Flying
|
|
@@ -90,19 +89,19 @@ pub const Type = enum(u4) {
|
|
|
90
89
|
pub const size = 15;
|
|
91
90
|
|
|
92
91
|
/// Whether or not this type is considered to be special as opposed to physical.
|
|
93
|
-
pub
|
|
94
|
-
return @
|
|
92
|
+
pub fn special(self: Type) bool {
|
|
93
|
+
return @intFromEnum(self) >= @intFromEnum(Type.Fire);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
/// The `Effectiveness` of type `t2` vs. type `t1`.
|
|
98
|
-
pub
|
|
99
|
-
return CHART[@
|
|
97
|
+
pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
|
|
98
|
+
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
/// The precedence order of
|
|
101
|
+
/// The precedence order of Type `t1` vs. Type `t2`.
|
|
103
102
|
pub fn precedence(t1: Type, t2: Type) u8 {
|
|
104
103
|
for (PRECEDENCE, 0..) |matchup, i| {
|
|
105
|
-
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(
|
|
104
|
+
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
|
106
105
|
}
|
|
107
106
|
unreachable;
|
|
108
107
|
}
|
|
@@ -120,12 +119,12 @@ pub const Types = packed struct {
|
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
/// Whether this typing is immune to type `t`.
|
|
123
|
-
pub
|
|
122
|
+
pub fn immune(self: Types, t: Type) bool {
|
|
124
123
|
return t.effectiveness(self.type1) == I or t.effectiveness(self.type2) == I;
|
|
125
124
|
}
|
|
126
125
|
|
|
127
126
|
/// Whether this typing includes type `t`.
|
|
128
|
-
pub
|
|
127
|
+
pub fn includes(self: Types, t: Type) bool {
|
|
129
128
|
return self.type1 == t or self.type2 == t;
|
|
130
129
|
}
|
|
131
130
|
};
|
package/src/lib/gen1/data.zig
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
1
|
const builtin = @import("builtin");
|
|
3
|
-
|
|
2
|
+
const calc = @import("calc.zig");
|
|
3
|
+
const chance = @import("chance.zig");
|
|
4
4
|
const data = @import("../common/data.zig");
|
|
5
|
-
const
|
|
5
|
+
const mechanics = @import("mechanics.zig");
|
|
6
|
+
const moves = @import("data/moves.zig");
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
6
8
|
const protocol = @import("../common/protocol.zig");
|
|
7
9
|
const rng = @import("../common/rng.zig");
|
|
8
|
-
|
|
9
|
-
const moves = @import("data/moves.zig");
|
|
10
10
|
const species = @import("data/species.zig");
|
|
11
|
+
const std = @import("std");
|
|
11
12
|
const types = @import("data/types.zig");
|
|
12
|
-
|
|
13
|
-
const mechanics = @import("mechanics.zig");
|
|
13
|
+
const util = @import("../common/util.zig");
|
|
14
14
|
|
|
15
15
|
const assert = std.debug.assert;
|
|
16
16
|
const expectEqual = std.testing.expectEqual;
|
|
17
17
|
const expect = std.testing.expect;
|
|
18
18
|
|
|
19
19
|
/// The minimum size in bytes required to hold all Generation I choice options.
|
|
20
|
-
pub const
|
|
21
|
-
/// The
|
|
22
|
-
/// At least as large as MAX_OPTIONS.
|
|
20
|
+
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
21
|
+
/// The maximum number of bytes possibly logged by a single Generation I update.
|
|
23
22
|
pub const MAX_LOGS: usize = 180;
|
|
24
23
|
|
|
25
|
-
/// The
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
/// The optimal size in bytes required to hold all Generation I choice options.
|
|
25
|
+
/// At least as large as MAX_CHOICES.
|
|
26
|
+
pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
27
|
+
MAX_CHOICES
|
|
28
28
|
else
|
|
29
|
-
std.math.ceilPowerOfTwo(usize,
|
|
29
|
+
std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
|
|
30
30
|
/// The optimal size in bytes required to hold the largest amount of log data possible from a
|
|
31
31
|
/// single Generation I update. At least as large as MAX_LOGS.
|
|
32
32
|
pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
@@ -34,12 +34,18 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
|
34
34
|
else
|
|
35
35
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
36
36
|
|
|
37
|
+
/// Null object pattern implementation of pkmn.battle.Options for Generation I.
|
|
38
|
+
pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
|
|
39
|
+
|
|
37
40
|
const Choice = data.Choice;
|
|
38
41
|
const ID = data.ID;
|
|
39
42
|
const Player = data.Player;
|
|
40
43
|
const Result = data.Result;
|
|
41
44
|
|
|
42
|
-
const
|
|
45
|
+
const PointerType = util.PointerType;
|
|
46
|
+
const isPointerTo = util.isPointerTo;
|
|
47
|
+
|
|
48
|
+
const showdown = pkmn.options.showdown;
|
|
43
49
|
|
|
44
50
|
/// The pseudo random number generator used by Generation I.
|
|
45
51
|
pub const PRNG = rng.PRNG(1);
|
|
@@ -55,31 +61,42 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
55
61
|
turn: u16 = 0,
|
|
56
62
|
/// The last damage dealt by either side.
|
|
57
63
|
last_damage: u16 = 0,
|
|
58
|
-
///
|
|
59
|
-
|
|
64
|
+
/// Details about the last move selected/used by each player required to detect desyncs.
|
|
65
|
+
last_moves: [2]MoveDetails = [_]MoveDetails{.{}} ** 2,
|
|
60
66
|
/// The pseudo random number generator.
|
|
61
67
|
rng: RNG,
|
|
62
68
|
|
|
63
69
|
/// Returns the `Side` for the given `player`.
|
|
64
|
-
pub
|
|
65
|
-
|
|
70
|
+
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
71
|
+
assert(isPointerTo(self, Self));
|
|
72
|
+
return &self.sides[@intFromEnum(player)];
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
/// Returns the `Side` of the opponent for the given `player`
|
|
69
|
-
pub
|
|
70
|
-
|
|
76
|
+
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
77
|
+
assert(isPointerTo(self, Self));
|
|
78
|
+
return &self.sides[@intFromEnum(player.foe())];
|
|
71
79
|
}
|
|
72
80
|
|
|
73
81
|
/// Returns an identifier for the active Pokémon of `player`.
|
|
74
|
-
pub
|
|
75
|
-
return player.ident(@intCast(
|
|
82
|
+
pub fn active(self: *const Self, player: Player) ID {
|
|
83
|
+
return player.ident(@intCast(self.side(player).order[0]));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Returns the `MoveDetails` for the given `player`.
|
|
87
|
+
pub fn lastMove(
|
|
88
|
+
self: anytype,
|
|
89
|
+
player: Player,
|
|
90
|
+
) PointerType(@TypeOf(self), MoveDetails) {
|
|
91
|
+
assert(isPointerTo(self, Self));
|
|
92
|
+
return &self.last_moves[@intFromEnum(player)];
|
|
76
93
|
}
|
|
77
94
|
|
|
78
95
|
/// Returns the result of applying Player 1's choice `c1` and Player 2's choice `c2` to the
|
|
79
|
-
/// battle, optionally writing protocol logs to `log` if `options.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return
|
|
96
|
+
/// battle, optionally writing protocol message logs to `options.log` if `pkmn.options.log`
|
|
97
|
+
/// is enabled.
|
|
98
|
+
pub fn update(self: *Self, c1: Choice, c2: Choice, options: anytype) !Result {
|
|
99
|
+
return mechanics.update(self, c1, c2, options);
|
|
83
100
|
}
|
|
84
101
|
|
|
85
102
|
/// Fills in at most `out.len` possible choices for the `player` given the previous `result`
|
|
@@ -91,8 +108,8 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
91
108
|
/// interacts with Disable, in which case there are no possible choices for the player to
|
|
92
109
|
/// make (i.e. on the cartridge a soft-lock occurs).
|
|
93
110
|
///
|
|
94
|
-
/// This function will always return
|
|
95
|
-
pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
111
|
+
/// This function will always return choices > 0 if `pkmn.options.showdown` is true.
|
|
112
|
+
pub fn choices(self: *const Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
96
113
|
return mechanics.choices(self, player, request, out);
|
|
97
114
|
}
|
|
98
115
|
};
|
|
@@ -121,7 +138,8 @@ pub const Side = extern struct {
|
|
|
121
138
|
}
|
|
122
139
|
|
|
123
140
|
/// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
|
|
124
|
-
pub
|
|
141
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
142
|
+
assert(isPointerTo(self, Side));
|
|
125
143
|
assert(slot > 0 and slot <= 6);
|
|
126
144
|
const id = self.order[slot - 1];
|
|
127
145
|
assert(id > 0 and id <= 6);
|
|
@@ -129,7 +147,8 @@ pub const Side = extern struct {
|
|
|
129
147
|
}
|
|
130
148
|
|
|
131
149
|
/// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
|
|
132
|
-
pub
|
|
150
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
151
|
+
assert(isPointerTo(self, Side));
|
|
133
152
|
return self.get(1);
|
|
134
153
|
}
|
|
135
154
|
};
|
|
@@ -154,7 +173,8 @@ pub const ActivePokemon = extern struct {
|
|
|
154
173
|
}
|
|
155
174
|
|
|
156
175
|
/// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
|
|
157
|
-
pub
|
|
176
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
177
|
+
assert(isPointerTo(self, ActivePokemon));
|
|
158
178
|
assert(mslot > 0 and mslot <= 4);
|
|
159
179
|
assert(self.moves[mslot - 1].id != .None);
|
|
160
180
|
return &self.moves[mslot - 1];
|
|
@@ -183,14 +203,15 @@ pub const Pokemon = extern struct {
|
|
|
183
203
|
}
|
|
184
204
|
|
|
185
205
|
/// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
|
|
186
|
-
pub
|
|
206
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
207
|
+
assert(isPointerTo(self, Pokemon));
|
|
187
208
|
assert(mslot > 0 and mslot <= 4);
|
|
188
209
|
assert(self.moves[mslot - 1].id != .None);
|
|
189
210
|
return &self.moves[mslot - 1];
|
|
190
211
|
}
|
|
191
212
|
};
|
|
192
213
|
|
|
193
|
-
/// Representation of a Generation I
|
|
214
|
+
/// Representation of a Generation I Pokémon's move slot in a battle.
|
|
194
215
|
pub const MoveSlot = extern struct {
|
|
195
216
|
/// The identifier for the move.
|
|
196
217
|
id: Move = .None,
|
|
@@ -198,22 +219,16 @@ pub const MoveSlot = extern struct {
|
|
|
198
219
|
pp: u8 = 0,
|
|
199
220
|
|
|
200
221
|
comptime {
|
|
201
|
-
assert(@sizeOf(MoveSlot) ==
|
|
222
|
+
assert(@sizeOf(MoveSlot) == 2);
|
|
202
223
|
}
|
|
203
224
|
};
|
|
204
225
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
///
|
|
208
|
-
|
|
209
|
-
///
|
|
210
|
-
|
|
211
|
-
/// Player 1's move index.
|
|
212
|
-
p2: uindex = 0,
|
|
213
|
-
|
|
214
|
-
comptime {
|
|
215
|
-
assert(@sizeOf(MoveIndexes) == if (showdown) 4 else 1);
|
|
216
|
-
}
|
|
226
|
+
/// Details required to detect desyncs based on the move last selected/executed by players.
|
|
227
|
+
pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
|
|
228
|
+
/// The move slot index of the last move selected in the battle menu.
|
|
229
|
+
index: if (showdown) u8 else u4 = 0,
|
|
230
|
+
/// Whether the last move executed was counterable.
|
|
231
|
+
counterable: if (showdown) u8 else u4 = 0,
|
|
217
232
|
};
|
|
218
233
|
|
|
219
234
|
/// Bitfield representation of a Generation I & II Pokémon's major status condition.
|
|
@@ -237,39 +252,39 @@ pub const Status = enum(u8) {
|
|
|
237
252
|
|
|
238
253
|
pub const TOX = 0b10001000;
|
|
239
254
|
|
|
240
|
-
const
|
|
255
|
+
const SLEEP = 0b111;
|
|
241
256
|
|
|
242
257
|
/// Whether or not the status `num` is the same as `status`.
|
|
243
|
-
pub
|
|
258
|
+
pub fn is(num: u8, status: Status) bool {
|
|
244
259
|
if (status == .SLP) return Status.duration(num) > 0;
|
|
245
|
-
return ((num >> @intCast(
|
|
260
|
+
return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
|
|
246
261
|
}
|
|
247
262
|
|
|
248
263
|
/// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
|
|
249
|
-
pub
|
|
264
|
+
pub fn init(status: Status) u8 {
|
|
250
265
|
assert(status != .SLP and status != .EXT);
|
|
251
|
-
return @as(u8, 1) << @intCast(
|
|
266
|
+
return @as(u8, 1) << @intCast(@intFromEnum(status));
|
|
252
267
|
}
|
|
253
268
|
|
|
254
269
|
/// Initializes a non self-inflicted sleep status with duration `dur`.
|
|
255
|
-
pub
|
|
270
|
+
pub fn slp(dur: u3) u8 {
|
|
256
271
|
assert(dur > 0);
|
|
257
272
|
return @as(u8, dur);
|
|
258
273
|
}
|
|
259
274
|
|
|
260
275
|
/// Initializes a self-inflicted sleep status with duration `dur`.
|
|
261
|
-
pub
|
|
276
|
+
pub fn slf(dur: u3) u8 {
|
|
262
277
|
assert(dur > 0);
|
|
263
278
|
return 0x80 | slp(dur);
|
|
264
279
|
}
|
|
265
280
|
|
|
266
281
|
/// Returns the duration of a sleep status.
|
|
267
|
-
pub
|
|
268
|
-
return @intCast(
|
|
282
|
+
pub fn duration(num: u8) u3 {
|
|
283
|
+
return @intCast(num & SLEEP);
|
|
269
284
|
}
|
|
270
285
|
|
|
271
286
|
/// Returns whether `num` reflects any status.
|
|
272
|
-
pub
|
|
287
|
+
pub fn any(num: u8) bool {
|
|
273
288
|
return num > 0;
|
|
274
289
|
}
|
|
275
290
|
|
|
@@ -307,7 +322,7 @@ test Status {
|
|
|
307
322
|
}
|
|
308
323
|
|
|
309
324
|
/// Bitfield representation of volatile statuses and associated data in Generation I.
|
|
310
|
-
pub const Volatiles = packed struct {
|
|
325
|
+
pub const Volatiles = packed struct(u64) {
|
|
311
326
|
/// Whether the "Bide" volatile status is present.
|
|
312
327
|
Bide: bool = false,
|
|
313
328
|
/// Whether the "Thrashing" volatile status is present.
|
|
@@ -360,19 +375,15 @@ pub const Volatiles = packed struct {
|
|
|
360
375
|
/// The identity of whom the active Pokémon is transformed into.
|
|
361
376
|
transform: u4 = 0,
|
|
362
377
|
/// The remaining turns the move is disabled.
|
|
363
|
-
|
|
378
|
+
disable_duration: u4 = 0,
|
|
364
379
|
/// The move slot (1-4) that is disabled.
|
|
365
|
-
|
|
380
|
+
disable_move: u3 = 0,
|
|
366
381
|
/// The number of turns toxic damage has been accumulating.
|
|
367
382
|
toxic: u5 = 0,
|
|
368
|
-
|
|
369
|
-
comptime {
|
|
370
|
-
assert(@sizeOf(Volatiles) == 8);
|
|
371
|
-
}
|
|
372
383
|
};
|
|
373
384
|
|
|
374
385
|
test Volatiles {
|
|
375
|
-
var volatiles =
|
|
386
|
+
var volatiles: Volatiles = .{};
|
|
376
387
|
volatiles.Confusion = true;
|
|
377
388
|
volatiles.confusion = 2;
|
|
378
389
|
volatiles.Thrashing = true;
|
|
@@ -381,8 +392,8 @@ test Volatiles {
|
|
|
381
392
|
volatiles.Substitute = true;
|
|
382
393
|
volatiles.substitute = 42;
|
|
383
394
|
volatiles.toxic = 4;
|
|
384
|
-
volatiles.
|
|
385
|
-
volatiles.
|
|
395
|
+
volatiles.disable_move = 2;
|
|
396
|
+
volatiles.disable_duration = 4;
|
|
386
397
|
|
|
387
398
|
try expect(volatiles.Confusion);
|
|
388
399
|
try expect(volatiles.Thrashing);
|
|
@@ -393,14 +404,14 @@ test Volatiles {
|
|
|
393
404
|
|
|
394
405
|
try expectEqual(@as(u16, 235), volatiles.state);
|
|
395
406
|
try expectEqual(@as(u8, 42), volatiles.substitute);
|
|
396
|
-
try expectEqual(@as(u4, 2), volatiles.
|
|
397
|
-
try expectEqual(@as(u4, 4), volatiles.
|
|
398
|
-
try expectEqual(@as(
|
|
407
|
+
try expectEqual(@as(u4, 2), volatiles.disable_move);
|
|
408
|
+
try expectEqual(@as(u4, 4), volatiles.disable_duration);
|
|
409
|
+
try expectEqual(@as(u3, 2), volatiles.confusion);
|
|
399
410
|
try expectEqual(@as(u5, 4), volatiles.toxic);
|
|
400
|
-
try expectEqual(@as(
|
|
411
|
+
try expectEqual(@as(u3, 3), volatiles.attacks);
|
|
401
412
|
}
|
|
402
413
|
|
|
403
|
-
///
|
|
414
|
+
/// Representation of a Pokémon's stats in Generation I.
|
|
404
415
|
pub fn Stats(comptime T: type) type {
|
|
405
416
|
return extern struct {
|
|
406
417
|
hp: T = 0,
|
|
@@ -412,10 +423,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
412
423
|
/// Computes the value of `stat` given a `base`, `dv`, stat `exp` and `level`.
|
|
413
424
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
414
425
|
assert(level > 0 and level <= 100);
|
|
415
|
-
const
|
|
426
|
+
const float: f32 = @floatFromInt(exp);
|
|
427
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
416
428
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
417
429
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
418
|
-
return @intCast(
|
|
430
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
419
431
|
}
|
|
420
432
|
};
|
|
421
433
|
}
|
|
@@ -433,7 +445,7 @@ test Stats {
|
|
|
433
445
|
}
|
|
434
446
|
|
|
435
447
|
/// Representation of a Pokémon's boosts in Generation I.
|
|
436
|
-
pub const Boosts = packed struct {
|
|
448
|
+
pub const Boosts = packed struct(u32) {
|
|
437
449
|
/// A Pokémon's Attack boosts.
|
|
438
450
|
atk: i4 = 0,
|
|
439
451
|
/// A Pokémon's Defense boosts.
|
|
@@ -448,14 +460,10 @@ pub const Boosts = packed struct {
|
|
|
448
460
|
evasion: i4 = 0,
|
|
449
461
|
|
|
450
462
|
_: u8 = 0,
|
|
451
|
-
|
|
452
|
-
comptime {
|
|
453
|
-
assert(@sizeOf(Boosts) == 4);
|
|
454
|
-
}
|
|
455
463
|
};
|
|
456
464
|
|
|
457
465
|
test Boosts {
|
|
458
|
-
const boosts =
|
|
466
|
+
const boosts: Boosts = .{ .spc = -6 };
|
|
459
467
|
try expectEqual(0, boosts.atk);
|
|
460
468
|
try expectEqual(-6, boosts.spc);
|
|
461
469
|
}
|
|
@@ -464,10 +472,10 @@ test Boosts {
|
|
|
464
472
|
pub const Move = moves.Move;
|
|
465
473
|
|
|
466
474
|
test Move {
|
|
467
|
-
try expectEqual(2, @
|
|
475
|
+
try expectEqual(2, @intFromEnum(Move.KarateChop));
|
|
468
476
|
const move = Move.get(.Fissure);
|
|
469
477
|
try expectEqual(Move.Effect.OHKO, move.effect);
|
|
470
|
-
try expectEqual(
|
|
478
|
+
try expectEqual(rng.Gen12.percent(30), move.accuracy);
|
|
471
479
|
try expectEqual(Type.Ground, move.type);
|
|
472
480
|
|
|
473
481
|
try expect(!Move.Effect.onBegin(.None));
|
|
@@ -525,7 +533,7 @@ test Move {
|
|
|
525
533
|
pub const Species = species.Species;
|
|
526
534
|
|
|
527
535
|
test Species {
|
|
528
|
-
try expectEqual(2, @
|
|
536
|
+
try expectEqual(2, @intFromEnum(Species.Ivysaur));
|
|
529
537
|
try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
|
|
530
538
|
}
|
|
531
539
|
|
|
@@ -542,9 +550,11 @@ pub const Effectiveness = enum(u8) {
|
|
|
542
550
|
Super = 20,
|
|
543
551
|
|
|
544
552
|
/// Used to determine the effectiness of a neutral damage hit.
|
|
545
|
-
pub const neutral: u16 =
|
|
553
|
+
pub const neutral: u16 =
|
|
554
|
+
@intFromEnum(Effectiveness.Neutral) * @intFromEnum(Effectiveness.Neutral);
|
|
546
555
|
/// Used to detect whether there is a mismatch in type effectivness (relevant for precedence).
|
|
547
|
-
pub const mismatch: u16 =
|
|
556
|
+
pub const mismatch: u16 =
|
|
557
|
+
@intFromEnum(Effectiveness.Resisted) + @intFromEnum(Effectiveness.Super);
|
|
548
558
|
|
|
549
559
|
comptime {
|
|
550
560
|
assert(@sizeOf(Effectiveness) == 1);
|
|
@@ -552,8 +562,8 @@ pub const Effectiveness = enum(u8) {
|
|
|
552
562
|
};
|
|
553
563
|
|
|
554
564
|
test Types {
|
|
555
|
-
try expectEqual(14, @
|
|
556
|
-
try expectEqual(20, @
|
|
565
|
+
try expectEqual(14, @intFromEnum(Type.Dragon));
|
|
566
|
+
try expectEqual(20, @intFromEnum(Effectiveness.Super));
|
|
557
567
|
|
|
558
568
|
try expect(!Type.Ghost.special());
|
|
559
569
|
try expect(Type.Dragon.special());
|
|
@@ -603,16 +613,16 @@ pub const DVs = struct {
|
|
|
603
613
|
};
|
|
604
614
|
|
|
605
615
|
test DVs {
|
|
606
|
-
var dvs =
|
|
616
|
+
var dvs: DVs = .{ .spc = 15, .spe = 15 };
|
|
607
617
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
608
|
-
dvs =
|
|
618
|
+
dvs = .{
|
|
609
619
|
.atk = 5,
|
|
610
620
|
.def = 15,
|
|
611
621
|
.spe = 13,
|
|
612
622
|
.spc = 13,
|
|
613
623
|
};
|
|
614
624
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
615
|
-
dvs =
|
|
625
|
+
dvs = .{
|
|
616
626
|
.def = 3,
|
|
617
627
|
.spe = 10,
|
|
618
628
|
.spc = 11,
|