@pkmn/engine 0.1.0-dev.d716e940 → 0.1.0-dev.d73cd76c
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 +114 -69
- 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 -70
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +39 -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 +43 -37
- 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 +428 -176
- package/package.json +72 -122
- package/src/bin/install-pkmn-engine +56 -25
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +201 -0
- package/src/lib/bindings/node.zig +117 -0
- package/src/lib/bindings/wasm.zig +51 -0
- package/src/lib/common/array.zig +70 -0
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +19 -31
- package/src/lib/common/debug.zig +58 -33
- package/src/lib/common/js.zig +114 -0
- package/src/lib/common/optional.zig +71 -0
- package/src/lib/common/options.zig +15 -0
- package/src/lib/common/protocol.zig +1037 -462
- package/src/lib/common/rational.zig +440 -0
- package/src/lib/common/rng.zig +31 -24
- package/src/lib/common/util.zig +64 -0
- package/src/lib/gen1/calc.zig +975 -0
- package/src/lib/gen1/chance.zig +1246 -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 +12 -13
- package/src/lib/gen1/data.zig +98 -89
- package/src/lib/gen1/helpers.zig +159 -51
- package/src/lib/gen1/mechanics.zig +1211 -846
- package/src/lib/gen2/calc.zig +150 -0
- package/src/lib/gen2/chance.zig +914 -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 +136 -100
- package/src/lib/gen2/generated.zig +801 -0
- package/src/lib/gen2/helpers.zig +81 -58
- package/src/lib/gen2/mechanics.zig +3582 -30
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +14 -0
- package/src/lib/pkmn.zig +172 -17
- 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/node.zig +0 -229
- package/src/lib/binding/wasm.zig +0 -10
- package/src/lib/gen1/README.md +0 -812
- 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,26 +89,26 @@ 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
|
}
|
|
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,16 +1,16 @@
|
|
|
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;
|
|
@@ -18,11 +18,11 @@ const expect = std.testing.expect;
|
|
|
18
18
|
|
|
19
19
|
/// The minimum size in bytes required to hold all Generation I choice options.
|
|
20
20
|
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
21
|
-
/// The
|
|
22
|
-
/// At least as large as MAX_CHOICES.
|
|
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
|
|
24
|
+
/// The optimal size in bytes required to hold all Generation I choice options.
|
|
25
|
+
/// At least as large as MAX_CHOICES.
|
|
26
26
|
pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
27
27
|
MAX_CHOICES
|
|
28
28
|
else
|
|
@@ -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);
|
|
@@ -56,29 +62,41 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
56
62
|
/// The last damage dealt by either side.
|
|
57
63
|
last_damage: u16 = 0,
|
|
58
64
|
/// Details about the last move selected/used by each player required to detect desyncs.
|
|
59
|
-
last_moves: MoveDetails = .{},
|
|
65
|
+
last_moves: [2]MoveDetails = @splat(.{}),
|
|
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 message logs to `log` if `options.log`
|
|
80
|
-
|
|
81
|
-
|
|
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);
|
|
82
100
|
}
|
|
83
101
|
|
|
84
102
|
/// Fills in at most `out.len` possible choices for the `player` given the previous `result`
|
|
@@ -90,8 +108,8 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
90
108
|
/// interacts with Disable, in which case there are no possible choices for the player to
|
|
91
109
|
/// make (i.e. on the cartridge a soft-lock occurs).
|
|
92
110
|
///
|
|
93
|
-
/// This function will always return
|
|
94
|
-
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 {
|
|
95
113
|
return mechanics.choices(self, player, request, out);
|
|
96
114
|
}
|
|
97
115
|
};
|
|
@@ -104,12 +122,12 @@ test Battle {
|
|
|
104
122
|
/// Representation of one side of a Generation I Pokémon battle.
|
|
105
123
|
pub const Side = extern struct {
|
|
106
124
|
/// The player's party in its original order
|
|
107
|
-
pokemon: [6]Pokemon =
|
|
125
|
+
pokemon: [6]Pokemon = @splat(.{}),
|
|
108
126
|
/// The active Pokémon for the side, zero initialized if the battle has yet to start.
|
|
109
127
|
/// Note that fainted Pokémon are still consider "active" until their replacement switches in.
|
|
110
128
|
active: ActivePokemon = .{},
|
|
111
129
|
/// One-based slot indexes reflecting the current order of the player's party.
|
|
112
|
-
order: [6]u8 =
|
|
130
|
+
order: [6]u8 = @splat(0),
|
|
113
131
|
/// The last move the player selected.
|
|
114
132
|
last_selected_move: Move = .None,
|
|
115
133
|
/// The last move the player used.
|
|
@@ -120,7 +138,8 @@ pub const Side = extern struct {
|
|
|
120
138
|
}
|
|
121
139
|
|
|
122
140
|
/// Returns the stored `Pokemon` corresponding to the one-indexed party `slot`.
|
|
123
|
-
pub
|
|
141
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
142
|
+
assert(isPointerTo(self, Side));
|
|
124
143
|
assert(slot > 0 and slot <= 6);
|
|
125
144
|
const id = self.order[slot - 1];
|
|
126
145
|
assert(id > 0 and id <= 6);
|
|
@@ -128,7 +147,8 @@ pub const Side = extern struct {
|
|
|
128
147
|
}
|
|
129
148
|
|
|
130
149
|
/// Returns the stored `Pokemon` corresponding to the `active` Pokémon (slot 1).
|
|
131
|
-
pub
|
|
150
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
151
|
+
assert(isPointerTo(self, Side));
|
|
132
152
|
return self.get(1);
|
|
133
153
|
}
|
|
134
154
|
};
|
|
@@ -146,14 +166,15 @@ pub const ActivePokemon = extern struct {
|
|
|
146
166
|
/// The active Pokémon's volatile statuses and associated data.
|
|
147
167
|
volatiles: Volatiles = .{},
|
|
148
168
|
/// The active Pokémon's current move slots.
|
|
149
|
-
moves: [4]MoveSlot =
|
|
169
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
150
170
|
|
|
151
171
|
comptime {
|
|
152
172
|
assert(@sizeOf(ActivePokemon) == 32);
|
|
153
173
|
}
|
|
154
174
|
|
|
155
175
|
/// Returns the active Pokémon's current move slot located at the one-indexed `mslot`.
|
|
156
|
-
pub
|
|
176
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
177
|
+
assert(isPointerTo(self, ActivePokemon));
|
|
157
178
|
assert(mslot > 0 and mslot <= 4);
|
|
158
179
|
assert(self.moves[mslot - 1].id != .None);
|
|
159
180
|
return &self.moves[mslot - 1];
|
|
@@ -165,7 +186,7 @@ pub const Pokemon = extern struct {
|
|
|
165
186
|
/// The Pokémon's unmodified stats.
|
|
166
187
|
stats: Stats(u16) = .{},
|
|
167
188
|
/// The Pokémon's original stored move slots.
|
|
168
|
-
moves: [4]MoveSlot =
|
|
189
|
+
moves: [4]MoveSlot = @splat(.{}),
|
|
169
190
|
/// The Pokémon's current HP.
|
|
170
191
|
hp: u16 = 0,
|
|
171
192
|
/// The Pokémon's current status.
|
|
@@ -182,14 +203,15 @@ pub const Pokemon = extern struct {
|
|
|
182
203
|
}
|
|
183
204
|
|
|
184
205
|
/// Returns the Pokémon's original move slot located at the one-indexed `mslot`.
|
|
185
|
-
pub
|
|
206
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
207
|
+
assert(isPointerTo(self, Pokemon));
|
|
186
208
|
assert(mslot > 0 and mslot <= 4);
|
|
187
209
|
assert(self.moves[mslot - 1].id != .None);
|
|
188
210
|
return &self.moves[mslot - 1];
|
|
189
211
|
}
|
|
190
212
|
};
|
|
191
213
|
|
|
192
|
-
/// Representation of a Generation I
|
|
214
|
+
/// Representation of a Generation I Pokémon's move slot in a battle.
|
|
193
215
|
pub const MoveSlot = extern struct {
|
|
194
216
|
/// The identifier for the move.
|
|
195
217
|
id: Move = .None,
|
|
@@ -197,24 +219,16 @@ pub const MoveSlot = extern struct {
|
|
|
197
219
|
pp: u8 = 0,
|
|
198
220
|
|
|
199
221
|
comptime {
|
|
200
|
-
assert(@sizeOf(MoveSlot) ==
|
|
222
|
+
assert(@sizeOf(MoveSlot) == 2);
|
|
201
223
|
}
|
|
202
224
|
};
|
|
203
225
|
|
|
204
226
|
/// Details required to detect desyncs based on the move last selected/executed by players.
|
|
205
|
-
pub const MoveDetails = packed struct {
|
|
206
|
-
/// The move slot index of the last move selected in the battle menu
|
|
207
|
-
|
|
208
|
-
/// Whether the last move executed
|
|
209
|
-
|
|
210
|
-
/// The move slot index of the last move selected in the battle menu by Player 2
|
|
211
|
-
p2_index: if (showdown) u8 else u3 = 0,
|
|
212
|
-
/// Whether the last move executed by Player 2 was counterable.
|
|
213
|
-
p2_counterable: if (showdown) u8 else u1 = 0,
|
|
214
|
-
|
|
215
|
-
comptime {
|
|
216
|
-
assert(@sizeOf(MoveDetails) == if (showdown) 4 else 1);
|
|
217
|
-
}
|
|
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,
|
|
218
232
|
};
|
|
219
233
|
|
|
220
234
|
/// Bitfield representation of a Generation I & II Pokémon's major status condition.
|
|
@@ -238,39 +252,39 @@ pub const Status = enum(u8) {
|
|
|
238
252
|
|
|
239
253
|
pub const TOX = 0b10001000;
|
|
240
254
|
|
|
241
|
-
const
|
|
255
|
+
const SLEEP = 0b111;
|
|
242
256
|
|
|
243
257
|
/// Whether or not the status `num` is the same as `status`.
|
|
244
|
-
pub
|
|
258
|
+
pub fn is(num: u8, status: Status) bool {
|
|
245
259
|
if (status == .SLP) return Status.duration(num) > 0;
|
|
246
|
-
return ((num >> @intCast(
|
|
260
|
+
return ((num >> @intCast(@intFromEnum(status))) & 1) != 0;
|
|
247
261
|
}
|
|
248
262
|
|
|
249
263
|
/// Initializes a non-sleep `status`. Use `slp` or `slf` to initialize a sleep status.
|
|
250
|
-
pub
|
|
264
|
+
pub fn init(status: Status) u8 {
|
|
251
265
|
assert(status != .SLP and status != .EXT);
|
|
252
|
-
return @as(u8, 1) << @intCast(
|
|
266
|
+
return @as(u8, 1) << @intCast(@intFromEnum(status));
|
|
253
267
|
}
|
|
254
268
|
|
|
255
269
|
/// Initializes a non self-inflicted sleep status with duration `dur`.
|
|
256
|
-
pub
|
|
270
|
+
pub fn slp(dur: u3) u8 {
|
|
257
271
|
assert(dur > 0);
|
|
258
272
|
return @as(u8, dur);
|
|
259
273
|
}
|
|
260
274
|
|
|
261
275
|
/// Initializes a self-inflicted sleep status with duration `dur`.
|
|
262
|
-
pub
|
|
276
|
+
pub fn slf(dur: u3) u8 {
|
|
263
277
|
assert(dur > 0);
|
|
264
278
|
return 0x80 | slp(dur);
|
|
265
279
|
}
|
|
266
280
|
|
|
267
281
|
/// Returns the duration of a sleep status.
|
|
268
|
-
pub
|
|
269
|
-
return @intCast(
|
|
282
|
+
pub fn duration(num: u8) u3 {
|
|
283
|
+
return @intCast(num & SLEEP);
|
|
270
284
|
}
|
|
271
285
|
|
|
272
286
|
/// Returns whether `num` reflects any status.
|
|
273
|
-
pub
|
|
287
|
+
pub fn any(num: u8) bool {
|
|
274
288
|
return num > 0;
|
|
275
289
|
}
|
|
276
290
|
|
|
@@ -308,7 +322,7 @@ test Status {
|
|
|
308
322
|
}
|
|
309
323
|
|
|
310
324
|
/// Bitfield representation of volatile statuses and associated data in Generation I.
|
|
311
|
-
pub const Volatiles = packed struct {
|
|
325
|
+
pub const Volatiles = packed struct(u64) {
|
|
312
326
|
/// Whether the "Bide" volatile status is present.
|
|
313
327
|
Bide: bool = false,
|
|
314
328
|
/// Whether the "Thrashing" volatile status is present.
|
|
@@ -361,19 +375,15 @@ pub const Volatiles = packed struct {
|
|
|
361
375
|
/// The identity of whom the active Pokémon is transformed into.
|
|
362
376
|
transform: u4 = 0,
|
|
363
377
|
/// The remaining turns the move is disabled.
|
|
364
|
-
|
|
378
|
+
disable_duration: u4 = 0,
|
|
365
379
|
/// The move slot (1-4) that is disabled.
|
|
366
|
-
|
|
380
|
+
disable_move: u3 = 0,
|
|
367
381
|
/// The number of turns toxic damage has been accumulating.
|
|
368
382
|
toxic: u5 = 0,
|
|
369
|
-
|
|
370
|
-
comptime {
|
|
371
|
-
assert(@sizeOf(Volatiles) == 8);
|
|
372
|
-
}
|
|
373
383
|
};
|
|
374
384
|
|
|
375
385
|
test Volatiles {
|
|
376
|
-
var volatiles =
|
|
386
|
+
var volatiles: Volatiles = .{};
|
|
377
387
|
volatiles.Confusion = true;
|
|
378
388
|
volatiles.confusion = 2;
|
|
379
389
|
volatiles.Thrashing = true;
|
|
@@ -382,8 +392,8 @@ test Volatiles {
|
|
|
382
392
|
volatiles.Substitute = true;
|
|
383
393
|
volatiles.substitute = 42;
|
|
384
394
|
volatiles.toxic = 4;
|
|
385
|
-
volatiles.
|
|
386
|
-
volatiles.
|
|
395
|
+
volatiles.disable_move = 2;
|
|
396
|
+
volatiles.disable_duration = 4;
|
|
387
397
|
|
|
388
398
|
try expect(volatiles.Confusion);
|
|
389
399
|
try expect(volatiles.Thrashing);
|
|
@@ -394,14 +404,14 @@ test Volatiles {
|
|
|
394
404
|
|
|
395
405
|
try expectEqual(@as(u16, 235), volatiles.state);
|
|
396
406
|
try expectEqual(@as(u8, 42), volatiles.substitute);
|
|
397
|
-
try expectEqual(@as(u4, 2), volatiles.
|
|
398
|
-
try expectEqual(@as(u4, 4), volatiles.
|
|
399
|
-
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);
|
|
400
410
|
try expectEqual(@as(u5, 4), volatiles.toxic);
|
|
401
|
-
try expectEqual(@as(
|
|
411
|
+
try expectEqual(@as(u3, 3), volatiles.attacks);
|
|
402
412
|
}
|
|
403
413
|
|
|
404
|
-
///
|
|
414
|
+
/// Representation of a Pokémon's stats in Generation I.
|
|
405
415
|
pub fn Stats(comptime T: type) type {
|
|
406
416
|
return extern struct {
|
|
407
417
|
hp: T = 0,
|
|
@@ -413,10 +423,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
413
423
|
/// Computes the value of `stat` given a `base`, `dv`, stat `exp` and `level`.
|
|
414
424
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
415
425
|
assert(level > 0 and level <= 100);
|
|
416
|
-
const
|
|
426
|
+
const float: f32 = @floatFromInt(exp);
|
|
427
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
417
428
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
418
429
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
419
|
-
return @intCast(
|
|
430
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
420
431
|
}
|
|
421
432
|
};
|
|
422
433
|
}
|
|
@@ -434,7 +445,7 @@ test Stats {
|
|
|
434
445
|
}
|
|
435
446
|
|
|
436
447
|
/// Representation of a Pokémon's boosts in Generation I.
|
|
437
|
-
pub const Boosts = packed struct {
|
|
448
|
+
pub const Boosts = packed struct(u32) {
|
|
438
449
|
/// A Pokémon's Attack boosts.
|
|
439
450
|
atk: i4 = 0,
|
|
440
451
|
/// A Pokémon's Defense boosts.
|
|
@@ -449,14 +460,10 @@ pub const Boosts = packed struct {
|
|
|
449
460
|
evasion: i4 = 0,
|
|
450
461
|
|
|
451
462
|
_: u8 = 0,
|
|
452
|
-
|
|
453
|
-
comptime {
|
|
454
|
-
assert(@sizeOf(Boosts) == 4);
|
|
455
|
-
}
|
|
456
463
|
};
|
|
457
464
|
|
|
458
465
|
test Boosts {
|
|
459
|
-
const boosts =
|
|
466
|
+
const boosts: Boosts = .{ .spc = -6 };
|
|
460
467
|
try expectEqual(0, boosts.atk);
|
|
461
468
|
try expectEqual(-6, boosts.spc);
|
|
462
469
|
}
|
|
@@ -465,10 +472,10 @@ test Boosts {
|
|
|
465
472
|
pub const Move = moves.Move;
|
|
466
473
|
|
|
467
474
|
test Move {
|
|
468
|
-
try expectEqual(2, @
|
|
475
|
+
try expectEqual(2, @intFromEnum(Move.KarateChop));
|
|
469
476
|
const move = Move.get(.Fissure);
|
|
470
477
|
try expectEqual(Move.Effect.OHKO, move.effect);
|
|
471
|
-
try expectEqual(
|
|
478
|
+
try expectEqual(rng.Gen12.percent(30), move.accuracy);
|
|
472
479
|
try expectEqual(Type.Ground, move.type);
|
|
473
480
|
|
|
474
481
|
try expect(!Move.Effect.onBegin(.None));
|
|
@@ -526,7 +533,7 @@ test Move {
|
|
|
526
533
|
pub const Species = species.Species;
|
|
527
534
|
|
|
528
535
|
test Species {
|
|
529
|
-
try expectEqual(2, @
|
|
536
|
+
try expectEqual(2, @intFromEnum(Species.Ivysaur));
|
|
530
537
|
try expectEqual(@as(u8, 100), Species.get(.Mew).stats.def);
|
|
531
538
|
}
|
|
532
539
|
|
|
@@ -543,9 +550,11 @@ pub const Effectiveness = enum(u8) {
|
|
|
543
550
|
Super = 20,
|
|
544
551
|
|
|
545
552
|
/// Used to determine the effectiness of a neutral damage hit.
|
|
546
|
-
pub const neutral: u16 =
|
|
553
|
+
pub const neutral: u16 =
|
|
554
|
+
@intFromEnum(Effectiveness.Neutral) * @intFromEnum(Effectiveness.Neutral);
|
|
547
555
|
/// Used to detect whether there is a mismatch in type effectivness (relevant for precedence).
|
|
548
|
-
pub const mismatch: u16 =
|
|
556
|
+
pub const mismatch: u16 =
|
|
557
|
+
@intFromEnum(Effectiveness.Resisted) + @intFromEnum(Effectiveness.Super);
|
|
549
558
|
|
|
550
559
|
comptime {
|
|
551
560
|
assert(@sizeOf(Effectiveness) == 1);
|
|
@@ -553,8 +562,8 @@ pub const Effectiveness = enum(u8) {
|
|
|
553
562
|
};
|
|
554
563
|
|
|
555
564
|
test Types {
|
|
556
|
-
try expectEqual(14, @
|
|
557
|
-
try expectEqual(20, @
|
|
565
|
+
try expectEqual(14, @intFromEnum(Type.Dragon));
|
|
566
|
+
try expectEqual(20, @intFromEnum(Effectiveness.Super));
|
|
558
567
|
|
|
559
568
|
try expect(!Type.Ghost.special());
|
|
560
569
|
try expect(Type.Dragon.special());
|
|
@@ -604,16 +613,16 @@ pub const DVs = struct {
|
|
|
604
613
|
};
|
|
605
614
|
|
|
606
615
|
test DVs {
|
|
607
|
-
var dvs =
|
|
616
|
+
var dvs: DVs = .{ .spc = 15, .spe = 15 };
|
|
608
617
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
609
|
-
dvs =
|
|
618
|
+
dvs = .{
|
|
610
619
|
.atk = 5,
|
|
611
620
|
.def = 15,
|
|
612
621
|
.spe = 13,
|
|
613
622
|
.spc = 13,
|
|
614
623
|
};
|
|
615
624
|
try expectEqual(@as(u4, 15), dvs.hp());
|
|
616
|
-
dvs =
|
|
625
|
+
dvs = .{
|
|
617
626
|
.def = 3,
|
|
618
627
|
.spe = 10,
|
|
619
628
|
.spc = 11,
|