@pkmn/engine 0.1.0-dev.7963b917 → 0.1.0-dev.79957401
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 +70 -28
- 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 +26 -4
- package/build/pkg/addon.js +39 -71
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +17 -4
- 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 +15 -18
- package/build/pkg/gen1.js +30 -21
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +42 -91
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +29 -1
- package/build/pkg/protocol.js +123 -102
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +84 -73
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +66 -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 +360 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +157 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +397 -149
- package/package.json +62 -123
- package/src/bin/install-pkmn-engine +43 -22
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +201 -0
- package/src/lib/{binding → bindings}/node.zig +15 -32
- package/src/lib/bindings/wasm.zig +51 -0
- package/src/lib/common/array.zig +70 -0
- package/src/lib/common/data.zig +12 -24
- package/src/lib/common/debug.zig +46 -30
- package/src/lib/common/js.zig +3 -7
- package/src/lib/common/optional.zig +30 -24
- package/src/lib/common/options.zig +6 -0
- package/src/lib/common/protocol.zig +961 -450
- package/src/lib/common/rational.zig +341 -254
- package/src/lib/common/rng.zig +26 -19
- package/src/lib/common/util.zig +53 -1
- package/src/lib/gen1/calc.zig +726 -178
- package/src/lib/gen1/chance.zig +526 -324
- package/src/lib/gen1/data/moves.zig +193 -183
- package/src/lib/gen1/data/species.zig +4 -6
- package/src/lib/gen1/data/types.zig +9 -10
- package/src/lib/gen1/data.zig +78 -90
- package/src/lib/gen1/helpers.zig +114 -422
- package/src/lib/gen1/mechanics.zig +699 -615
- package/src/lib/gen2/calc.zig +150 -0
- package/src/lib/gen2/chance.zig +914 -0
- package/src/lib/gen2/data/items.zig +13 -21
- package/src/lib/gen2/data/moves.zig +664 -572
- package/src/lib/gen2/data/species.zig +2 -4
- package/src/lib/gen2/data/types.zig +139 -34
- package/src/lib/gen2/data.zig +131 -103
- package/src/lib/gen2/generated.zig +801 -0
- package/src/lib/gen2/helpers.zig +76 -51
- package/src/lib/gen2/mechanics.zig +3569 -29
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +14 -0
- package/src/lib/pkmn.zig +125 -29
- package/src/lib/wasm.zig +13 -0
- package/src/tools/display/dom.ts +108 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/binding/wasm.zig +0 -12
- package/src/lib/gen1/README.md +0 -812
- package/src/lib/gen2/README.md +0 -308
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
//! Code generated by `tools/generate` - manual edits will be overwritten
|
|
2
|
-
|
|
3
|
-
const std = @import("std");
|
|
1
|
+
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
4
2
|
|
|
5
3
|
const 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,8 +1089,8 @@ 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
1095
|
return CHANCES[@intFromEnum(id) - 1];
|
|
1098
1096
|
}
|
|
@@ -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,16 +89,16 @@ 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
|
|
92
|
+
pub fn special(self: Type) bool {
|
|
94
93
|
return @intFromEnum(self) >= @intFromEnum(Type.Fire);
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
/// The `Effectiveness` of type `t2` vs. type `t1`.
|
|
98
|
-
pub
|
|
97
|
+
pub fn effectiveness(t1: Type, t2: Type) Effectiveness {
|
|
99
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
104
|
if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
|
|
@@ -109,7 +108,7 @@ pub const Type = enum(u4) {
|
|
|
109
108
|
};
|
|
110
109
|
|
|
111
110
|
/// Representation of a Pokémon's typing.
|
|
112
|
-
pub const Types = packed struct {
|
|
111
|
+
pub const Types = packed struct(u8) {
|
|
113
112
|
/// A Pokémon's primary type.
|
|
114
113
|
type1: Type = .Normal,
|
|
115
114
|
/// A Pokémon's secondary type (may be identical to its primary type).
|
|
@@ -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,20 +1,16 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
1
|
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
2
|
+
const calc = @import("calc.zig");
|
|
3
|
+
const chance = @import("chance.zig");
|
|
6
4
|
const data = @import("../common/data.zig");
|
|
5
|
+
const mechanics = @import("mechanics.zig");
|
|
6
|
+
const moves = @import("data/moves.zig");
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
7
8
|
const protocol = @import("../common/protocol.zig");
|
|
8
9
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
|
-
|
|
11
|
-
const moves = @import("data/moves.zig");
|
|
12
10
|
const species = @import("data/species.zig");
|
|
11
|
+
const std = @import("std");
|
|
13
12
|
const types = @import("data/types.zig");
|
|
14
|
-
|
|
15
|
-
const calc = @import("calc.zig");
|
|
16
|
-
const chance = @import("chance.zig");
|
|
17
|
-
const mechanics = @import("mechanics.zig");
|
|
13
|
+
const util = @import("../common/util.zig");
|
|
18
14
|
|
|
19
15
|
const assert = std.debug.assert;
|
|
20
16
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -22,18 +18,21 @@ const expect = std.testing.expect;
|
|
|
22
18
|
|
|
23
19
|
/// The minimum size in bytes required to hold all Generation I choice options.
|
|
24
20
|
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
25
|
-
/// The
|
|
26
|
-
/// At least as large as MAX_CHOICES.
|
|
21
|
+
/// The maximum number of bytes possibly logged by a single Generation I update.
|
|
27
22
|
pub const MAX_LOGS: usize = 180;
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
const ReleaseSmall: std.builtin.OptimizeMode =
|
|
25
|
+
if (@hasDecl(std.builtin.OptimizeMode, "ReleaseSmall")) .small else .ReleaseSmall;
|
|
26
|
+
|
|
27
|
+
/// The optimal size in bytes required to hold all Generation I choice options.
|
|
28
|
+
/// At least as large as MAX_CHOICES.
|
|
29
|
+
pub const CHOICES_SIZE = if (builtin.mode == ReleaseSmall)
|
|
31
30
|
MAX_CHOICES
|
|
32
31
|
else
|
|
33
32
|
std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
|
|
34
33
|
/// The optimal size in bytes required to hold the largest amount of log data possible from a
|
|
35
34
|
/// single Generation I update. At least as large as MAX_LOGS.
|
|
36
|
-
pub const LOGS_SIZE = if (builtin.mode ==
|
|
35
|
+
pub const LOGS_SIZE = if (builtin.mode == ReleaseSmall)
|
|
37
36
|
MAX_LOGS
|
|
38
37
|
else
|
|
39
38
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
@@ -47,6 +46,7 @@ const Player = data.Player;
|
|
|
47
46
|
const Result = data.Result;
|
|
48
47
|
|
|
49
48
|
const PointerType = util.PointerType;
|
|
49
|
+
const isPointerTo = util.isPointerTo;
|
|
50
50
|
|
|
51
51
|
const showdown = pkmn.options.showdown;
|
|
52
52
|
|
|
@@ -65,33 +65,33 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
65
65
|
/// The last damage dealt by either side.
|
|
66
66
|
last_damage: u16 = 0,
|
|
67
67
|
/// Details about the last move selected/used by each player required to detect desyncs.
|
|
68
|
-
last_moves: [2]MoveDetails =
|
|
68
|
+
last_moves: [2]MoveDetails = @splat(.{}),
|
|
69
69
|
/// The pseudo random number generator.
|
|
70
70
|
rng: RNG,
|
|
71
71
|
|
|
72
72
|
/// Returns the `Side` for the given `player`.
|
|
73
|
-
pub
|
|
74
|
-
assert(
|
|
73
|
+
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
74
|
+
assert(isPointerTo(self, Self));
|
|
75
75
|
return &self.sides[@intFromEnum(player)];
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/// Returns the `Side` of the opponent for the given `player`
|
|
79
|
-
pub
|
|
80
|
-
assert(
|
|
79
|
+
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
80
|
+
assert(isPointerTo(self, Self));
|
|
81
81
|
return &self.sides[@intFromEnum(player.foe())];
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/// Returns an identifier for the active Pokémon of `player`.
|
|
85
|
-
pub
|
|
85
|
+
pub fn active(self: *const Self, player: Player) ID {
|
|
86
86
|
return player.ident(@intCast(self.side(player).order[0]));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/// Returns the `MoveDetails` for the given `player`.
|
|
90
|
-
pub
|
|
90
|
+
pub fn lastMove(
|
|
91
91
|
self: anytype,
|
|
92
92
|
player: Player,
|
|
93
93
|
) PointerType(@TypeOf(self), MoveDetails) {
|
|
94
|
-
assert(
|
|
94
|
+
assert(isPointerTo(self, Self));
|
|
95
95
|
return &self.last_moves[@intFromEnum(player)];
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -112,7 +112,7 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
112
112
|
/// make (i.e. on the cartridge a soft-lock occurs).
|
|
113
113
|
///
|
|
114
114
|
/// This function will always return choices > 0 if `pkmn.options.showdown` is true.
|
|
115
|
-
pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
115
|
+
pub fn choices(self: *const Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
116
116
|
return mechanics.choices(self, player, request, out);
|
|
117
117
|
}
|
|
118
118
|
};
|
|
@@ -125,12 +125,12 @@ test Battle {
|
|
|
125
125
|
/// Representation of one side of a Generation I Pokémon battle.
|
|
126
126
|
pub const Side = extern struct {
|
|
127
127
|
/// The player's party in its original order
|
|
128
|
-
pokemon: [6]Pokemon =
|
|
128
|
+
pokemon: [6]Pokemon = @splat(.{}),
|
|
129
129
|
/// The active Pokémon for the side, zero initialized if the battle has yet to start.
|
|
130
130
|
/// Note that fainted Pokémon are still consider "active" until their replacement switches in.
|
|
131
131
|
active: ActivePokemon = .{},
|
|
132
132
|
/// One-based slot indexes reflecting the current order of the player's party.
|
|
133
|
-
order: [6]u8 =
|
|
133
|
+
order: [6]u8 = @splat(0),
|
|
134
134
|
/// The last move the player selected.
|
|
135
135
|
last_selected_move: Move = .None,
|
|
136
136
|
/// The last move the player used.
|
|
@@ -141,8 +141,8 @@ pub const Side = extern struct {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
|
|
144
|
-
pub
|
|
145
|
-
assert(
|
|
144
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
145
|
+
assert(isPointerTo(self, Side));
|
|
146
146
|
assert(slot > 0 and slot <= 6);
|
|
147
147
|
const id = self.order[slot - 1];
|
|
148
148
|
assert(id > 0 and id <= 6);
|
|
@@ -150,8 +150,8 @@ pub const Side = extern struct {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
|
|
153
|
-
pub
|
|
154
|
-
assert(
|
|
153
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
154
|
+
assert(isPointerTo(self, Side));
|
|
155
155
|
return self.get(1);
|
|
156
156
|
}
|
|
157
157
|
};
|
|
@@ -169,15 +169,15 @@ pub const ActivePokemon = extern struct {
|
|
|
169
169
|
/// The active Pokémon's volatile statuses and associated data.
|
|
170
170
|
volatiles: Volatiles = .{},
|
|
171
171
|
/// The active Pokémon's current move slots.
|
|
172
|
-
moves: [4]MoveSlot =
|
|
172
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
173
173
|
|
|
174
174
|
comptime {
|
|
175
175
|
assert(@sizeOf(ActivePokemon) == 32);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
/// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
|
|
179
|
-
pub
|
|
180
|
-
assert(
|
|
179
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
180
|
+
assert(isPointerTo(self, ActivePokemon));
|
|
181
181
|
assert(mslot > 0 and mslot <= 4);
|
|
182
182
|
assert(self.moves[mslot - 1].id != .None);
|
|
183
183
|
return &self.moves[mslot - 1];
|
|
@@ -189,7 +189,7 @@ pub const Pokemon = extern struct {
|
|
|
189
189
|
/// The Pokémon's unmodified stats.
|
|
190
190
|
stats: Stats(u16) = .{},
|
|
191
191
|
/// The Pokémon's original stored move slots.
|
|
192
|
-
moves: [4]MoveSlot =
|
|
192
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
193
193
|
/// The Pokémon's current HP.
|
|
194
194
|
hp: u16 = 0,
|
|
195
195
|
/// The Pokémon's current status.
|
|
@@ -206,15 +206,15 @@ pub const Pokemon = extern struct {
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
/// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
|
|
209
|
-
pub
|
|
210
|
-
assert(
|
|
209
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
210
|
+
assert(isPointerTo(self, Pokemon));
|
|
211
211
|
assert(mslot > 0 and mslot <= 4);
|
|
212
212
|
assert(self.moves[mslot - 1].id != .None);
|
|
213
213
|
return &self.moves[mslot - 1];
|
|
214
214
|
}
|
|
215
215
|
};
|
|
216
216
|
|
|
217
|
-
/// Representation of a Generation I
|
|
217
|
+
/// Representation of a Generation I Pokémon's move slot in a battle.
|
|
218
218
|
pub const MoveSlot = extern struct {
|
|
219
219
|
/// The identifier for the move.
|
|
220
220
|
id: Move = .None,
|
|
@@ -222,20 +222,16 @@ pub const MoveSlot = extern struct {
|
|
|
222
222
|
pp: u8 = 0,
|
|
223
223
|
|
|
224
224
|
comptime {
|
|
225
|
-
assert(@sizeOf(MoveSlot) ==
|
|
225
|
+
assert(@sizeOf(MoveSlot) == 2);
|
|
226
226
|
}
|
|
227
227
|
};
|
|
228
228
|
|
|
229
229
|
/// Details required to detect desyncs based on the move last selected/executed by players.
|
|
230
|
-
pub const MoveDetails = packed struct {
|
|
230
|
+
pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
|
|
231
231
|
/// The move slot index of the last move selected in the battle menu.
|
|
232
232
|
index: if (showdown) u8 else u4 = 0,
|
|
233
233
|
/// Whether the last move executed was counterable.
|
|
234
234
|
counterable: if (showdown) u8 else u4 = 0,
|
|
235
|
-
|
|
236
|
-
comptime {
|
|
237
|
-
assert(@sizeOf(MoveDetails) == if (showdown) 2 else 1);
|
|
238
|
-
}
|
|
239
235
|
};
|
|
240
236
|
|
|
241
237
|
/// Bitfield representation of a Generation I & II Pokémon's major status condition.
|
|
@@ -259,39 +255,39 @@ pub const Status = enum(u8) {
|
|
|
259
255
|
|
|
260
256
|
pub const TOX = 0b10001000;
|
|
261
257
|
|
|
262
|
-
const
|
|
258
|
+
const SLEEP = 0b111;
|
|
263
259
|
|
|
264
260
|
/// Whether or not the status `num` is the same as `status`.
|
|
265
|
-
pub
|
|
261
|
+
pub fn is(num: u8, status: Status) bool {
|
|
266
262
|
if (status == .SLP) return Status.duration(num) > 0;
|
|
267
263
|
return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
|
|
268
264
|
}
|
|
269
265
|
|
|
270
266
|
/// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
|
|
271
|
-
pub
|
|
267
|
+
pub fn init(status: Status) u8 {
|
|
272
268
|
assert(status != .SLP and status != .EXT);
|
|
273
269
|
return @as(u8, 1) << @intCast(@intFromEnum(status));
|
|
274
270
|
}
|
|
275
271
|
|
|
276
272
|
/// Initializes a non self-inflicted sleep status with duration `dur`.
|
|
277
|
-
pub
|
|
273
|
+
pub fn slp(dur: u3) u8 {
|
|
278
274
|
assert(dur > 0);
|
|
279
275
|
return @as(u8, dur);
|
|
280
276
|
}
|
|
281
277
|
|
|
282
278
|
/// Initializes a self-inflicted sleep status with duration `dur`.
|
|
283
|
-
pub
|
|
279
|
+
pub fn slf(dur: u3) u8 {
|
|
284
280
|
assert(dur > 0);
|
|
285
281
|
return 0x80 | slp(dur);
|
|
286
282
|
}
|
|
287
283
|
|
|
288
284
|
/// Returns the duration of a sleep status.
|
|
289
|
-
pub
|
|
290
|
-
return @intCast(num &
|
|
285
|
+
pub fn duration(num: u8) u3 {
|
|
286
|
+
return @intCast(num & SLEEP);
|
|
291
287
|
}
|
|
292
288
|
|
|
293
289
|
/// Returns whether `num` reflects any status.
|
|
294
|
-
pub
|
|
290
|
+
pub fn any(num: u8) bool {
|
|
295
291
|
return num > 0;
|
|
296
292
|
}
|
|
297
293
|
|
|
@@ -320,16 +316,16 @@ test Status {
|
|
|
320
316
|
try expect(Status.is(Status.slp(5), .SLP));
|
|
321
317
|
try expect(!Status.is(Status.slp(5), .EXT));
|
|
322
318
|
try expect(!Status.is(Status.slp(7), .PSN));
|
|
323
|
-
try expectEqual(
|
|
319
|
+
try expectEqual(5, Status.duration(Status.slp(5)));
|
|
324
320
|
try expect(Status.is(Status.slf(2), .SLP));
|
|
325
321
|
try expect(Status.is(Status.slf(2), .EXT));
|
|
326
322
|
try expect(Status.is(Status.TOX, .PSN));
|
|
327
323
|
try expect(Status.is(Status.TOX, .EXT));
|
|
328
|
-
try expectEqual(
|
|
324
|
+
try expectEqual(1, Status.duration(Status.slp(1)));
|
|
329
325
|
}
|
|
330
326
|
|
|
331
327
|
/// Bitfield representation of volatile statuses and associated data in Generation I.
|
|
332
|
-
pub const Volatiles = packed struct {
|
|
328
|
+
pub const Volatiles = packed struct(u64) {
|
|
333
329
|
/// Whether the "Bide" volatile status is present.
|
|
334
330
|
Bide: bool = false,
|
|
335
331
|
/// Whether the "Thrashing" volatile status is present.
|
|
@@ -382,19 +378,15 @@ pub const Volatiles = packed struct {
|
|
|
382
378
|
/// The identity of whom the active Pokémon is transformed into.
|
|
383
379
|
transform: u4 = 0,
|
|
384
380
|
/// The remaining turns the move is disabled.
|
|
385
|
-
|
|
381
|
+
disable_duration: u4 = 0,
|
|
386
382
|
/// The move slot (1-4) that is disabled.
|
|
387
|
-
|
|
383
|
+
disable_move: u3 = 0,
|
|
388
384
|
/// The number of turns toxic damage has been accumulating.
|
|
389
385
|
toxic: u5 = 0,
|
|
390
|
-
|
|
391
|
-
comptime {
|
|
392
|
-
assert(@sizeOf(Volatiles) == 8);
|
|
393
|
-
}
|
|
394
386
|
};
|
|
395
387
|
|
|
396
388
|
test Volatiles {
|
|
397
|
-
var volatiles =
|
|
389
|
+
var volatiles: Volatiles = .{};
|
|
398
390
|
volatiles.Confusion = true;
|
|
399
391
|
volatiles.confusion = 2;
|
|
400
392
|
volatiles.Thrashing = true;
|
|
@@ -403,8 +395,8 @@ test Volatiles {
|
|
|
403
395
|
volatiles.Substitute = true;
|
|
404
396
|
volatiles.substitute = 42;
|
|
405
397
|
volatiles.toxic = 4;
|
|
406
|
-
volatiles.
|
|
407
|
-
volatiles.
|
|
398
|
+
volatiles.disable_move = 2;
|
|
399
|
+
volatiles.disable_duration = 4;
|
|
408
400
|
|
|
409
401
|
try expect(volatiles.Confusion);
|
|
410
402
|
try expect(volatiles.Thrashing);
|
|
@@ -413,16 +405,16 @@ test Volatiles {
|
|
|
413
405
|
try expect(!volatiles.Transform);
|
|
414
406
|
try expect(!volatiles.MultiHit);
|
|
415
407
|
|
|
416
|
-
try expectEqual(
|
|
417
|
-
try expectEqual(
|
|
418
|
-
try expectEqual(
|
|
419
|
-
try expectEqual(
|
|
420
|
-
try expectEqual(
|
|
421
|
-
try expectEqual(
|
|
422
|
-
try expectEqual(
|
|
408
|
+
try expectEqual(235, volatiles.state);
|
|
409
|
+
try expectEqual(42, volatiles.substitute);
|
|
410
|
+
try expectEqual(2, volatiles.disable_move);
|
|
411
|
+
try expectEqual(4, volatiles.disable_duration);
|
|
412
|
+
try expectEqual(2, volatiles.confusion);
|
|
413
|
+
try expectEqual(4, volatiles.toxic);
|
|
414
|
+
try expectEqual(3, volatiles.attacks);
|
|
423
415
|
}
|
|
424
416
|
|
|
425
|
-
///
|
|
417
|
+
/// Representation of a Pokémon's stats in Generation I.
|
|
426
418
|
pub fn Stats(comptime T: type) type {
|
|
427
419
|
return extern struct {
|
|
428
420
|
hp: T = 0,
|
|
@@ -450,13 +442,13 @@ test Stats {
|
|
|
450
442
|
try expectEqual(0, stats.def);
|
|
451
443
|
|
|
452
444
|
var base = Species.get(.Gyarados).stats;
|
|
453
|
-
try expectEqual(
|
|
445
|
+
try expectEqual(127, Stats(u16).calc("hp", base.hp, 0, 5120, 38));
|
|
454
446
|
base = Species.get(.Pidgeot).stats;
|
|
455
|
-
try expectEqual(
|
|
447
|
+
try expectEqual(279, Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
456
448
|
}
|
|
457
449
|
|
|
458
450
|
/// Representation of a Pokémon's boosts in Generation I.
|
|
459
|
-
pub const Boosts = packed struct {
|
|
451
|
+
pub const Boosts = packed struct(u32) {
|
|
460
452
|
/// A Pokémon's Attack boosts.
|
|
461
453
|
atk: i4 = 0,
|
|
462
454
|
/// A Pokémon's Defense boosts.
|
|
@@ -471,14 +463,10 @@ pub const Boosts = packed struct {
|
|
|
471
463
|
evasion: i4 = 0,
|
|
472
464
|
|
|
473
465
|
_: u8 = 0,
|
|
474
|
-
|
|
475
|
-
comptime {
|
|
476
|
-
assert(@sizeOf(Boosts) == 4);
|
|
477
|
-
}
|
|
478
466
|
};
|
|
479
467
|
|
|
480
468
|
test Boosts {
|
|
481
|
-
const boosts =
|
|
469
|
+
const boosts: Boosts = .{ .spc = -6 };
|
|
482
470
|
try expectEqual(0, boosts.atk);
|
|
483
471
|
try expectEqual(-6, boosts.spc);
|
|
484
472
|
}
|
|
@@ -490,7 +478,7 @@ test Move {
|
|
|
490
478
|
try expectEqual(2, @intFromEnum(Move.KarateChop));
|
|
491
479
|
const move = Move.get(.Fissure);
|
|
492
480
|
try expectEqual(Move.Effect.OHKO, move.effect);
|
|
493
|
-
try expectEqual(
|
|
481
|
+
try expectEqual(rng.Gen12.percent(30), move.accuracy);
|
|
494
482
|
try expectEqual(Type.Ground, move.type);
|
|
495
483
|
|
|
496
484
|
try expect(!Move.Effect.onBegin(.None));
|
|
@@ -549,7 +537,7 @@ pub const Species = species.Species;
|
|
|
549
537
|
|
|
550
538
|
test Species {
|
|
551
539
|
try expectEqual(2, @intFromEnum(Species.Ivysaur));
|
|
552
|
-
try expectEqual(
|
|
540
|
+
try expectEqual(100, Species.get(.Mew).stats.def);
|
|
553
541
|
}
|
|
554
542
|
|
|
555
543
|
/// Representation of a Generation I type in Pokémon.
|
|
@@ -588,8 +576,8 @@ test Types {
|
|
|
588
576
|
try expectEqual(Effectiveness.Resisted, Type.effectiveness(.Fire, .Water));
|
|
589
577
|
try expectEqual(Effectiveness.Neutral, Type.effectiveness(.Normal, .Grass));
|
|
590
578
|
|
|
591
|
-
try expectEqual(
|
|
592
|
-
try expectEqual(
|
|
579
|
+
try expectEqual(3, Type.precedence(.Water, .Water));
|
|
580
|
+
try expectEqual(26, Type.precedence(.Bug, .Ghost));
|
|
593
581
|
try expect(Type.precedence(.Poison, .Bug) > Type.precedence(.Poison, .Poison));
|
|
594
582
|
|
|
595
583
|
const t: Types = .{ .type1 = .Rock, .type2 = .Ground };
|
|
@@ -628,19 +616,19 @@ pub const DVs = struct {
|
|
|
628
616
|
};
|
|
629
617
|
|
|
630
618
|
test DVs {
|
|
631
|
-
var dvs =
|
|
632
|
-
try expectEqual(
|
|
633
|
-
dvs =
|
|
619
|
+
var dvs: DVs = .{ .spc = 15, .spe = 15 };
|
|
620
|
+
try expectEqual(15, dvs.hp());
|
|
621
|
+
dvs = .{
|
|
634
622
|
.atk = 5,
|
|
635
623
|
.def = 15,
|
|
636
624
|
.spe = 13,
|
|
637
625
|
.spc = 13,
|
|
638
626
|
};
|
|
639
|
-
try expectEqual(
|
|
640
|
-
dvs =
|
|
627
|
+
try expectEqual(15, dvs.hp());
|
|
628
|
+
dvs = .{
|
|
641
629
|
.def = 3,
|
|
642
630
|
.spe = 10,
|
|
643
631
|
.spc = 11,
|
|
644
632
|
};
|
|
645
|
-
try expectEqual(
|
|
633
|
+
try expectEqual(13, dvs.hp());
|
|
646
634
|
}
|