@pkmn/engine 0.1.0-dev.fb5bf955 → 0.1.0-dev.fb6653d6
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 +115 -76
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +44 -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 +97 -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 +16 -19
- package/build/pkg/gen1.js +54 -42
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +55 -103
- package/build/pkg/index.js +6 -63
- 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 +36 -69
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +142 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +373 -152
- package/package.json +68 -120
- package/src/bin/install-pkmn-engine +41 -26
- package/src/bin/pkmn-debug +29 -6
- package/src/lib/bindings/node.zig +121 -0
- package/src/lib/bindings/wasm.zig +58 -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 +449 -0
- package/src/lib/common/rng.zig +31 -24
- package/src/lib/common/util.zig +61 -0
- package/src/lib/gen1/calc.zig +953 -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 -89
- package/src/lib/gen1/helpers.zig +139 -50
- package/src/lib/gen1/mechanics.zig +1344 -907
- 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 +138 -36
- package/src/lib/gen2/data.zig +134 -99
- 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 +96 -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 -784
- package/src/lib/gen2/README.md +0 -309
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen2/data.zig
CHANGED
|
@@ -1,46 +1,54 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const data = @import("../common/data.zig");
|
|
5
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
6
|
-
const options = @import("../common/options.zig");
|
|
7
|
-
const rng = @import("../common/rng.zig");
|
|
8
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const calc = @import("calc.zig");
|
|
5
|
+
const chance = @import("chance.zig");
|
|
6
|
+
const data = @import("../common/data.zig");
|
|
7
|
+
const gen1 = @import("../gen1/data.zig");
|
|
9
8
|
const items = @import("data/items.zig");
|
|
9
|
+
const mechanics = @import("mechanics.zig");
|
|
10
10
|
const moves = @import("data/moves.zig");
|
|
11
|
+
const pkmn = @import("../pkmn.zig");
|
|
12
|
+
const protocol = @import("../common/protocol.zig");
|
|
13
|
+
const rng = @import("../common/rng.zig");
|
|
11
14
|
const species = @import("data/species.zig");
|
|
15
|
+
const std = @import("std");
|
|
12
16
|
const types = @import("data/types.zig");
|
|
13
|
-
|
|
14
|
-
const gen1 = @import("../gen1/data.zig");
|
|
15
|
-
|
|
16
|
-
const mechanics = @import("mechanics.zig");
|
|
17
|
+
const util = @import("../common/util.zig");
|
|
17
18
|
|
|
18
19
|
const assert = std.debug.assert;
|
|
19
20
|
const expectEqual = std.testing.expectEqual;
|
|
20
21
|
const expect = std.testing.expect;
|
|
21
22
|
|
|
22
|
-
pub const
|
|
23
|
+
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
23
24
|
pub const MAX_LOGS: usize = 180; // TODO
|
|
24
25
|
|
|
25
|
-
pub const
|
|
26
|
-
|
|
26
|
+
pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
27
|
+
MAX_CHOICES
|
|
27
28
|
else
|
|
28
|
-
std.math.ceilPowerOfTwo(usize,
|
|
29
|
+
std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
|
|
29
30
|
pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
30
31
|
MAX_LOGS
|
|
31
32
|
else
|
|
32
33
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
33
34
|
|
|
35
|
+
/// Null object pattern implementation of pkmn.battle.Options for Generation II.
|
|
36
|
+
pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
|
|
37
|
+
|
|
34
38
|
const Choice = data.Choice;
|
|
35
39
|
const ID = data.ID;
|
|
36
40
|
const Player = data.Player;
|
|
37
41
|
const Result = data.Result;
|
|
38
42
|
|
|
39
|
-
const
|
|
43
|
+
const PointerType = util.PointerType;
|
|
44
|
+
const isPointerTo = util.isPointerTo;
|
|
45
|
+
|
|
46
|
+
const showdown = pkmn.options.showdown;
|
|
40
47
|
|
|
41
48
|
pub const PRNG = rng.PRNG(2);
|
|
42
49
|
|
|
43
50
|
pub fn Battle(comptime RNG: anytype) type {
|
|
51
|
+
const padding = if (!showdown) 3 else 5;
|
|
44
52
|
return extern struct {
|
|
45
53
|
const Self = @This();
|
|
46
54
|
|
|
@@ -49,20 +57,24 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
49
57
|
turn: u16 = 0,
|
|
50
58
|
field: Field = .{},
|
|
51
59
|
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
_: [padding]u8 = .{0} ** padding,
|
|
61
|
+
|
|
62
|
+
pub fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
63
|
+
assert(isPointerTo(self, Self));
|
|
64
|
+
return &self.sides[@intFromEnum(player)];
|
|
54
65
|
}
|
|
55
66
|
|
|
56
|
-
pub
|
|
57
|
-
|
|
67
|
+
pub fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
68
|
+
assert(isPointerTo(self, Self));
|
|
69
|
+
return &self.sides[@intFromEnum(player.foe())];
|
|
58
70
|
}
|
|
59
71
|
|
|
60
|
-
pub
|
|
61
|
-
return player.ident(@intCast(
|
|
72
|
+
pub fn active(self: *const Self, player: Player) ID {
|
|
73
|
+
return player.ident(@intCast(self.side(player).pokemon[0].position));
|
|
62
74
|
}
|
|
63
75
|
|
|
64
|
-
pub fn update(self: *Self, c1: Choice, c2: Choice,
|
|
65
|
-
return mechanics.update(self, c1, c2,
|
|
76
|
+
pub fn update(self: *Self, c1: Choice, c2: Choice, options: anytype) !Result {
|
|
77
|
+
return mechanics.update(self, c1, c2, options);
|
|
66
78
|
}
|
|
67
79
|
|
|
68
80
|
pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
@@ -71,35 +83,32 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
71
83
|
};
|
|
72
84
|
}
|
|
73
85
|
|
|
74
|
-
// TODO
|
|
75
86
|
test Battle {
|
|
76
|
-
try expectEqual(
|
|
87
|
+
try expectEqual(512, @sizeOf(Battle(PRNG)));
|
|
77
88
|
}
|
|
78
89
|
|
|
79
|
-
const Field = packed struct {
|
|
90
|
+
pub const Field = packed struct(u8) {
|
|
80
91
|
weather: Weather = .None,
|
|
81
92
|
weather_duration: u4 = 0,
|
|
82
|
-
|
|
83
|
-
comptime {
|
|
84
|
-
assert(@sizeOf(Field) == 1);
|
|
85
|
-
}
|
|
86
93
|
};
|
|
87
94
|
|
|
88
|
-
const Weather = enum(u4) {
|
|
95
|
+
pub const Weather = enum(u4) {
|
|
89
96
|
None,
|
|
90
97
|
Rain,
|
|
91
98
|
Sun,
|
|
92
99
|
Sandstorm,
|
|
93
100
|
};
|
|
94
101
|
|
|
95
|
-
const Side = extern struct {
|
|
102
|
+
pub const Side = extern struct {
|
|
96
103
|
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
97
|
-
active: ActivePokemon = .{},
|
|
98
|
-
conditions: Conditions,
|
|
104
|
+
active: ActivePokemon align(8) = .{},
|
|
105
|
+
conditions: Conditions = .{},
|
|
106
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
99
107
|
last_used_move: Move = .None,
|
|
100
|
-
_: u8 = 0,
|
|
101
108
|
|
|
102
|
-
|
|
109
|
+
_: [7]u8 = .{0} ** 7,
|
|
110
|
+
|
|
111
|
+
const Conditions = packed struct(u32) {
|
|
103
112
|
Spikes: bool = false,
|
|
104
113
|
Safeguard: bool = false,
|
|
105
114
|
LightScreen: bool = false,
|
|
@@ -109,50 +118,69 @@ const Side = extern struct {
|
|
|
109
118
|
light_screen: u4 = 0,
|
|
110
119
|
reflect: u4 = 0,
|
|
111
120
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
future_sight: FutureSight = .{},
|
|
122
|
+
|
|
123
|
+
const FutureSight = packed struct(u16) {
|
|
124
|
+
damage: u12 = 0,
|
|
125
|
+
count: u4 = 0,
|
|
126
|
+
};
|
|
115
127
|
};
|
|
116
128
|
|
|
117
129
|
comptime {
|
|
118
|
-
assert(@sizeOf(Side) ==
|
|
130
|
+
assert(@sizeOf(Side) == 248);
|
|
119
131
|
}
|
|
120
132
|
|
|
121
|
-
pub
|
|
133
|
+
pub fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
134
|
+
assert(isPointerTo(self, Side));
|
|
122
135
|
assert(slot > 0 and slot <= 6);
|
|
123
136
|
const id = self.pokemon[slot - 1].position;
|
|
124
137
|
assert(id > 0 and id <= 6);
|
|
125
138
|
return &self.pokemon[id - 1];
|
|
126
139
|
}
|
|
127
140
|
|
|
128
|
-
pub
|
|
141
|
+
pub fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
142
|
+
assert(isPointerTo(self, Side));
|
|
129
143
|
return self.get(1);
|
|
130
144
|
}
|
|
145
|
+
|
|
146
|
+
pub fn lastMove(self: anytype, encore: bool) Move {
|
|
147
|
+
assert(self.last_used_move != .None or !self.active.volatiles.dirty);
|
|
148
|
+
if (encore) return self.last_used_move;
|
|
149
|
+
return if (self.active.volatiles.dirty) .None else self.last_used_move;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// TODO
|
|
153
|
+
// pub fn useMove(self: anytype) void {
|
|
154
|
+
// assert(side.last_selected_move != .None);
|
|
155
|
+
// self.last_used_move = side.last_selected_move;
|
|
156
|
+
// self.active.volatiles.dirty = false;
|
|
157
|
+
// }
|
|
131
158
|
};
|
|
132
159
|
|
|
133
160
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
134
|
-
const ActivePokemon = extern struct {
|
|
135
|
-
volatiles:
|
|
161
|
+
pub const ActivePokemon = extern struct {
|
|
162
|
+
volatiles: Volatiles align(4) = .{},
|
|
136
163
|
stats: Stats(u16) = .{},
|
|
137
164
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
138
165
|
boosts: Boosts = .{},
|
|
166
|
+
types: Types = .{},
|
|
139
167
|
species: Species = .None,
|
|
140
|
-
|
|
141
|
-
last_move: Move = .None,
|
|
168
|
+
|
|
142
169
|
_: u8 = 0,
|
|
143
170
|
|
|
144
171
|
comptime {
|
|
145
172
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
146
173
|
}
|
|
147
174
|
|
|
148
|
-
pub
|
|
175
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
176
|
+
assert(isPointerTo(self, Pokemon));
|
|
149
177
|
assert(mslot > 0 and mslot <= 4);
|
|
150
178
|
assert(self.moves[mslot - 1].id != .None);
|
|
151
179
|
return &self.moves[mslot - 1];
|
|
152
180
|
}
|
|
153
181
|
};
|
|
154
182
|
|
|
155
|
-
const Pokemon = extern struct {
|
|
183
|
+
pub const Pokemon = extern struct {
|
|
156
184
|
stats: Stats(u16) = .{},
|
|
157
185
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
158
186
|
types: Types = .{},
|
|
@@ -169,7 +197,8 @@ const Pokemon = extern struct {
|
|
|
169
197
|
assert(@sizeOf(Pokemon) == 32);
|
|
170
198
|
}
|
|
171
199
|
|
|
172
|
-
pub
|
|
200
|
+
pub fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
201
|
+
assert(isPointerTo(self, Pokemon));
|
|
173
202
|
assert(mslot > 0 and mslot <= 4);
|
|
174
203
|
assert(self.moves[mslot - 1].id != .None);
|
|
175
204
|
return &self.moves[mslot - 1];
|
|
@@ -182,7 +211,7 @@ pub const Gender = enum(u2) {
|
|
|
182
211
|
Unknown,
|
|
183
212
|
};
|
|
184
213
|
|
|
185
|
-
const DVs = packed struct {
|
|
214
|
+
pub const DVs = packed struct(u16) {
|
|
186
215
|
gender: Gender = .Male,
|
|
187
216
|
pow: u6 = 40,
|
|
188
217
|
type: Type = .Dark,
|
|
@@ -190,10 +219,10 @@ const DVs = packed struct {
|
|
|
190
219
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
191
220
|
assert(p >= 1 and p <= 40);
|
|
192
221
|
assert(t != .Normal and t != .@"???");
|
|
193
|
-
return
|
|
222
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
194
223
|
}
|
|
195
224
|
|
|
196
|
-
pub
|
|
225
|
+
pub fn power(dvs: DVs) u8 {
|
|
197
226
|
return @as(u8, dvs.pow) + 30;
|
|
198
227
|
}
|
|
199
228
|
|
|
@@ -205,15 +234,10 @@ const DVs = packed struct {
|
|
|
205
234
|
0xFF => .Unknown,
|
|
206
235
|
else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
|
|
207
236
|
};
|
|
208
|
-
const p = @intCast(
|
|
237
|
+
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
209
238
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return DVs{ .gender = g, .pow = p, .type = @intToEnum(Type, t) };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
comptime {
|
|
216
|
-
assert(@sizeOf(DVs) == 2);
|
|
239
|
+
const t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
|
|
240
|
+
return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
|
|
217
241
|
}
|
|
218
242
|
};
|
|
219
243
|
|
|
@@ -238,10 +262,12 @@ test DVs {
|
|
|
238
262
|
try expectEqual(Gender.Female, dvs.gender);
|
|
239
263
|
}
|
|
240
264
|
|
|
241
|
-
|
|
265
|
+
/// Representation of a Generation II Pokémon's move slot in a battle.
|
|
266
|
+
pub const MoveSlot = extern struct {
|
|
267
|
+
/// The identifier for the move.
|
|
242
268
|
id: Move = .None,
|
|
243
|
-
|
|
244
|
-
pp:
|
|
269
|
+
/// The remaining PP of the move.
|
|
270
|
+
pp: u8 = 0,
|
|
245
271
|
|
|
246
272
|
comptime {
|
|
247
273
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -250,14 +276,19 @@ const MoveSlot = packed struct {
|
|
|
250
276
|
|
|
251
277
|
pub const Status = gen1.Status;
|
|
252
278
|
|
|
253
|
-
|
|
279
|
+
/// TODO
|
|
280
|
+
pub const TriAttack = enum(u2) { PAR, FRZ, BRN };
|
|
281
|
+
|
|
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,52 +299,55 @@ 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
|
-
|
|
314
|
+
Trapped: bool = false,
|
|
283
315
|
|
|
284
|
-
trapped: bool = false,
|
|
285
316
|
switched: bool = false,
|
|
317
|
+
// TODO: replace with simply looking at Choice on Battle
|
|
286
318
|
switching: bool = false,
|
|
319
|
+
// TODO: simply store both last moves on side
|
|
320
|
+
dirty: bool = false,
|
|
287
321
|
|
|
288
|
-
|
|
322
|
+
frozen: bool = false,
|
|
323
|
+
minimized: bool = false,
|
|
324
|
+
_: u10 = 0,
|
|
325
|
+
// TODO: is this needed for unmodified stats?
|
|
326
|
+
transform: u4 = 0,
|
|
289
327
|
|
|
290
|
-
bind: u4 = 0,
|
|
291
|
-
future_sight: FutureSight = .{},
|
|
292
328
|
bide: u16 = 0,
|
|
293
|
-
|
|
329
|
+
bind: Bind = .{},
|
|
330
|
+
disable: Disable = .{},
|
|
294
331
|
rage: u8 = 0,
|
|
295
332
|
substitute: u8 = 0,
|
|
296
333
|
toxic: u8 = 0,
|
|
297
334
|
confusion: u4 = 0,
|
|
298
335
|
encore: u4 = 0,
|
|
336
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
299
337
|
fury_cutter: u4 = 0,
|
|
300
338
|
perish_song: u4 = 0,
|
|
301
339
|
protect: u4 = 0,
|
|
302
|
-
|
|
340
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
303
341
|
|
|
304
|
-
const
|
|
305
|
-
move: u4 = 0,
|
|
342
|
+
const Bind = packed struct(u8) {
|
|
306
343
|
duration: u4 = 0,
|
|
344
|
+
reason: u4 = 0,
|
|
307
345
|
};
|
|
308
346
|
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
347
|
+
const Disable = packed struct(u8) {
|
|
348
|
+
move: u4 = 0,
|
|
349
|
+
duration: u4 = 0,
|
|
312
350
|
};
|
|
313
|
-
|
|
314
|
-
comptime {
|
|
315
|
-
assert(@sizeOf(Volatile) == 16);
|
|
316
|
-
}
|
|
317
351
|
};
|
|
318
352
|
|
|
319
353
|
pub fn Stats(comptime T: type) type {
|
|
@@ -327,10 +361,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
327
361
|
|
|
328
362
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
363
|
assert(level > 0 and level <= 100);
|
|
330
|
-
const
|
|
364
|
+
const float: f32 = @floatFromInt(exp);
|
|
365
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
331
366
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
367
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
|
-
return @intCast(
|
|
368
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
334
369
|
}
|
|
335
370
|
};
|
|
336
371
|
}
|
|
@@ -347,7 +382,7 @@ test Stats {
|
|
|
347
382
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
348
383
|
}
|
|
349
384
|
|
|
350
|
-
pub const Boosts = packed struct {
|
|
385
|
+
pub const Boosts = packed struct(u32) {
|
|
351
386
|
atk: i4 = 0,
|
|
352
387
|
def: i4 = 0,
|
|
353
388
|
spe: i4 = 0,
|
|
@@ -363,7 +398,7 @@ pub const Boosts = packed struct {
|
|
|
363
398
|
};
|
|
364
399
|
|
|
365
400
|
test Boosts {
|
|
366
|
-
const boosts =
|
|
401
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
367
402
|
try expectEqual(0, boosts.atk);
|
|
368
403
|
try expectEqual(-6, boosts.spd);
|
|
369
404
|
}
|
|
@@ -376,31 +411,31 @@ test Item {
|
|
|
376
411
|
try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
|
|
377
412
|
try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
|
|
378
413
|
|
|
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));
|
|
414
|
+
try expect(!Item.isMail(.UpGrade));
|
|
415
|
+
try expect(Item.isMail(.FlowerMail));
|
|
416
|
+
try expect(Item.isMail(.MirageMail));
|
|
417
|
+
try expect(!Item.isMail(.TownMap));
|
|
388
418
|
}
|
|
389
419
|
|
|
390
420
|
pub const Move = moves.Move;
|
|
391
421
|
|
|
392
422
|
test Move {
|
|
393
|
-
try expectEqual(251, @
|
|
423
|
+
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
394
424
|
const move = Move.get(.DynamicPunch);
|
|
395
425
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
|
-
try expectEqual(
|
|
397
|
-
try expectEqual(
|
|
426
|
+
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
427
|
+
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
428
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
429
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
430
|
+
try expect(!move.effect.isHighCritical());
|
|
431
|
+
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
432
|
+
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
398
433
|
}
|
|
399
434
|
|
|
400
435
|
pub const Species = species.Species;
|
|
401
436
|
|
|
402
437
|
test Species {
|
|
403
|
-
try expectEqual(152, @
|
|
438
|
+
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
404
439
|
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
405
440
|
}
|
|
406
441
|
|
|
@@ -409,7 +444,7 @@ pub const Types = types.Types;
|
|
|
409
444
|
pub const Effectiveness = gen1.Effectiveness;
|
|
410
445
|
|
|
411
446
|
test Types {
|
|
412
|
-
try expectEqual(13, @
|
|
447
|
+
try expectEqual(13, @intFromEnum(Type.Electric));
|
|
413
448
|
|
|
414
449
|
try expect(!Type.Steel.special());
|
|
415
450
|
try expect(Type.Dark.special());
|