@pkmn/engine 0.1.0-dev.b9fb6eab → 0.1.0-dev.c42deb42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -59
- package/build/pkg/addon.d.ts +7 -0
- package/build/pkg/addon.js +112 -0
- package/build/pkg/addon.js.map +1 -0
- package/build/pkg/data/ids.json +12 -12
- package/build/pkg/data/layout.json +25 -3
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/data.d.ts +3 -4
- package/build/pkg/data.js +3 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +5 -4
- package/build/pkg/gen1.js +47 -31
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +15 -12
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +4 -2
- package/build/pkg/protocol.js +60 -39
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +400 -0
- package/build/test/display.js.map +1 -0
- package/build/tools/debug.js +133 -0
- package/build/tools/debug.js.map +1 -0
- package/build.zig +98 -79
- package/package.json +65 -36
- package/src/bin/install-pkmn-engine +11 -6
- package/src/bin/pkmn-debug +62 -0
- package/src/lib/binding/node.zig +47 -142
- package/src/lib/binding/wasm.zig +6 -4
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +15 -27
- package/src/lib/common/debug.zig +21 -11
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +67 -0
- package/src/lib/common/options.zig +12 -3
- package/src/lib/common/protocol.zig +765 -245
- package/src/lib/common/rational.zig +353 -0
- package/src/lib/common/rng.zig +15 -15
- package/src/lib/common/util.zig +12 -0
- package/src/lib/gen1/README.md +268 -154
- package/src/lib/gen1/calc.zig +426 -0
- package/src/lib/gen1/chance.zig +1060 -0
- package/src/lib/gen1/data/moves.zig +192 -180
- package/src/lib/gen1/data/species.zig +2 -2
- package/src/lib/gen1/data/types.zig +5 -5
- package/src/lib/gen1/data.zig +78 -64
- package/src/lib/gen1/helpers.zig +577 -32
- package/src/lib/gen1/mechanics.zig +1023 -694
- package/src/lib/gen2/README.md +106 -85
- package/src/lib/gen2/data/items.zig +12 -18
- package/src/lib/gen2/data/moves.zig +660 -566
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +130 -28
- package/src/lib/gen2/data.zig +135 -87
- package/src/lib/gen2/helpers.zig +16 -22
- package/src/lib/gen2/mechanics.zig +2594 -11
- package/src/lib/pkmn.zig +68 -9
- package/src/test/display/pkmn.html.tmpl +368 -0
package/src/lib/gen2/data.zig
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const builtin = @import("builtin");
|
|
3
3
|
|
|
4
|
+
const pkmn = @import("../pkmn.zig");
|
|
5
|
+
|
|
4
6
|
const data = @import("../common/data.zig");
|
|
5
7
|
const DEBUG = @import("../common/debug.zig").print;
|
|
6
|
-
const
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
7
9
|
const rng = @import("../common/rng.zig");
|
|
10
|
+
const util = @import("../common/util.zig");
|
|
8
11
|
|
|
9
12
|
const items = @import("data/items.zig");
|
|
10
13
|
const moves = @import("data/moves.zig");
|
|
@@ -13,34 +16,42 @@ const types = @import("data/types.zig");
|
|
|
13
16
|
|
|
14
17
|
const gen1 = @import("../gen1/data.zig");
|
|
15
18
|
|
|
19
|
+
const calc = @import("calc.zig");
|
|
20
|
+
const chance = @import("chance.zig");
|
|
16
21
|
const mechanics = @import("mechanics.zig");
|
|
17
22
|
|
|
18
23
|
const assert = std.debug.assert;
|
|
19
24
|
const expectEqual = std.testing.expectEqual;
|
|
20
25
|
const expect = std.testing.expect;
|
|
21
26
|
|
|
22
|
-
pub const
|
|
27
|
+
pub const MAX_CHOICES: usize = 9; // move 1..4, switch 2..6
|
|
23
28
|
pub const MAX_LOGS: usize = 180; // TODO
|
|
24
29
|
|
|
25
|
-
pub const
|
|
26
|
-
|
|
30
|
+
pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
31
|
+
MAX_CHOICES
|
|
27
32
|
else
|
|
28
|
-
std.math.ceilPowerOfTwo(usize,
|
|
33
|
+
std.math.ceilPowerOfTwo(usize, MAX_CHOICES) catch unreachable;
|
|
29
34
|
pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
30
35
|
MAX_LOGS
|
|
31
36
|
else
|
|
32
37
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
33
38
|
|
|
39
|
+
/// Null object pattern implementation of pkmn.battle.Options for Generation II.
|
|
40
|
+
pub const NULL = pkmn.battle.options(protocol.NULL, chance.NULL, calc.NULL);
|
|
41
|
+
|
|
34
42
|
const Choice = data.Choice;
|
|
35
43
|
const ID = data.ID;
|
|
36
44
|
const Player = data.Player;
|
|
37
45
|
const Result = data.Result;
|
|
38
46
|
|
|
39
|
-
const
|
|
47
|
+
const PointerType = util.PointerType;
|
|
48
|
+
|
|
49
|
+
const showdown = pkmn.options.showdown;
|
|
40
50
|
|
|
41
51
|
pub const PRNG = rng.PRNG(2);
|
|
42
52
|
|
|
43
53
|
pub fn Battle(comptime RNG: anytype) type {
|
|
54
|
+
const padding = if (!showdown) 3 else 5;
|
|
44
55
|
return extern struct {
|
|
45
56
|
const Self = @This();
|
|
46
57
|
|
|
@@ -49,20 +60,24 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
49
60
|
turn: u16 = 0,
|
|
50
61
|
field: Field = .{},
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
|
|
63
|
+
_: [padding]u8 = .{0} ** padding,
|
|
64
|
+
|
|
65
|
+
pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
66
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
67
|
+
return &self.sides[@intFromEnum(player)];
|
|
54
68
|
}
|
|
55
69
|
|
|
56
|
-
pub inline fn foe(self:
|
|
57
|
-
|
|
70
|
+
pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
71
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
72
|
+
return &self.sides[@intFromEnum(player.foe())];
|
|
58
73
|
}
|
|
59
74
|
|
|
60
|
-
pub inline fn active(self: *Self, player: Player) ID {
|
|
61
|
-
return player.ident(@intCast(
|
|
75
|
+
pub inline fn active(self: *const Self, player: Player) ID {
|
|
76
|
+
return player.ident(@intCast(self.side(player).pokemon[0].position));
|
|
62
77
|
}
|
|
63
78
|
|
|
64
|
-
pub fn update(self: *Self, c1: Choice, c2: Choice,
|
|
65
|
-
return mechanics.update(self, c1, c2,
|
|
79
|
+
pub fn update(self: *Self, c1: Choice, c2: Choice, options: anytype) !Result {
|
|
80
|
+
return mechanics.update(self, c1, c2, options);
|
|
66
81
|
}
|
|
67
82
|
|
|
68
83
|
pub fn choices(self: *Self, player: Player, request: Choice.Type, out: []Choice) u8 {
|
|
@@ -71,35 +86,32 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
71
86
|
};
|
|
72
87
|
}
|
|
73
88
|
|
|
74
|
-
// TODO
|
|
75
89
|
test Battle {
|
|
76
|
-
try expectEqual(
|
|
90
|
+
try expectEqual(512, @sizeOf(Battle(PRNG)));
|
|
77
91
|
}
|
|
78
92
|
|
|
79
|
-
const Field = packed struct {
|
|
93
|
+
pub const Field = packed struct(u8) {
|
|
80
94
|
weather: Weather = .None,
|
|
81
95
|
weather_duration: u4 = 0,
|
|
82
|
-
|
|
83
|
-
comptime {
|
|
84
|
-
assert(@sizeOf(Field) == 1);
|
|
85
|
-
}
|
|
86
96
|
};
|
|
87
97
|
|
|
88
|
-
const Weather = enum(u4) {
|
|
98
|
+
pub const Weather = enum(u4) {
|
|
89
99
|
None,
|
|
90
100
|
Rain,
|
|
91
101
|
Sun,
|
|
92
102
|
Sandstorm,
|
|
93
103
|
};
|
|
94
104
|
|
|
95
|
-
const Side = extern struct {
|
|
105
|
+
pub const Side = extern struct {
|
|
96
106
|
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
97
|
-
active: ActivePokemon = .{},
|
|
98
|
-
conditions: Conditions,
|
|
107
|
+
active: ActivePokemon align(8) = .{},
|
|
108
|
+
conditions: Conditions = .{},
|
|
109
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
99
110
|
last_used_move: Move = .None,
|
|
100
|
-
_: u8 = 0,
|
|
101
111
|
|
|
102
|
-
|
|
112
|
+
_: [7]u8 = .{0} ** 7,
|
|
113
|
+
|
|
114
|
+
const Conditions = packed struct(u32) {
|
|
103
115
|
Spikes: bool = false,
|
|
104
116
|
Safeguard: bool = false,
|
|
105
117
|
LightScreen: bool = false,
|
|
@@ -109,50 +121,69 @@ const Side = extern struct {
|
|
|
109
121
|
light_screen: u4 = 0,
|
|
110
122
|
reflect: u4 = 0,
|
|
111
123
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
124
|
+
future_sight: FutureSight = .{},
|
|
125
|
+
|
|
126
|
+
const FutureSight = packed struct(u16) {
|
|
127
|
+
damage: u12 = 0,
|
|
128
|
+
count: u4 = 0,
|
|
129
|
+
};
|
|
115
130
|
};
|
|
116
131
|
|
|
117
132
|
comptime {
|
|
118
|
-
assert(@sizeOf(Side) ==
|
|
133
|
+
assert(@sizeOf(Side) == 248);
|
|
119
134
|
}
|
|
120
135
|
|
|
121
|
-
pub inline fn get(self:
|
|
136
|
+
pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
137
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
122
138
|
assert(slot > 0 and slot <= 6);
|
|
123
139
|
const id = self.pokemon[slot - 1].position;
|
|
124
140
|
assert(id > 0 and id <= 6);
|
|
125
141
|
return &self.pokemon[id - 1];
|
|
126
142
|
}
|
|
127
143
|
|
|
128
|
-
pub inline fn stored(self:
|
|
144
|
+
pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
145
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
129
146
|
return self.get(1);
|
|
130
147
|
}
|
|
148
|
+
|
|
149
|
+
pub inline fn lastMove(self: anytype, encore: bool) Move {
|
|
150
|
+
assert(self.last_used_move != .None or !self.active.volatiles.dirty);
|
|
151
|
+
if (encore) return self.last_used_move;
|
|
152
|
+
return if (self.active.volatiles.dirty) .None else self.last_used_move;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// TODO
|
|
156
|
+
// pub inline fn useMove(self: anytype) void {
|
|
157
|
+
// assert(side.last_selected_move != .None);
|
|
158
|
+
// self.last_used_move = side.last_selected_move;
|
|
159
|
+
// self.active.volatiles.dirty = false;
|
|
160
|
+
// }
|
|
131
161
|
};
|
|
132
162
|
|
|
133
163
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
134
|
-
const ActivePokemon = extern struct {
|
|
135
|
-
volatiles:
|
|
164
|
+
pub const ActivePokemon = extern struct {
|
|
165
|
+
volatiles: Volatiles align(4) = .{},
|
|
136
166
|
stats: Stats(u16) = .{},
|
|
137
167
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
138
168
|
boosts: Boosts = .{},
|
|
169
|
+
types: Types = .{},
|
|
139
170
|
species: Species = .None,
|
|
140
|
-
|
|
141
|
-
last_move: Move = .None,
|
|
171
|
+
|
|
142
172
|
_: u8 = 0,
|
|
143
173
|
|
|
144
174
|
comptime {
|
|
145
175
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
146
176
|
}
|
|
147
177
|
|
|
148
|
-
pub inline fn move(self:
|
|
178
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
179
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
149
180
|
assert(mslot > 0 and mslot <= 4);
|
|
150
181
|
assert(self.moves[mslot - 1].id != .None);
|
|
151
182
|
return &self.moves[mslot - 1];
|
|
152
183
|
}
|
|
153
184
|
};
|
|
154
185
|
|
|
155
|
-
const Pokemon = extern struct {
|
|
186
|
+
pub const Pokemon = extern struct {
|
|
156
187
|
stats: Stats(u16) = .{},
|
|
157
188
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
158
189
|
types: Types = .{},
|
|
@@ -169,7 +200,8 @@ const Pokemon = extern struct {
|
|
|
169
200
|
assert(@sizeOf(Pokemon) == 32);
|
|
170
201
|
}
|
|
171
202
|
|
|
172
|
-
pub inline fn move(self:
|
|
203
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
204
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
173
205
|
assert(mslot > 0 and mslot <= 4);
|
|
174
206
|
assert(self.moves[mslot - 1].id != .None);
|
|
175
207
|
return &self.moves[mslot - 1];
|
|
@@ -182,7 +214,7 @@ pub const Gender = enum(u2) {
|
|
|
182
214
|
Unknown,
|
|
183
215
|
};
|
|
184
216
|
|
|
185
|
-
const DVs = packed struct {
|
|
217
|
+
pub const DVs = packed struct(u16) {
|
|
186
218
|
gender: Gender = .Male,
|
|
187
219
|
pow: u6 = 40,
|
|
188
220
|
type: Type = .Dark,
|
|
@@ -190,7 +222,7 @@ const DVs = packed struct {
|
|
|
190
222
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
191
223
|
assert(p >= 1 and p <= 40);
|
|
192
224
|
assert(t != .Normal and t != .@"???");
|
|
193
|
-
return
|
|
225
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
194
226
|
}
|
|
195
227
|
|
|
196
228
|
pub inline fn power(dvs: DVs) u8 {
|
|
@@ -205,15 +237,10 @@ const DVs = packed struct {
|
|
|
205
237
|
0xFF => .Unknown,
|
|
206
238
|
else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
|
|
207
239
|
};
|
|
208
|
-
const p = @intCast(
|
|
240
|
+
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
209
241
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
210
|
-
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2)
|
|
211
|
-
|
|
212
|
-
return DVs{ .gender = g, .pow = p, .type = @intToEnum(Type, t) };
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
comptime {
|
|
216
|
-
assert(@sizeOf(DVs) == 2);
|
|
242
|
+
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2);
|
|
243
|
+
return .{ .gender = g, .pow = p, .type = Type.hiddenPower(t) };
|
|
217
244
|
}
|
|
218
245
|
};
|
|
219
246
|
|
|
@@ -238,10 +265,12 @@ test DVs {
|
|
|
238
265
|
try expectEqual(Gender.Female, dvs.gender);
|
|
239
266
|
}
|
|
240
267
|
|
|
241
|
-
|
|
268
|
+
/// Representation of a Generation II Pokémon's move slot in a battle.
|
|
269
|
+
pub const MoveSlot = extern struct {
|
|
270
|
+
/// The identifier for the move.
|
|
242
271
|
id: Move = .None,
|
|
243
|
-
|
|
244
|
-
pp:
|
|
272
|
+
/// The remaining PP of the move.
|
|
273
|
+
pp: u8 = 0,
|
|
245
274
|
|
|
246
275
|
comptime {
|
|
247
276
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -250,14 +279,34 @@ const MoveSlot = packed struct {
|
|
|
250
279
|
|
|
251
280
|
pub const Status = gen1.Status;
|
|
252
281
|
|
|
253
|
-
|
|
282
|
+
/// TODO
|
|
283
|
+
pub const TriAttack = enum(u2) {
|
|
284
|
+
PAR,
|
|
285
|
+
FRZ,
|
|
286
|
+
BRN,
|
|
287
|
+
|
|
288
|
+
/// TODO
|
|
289
|
+
pub inline fn status(self: TriAttack) u8 {
|
|
290
|
+
return Status.init(@enumFromInt(6 - @as(u8, @intFromEnum(self))));
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
test TriAttack {
|
|
295
|
+
try expectEqual(Status.init(.PAR), TriAttack.PAR.status());
|
|
296
|
+
try expectEqual(Status.init(.FRZ), TriAttack.FRZ.status());
|
|
297
|
+
try expectEqual(Status.init(.BRN), TriAttack.BRN.status());
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
pub const Volatiles = packed struct(u128) {
|
|
254
301
|
Bide: bool = false,
|
|
255
302
|
Thrashing: bool = false,
|
|
303
|
+
BeatUp: bool = false,
|
|
256
304
|
Flinch: bool = false,
|
|
257
305
|
Charging: bool = false,
|
|
258
306
|
Underground: bool = false,
|
|
259
307
|
Flying: bool = false,
|
|
260
308
|
Confusion: bool = false,
|
|
309
|
+
|
|
261
310
|
Mist: bool = false,
|
|
262
311
|
FocusEnergy: bool = false,
|
|
263
312
|
Substitute: bool = false,
|
|
@@ -268,52 +317,50 @@ const Volatile = packed struct {
|
|
|
268
317
|
Transform: bool = false,
|
|
269
318
|
|
|
270
319
|
Nightmare: bool = false,
|
|
271
|
-
|
|
320
|
+
Curse: bool = false,
|
|
272
321
|
Protect: bool = false,
|
|
273
322
|
Foresight: bool = false,
|
|
274
323
|
PerishSong: bool = false,
|
|
275
324
|
Endure: bool = false,
|
|
276
325
|
Rollout: bool = false,
|
|
277
326
|
Attract: bool = false,
|
|
327
|
+
|
|
278
328
|
DefenseCurl: bool = false,
|
|
279
329
|
Encore: bool = false,
|
|
280
330
|
LockOn: bool = false,
|
|
281
331
|
DestinyBond: bool = false,
|
|
282
|
-
BeatUp: bool = false,
|
|
283
332
|
|
|
284
333
|
trapped: bool = false,
|
|
285
334
|
switched: bool = false,
|
|
286
335
|
switching: bool = false,
|
|
336
|
+
dirty: bool = false,
|
|
287
337
|
|
|
288
|
-
|
|
338
|
+
frozen: bool = false,
|
|
339
|
+
_: u15 = 0,
|
|
289
340
|
|
|
290
|
-
bind: u4 = 0,
|
|
291
|
-
future_sight: FutureSight = .{},
|
|
292
341
|
bide: u16 = 0,
|
|
342
|
+
bind: Bind = .{},
|
|
293
343
|
disabled: Disabled = .{},
|
|
294
344
|
rage: u8 = 0,
|
|
295
345
|
substitute: u8 = 0,
|
|
296
346
|
toxic: u8 = 0,
|
|
297
347
|
confusion: u4 = 0,
|
|
298
348
|
encore: u4 = 0,
|
|
349
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
299
350
|
fury_cutter: u4 = 0,
|
|
300
351
|
perish_song: u4 = 0,
|
|
301
352
|
protect: u4 = 0,
|
|
302
|
-
|
|
353
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
303
354
|
|
|
304
|
-
const
|
|
305
|
-
move: u4 = 0,
|
|
355
|
+
const Bind = packed struct(u8) {
|
|
306
356
|
duration: u4 = 0,
|
|
357
|
+
reason: u4 = 0,
|
|
307
358
|
};
|
|
308
359
|
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
360
|
+
const Disabled = packed struct(u8) {
|
|
361
|
+
move: u4 = 0,
|
|
362
|
+
duration: u4 = 0,
|
|
312
363
|
};
|
|
313
|
-
|
|
314
|
-
comptime {
|
|
315
|
-
assert(@sizeOf(Volatile) == 16);
|
|
316
|
-
}
|
|
317
364
|
};
|
|
318
365
|
|
|
319
366
|
pub fn Stats(comptime T: type) type {
|
|
@@ -327,10 +374,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
327
374
|
|
|
328
375
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
376
|
assert(level > 0 and level <= 100);
|
|
330
|
-
const
|
|
377
|
+
const float: f32 = @floatFromInt(exp);
|
|
378
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
331
379
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
380
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
|
-
return @intCast(
|
|
381
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
334
382
|
}
|
|
335
383
|
};
|
|
336
384
|
}
|
|
@@ -347,7 +395,7 @@ test Stats {
|
|
|
347
395
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
348
396
|
}
|
|
349
397
|
|
|
350
|
-
pub const Boosts = packed struct {
|
|
398
|
+
pub const Boosts = packed struct(u32) {
|
|
351
399
|
atk: i4 = 0,
|
|
352
400
|
def: i4 = 0,
|
|
353
401
|
spe: i4 = 0,
|
|
@@ -363,7 +411,7 @@ pub const Boosts = packed struct {
|
|
|
363
411
|
};
|
|
364
412
|
|
|
365
413
|
test Boosts {
|
|
366
|
-
const boosts =
|
|
414
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
367
415
|
try expectEqual(0, boosts.atk);
|
|
368
416
|
try expectEqual(-6, boosts.spd);
|
|
369
417
|
}
|
|
@@ -376,31 +424,31 @@ test Item {
|
|
|
376
424
|
try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
|
|
377
425
|
try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
|
|
378
426
|
|
|
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));
|
|
427
|
+
try expect(!Item.isMail(.UpGrade));
|
|
428
|
+
try expect(Item.isMail(.FlowerMail));
|
|
429
|
+
try expect(Item.isMail(.MirageMail));
|
|
430
|
+
try expect(!Item.isMail(.TownMap));
|
|
388
431
|
}
|
|
389
432
|
|
|
390
433
|
pub const Move = moves.Move;
|
|
391
434
|
|
|
392
435
|
test Move {
|
|
393
|
-
try expectEqual(251, @
|
|
436
|
+
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
394
437
|
const move = Move.get(.DynamicPunch);
|
|
395
438
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
|
-
try expectEqual(
|
|
397
|
-
try expectEqual(
|
|
439
|
+
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
440
|
+
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
441
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
442
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
443
|
+
try expect(!move.effect.isHighCritical());
|
|
444
|
+
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
445
|
+
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
398
446
|
}
|
|
399
447
|
|
|
400
448
|
pub const Species = species.Species;
|
|
401
449
|
|
|
402
450
|
test Species {
|
|
403
|
-
try expectEqual(152, @
|
|
451
|
+
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
404
452
|
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
405
453
|
}
|
|
406
454
|
|
|
@@ -409,7 +457,7 @@ pub const Types = types.Types;
|
|
|
409
457
|
pub const Effectiveness = gen1.Effectiveness;
|
|
410
458
|
|
|
411
459
|
test Types {
|
|
412
|
-
try expectEqual(13, @
|
|
460
|
+
try expectEqual(13, @intFromEnum(Type.Electric));
|
|
413
461
|
|
|
414
462
|
try expect(!Type.Steel.special());
|
|
415
463
|
try expect(Type.Dark.special());
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -48,7 +48,7 @@ pub const Battle = struct {
|
|
|
48
48
|
comptime rolls: anytype,
|
|
49
49
|
p1: []const Pokemon,
|
|
50
50
|
p2: []const Pokemon,
|
|
51
|
-
) data.Battle(rng.FixedRNG(
|
|
51
|
+
) data.Battle(rng.FixedRNG(2, rolls.len)) {
|
|
52
52
|
return .{
|
|
53
53
|
.rng = .{ .rolls = rolls },
|
|
54
54
|
.sides = .{ Side.init(p1), Side.init(p2) },
|
|
@@ -72,11 +72,9 @@ pub fn prng(rand: *PSRNG) data.PRNG {
|
|
|
72
72
|
rand.newSeed()
|
|
73
73
|
else
|
|
74
74
|
.{
|
|
75
|
-
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
76
|
-
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
77
|
-
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
78
|
-
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
79
|
-
rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
75
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
76
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
77
|
+
rand.range(u8, 0, 256), rand.range(u8, 0, 256), rand.range(u8, 0, 256),
|
|
80
78
|
},
|
|
81
79
|
},
|
|
82
80
|
};
|
|
@@ -89,7 +87,7 @@ pub const Side = struct {
|
|
|
89
87
|
|
|
90
88
|
for (0..ps.len) |i| {
|
|
91
89
|
side.pokemon[i] = Pokemon.init(ps[i]);
|
|
92
|
-
|
|
90
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
93
91
|
}
|
|
94
92
|
return side;
|
|
95
93
|
}
|
|
@@ -100,7 +98,7 @@ pub const Side = struct {
|
|
|
100
98
|
|
|
101
99
|
for (0..n) |i| {
|
|
102
100
|
side.pokemon[i] = Pokemon.random(rand, opt);
|
|
103
|
-
side.pokemon[i].position = i + 1;
|
|
101
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
return side;
|
|
@@ -137,12 +135,9 @@ pub const Pokemon = struct {
|
|
|
137
135
|
}
|
|
138
136
|
assert(p.moves.len > 0 and p.moves.len <= 4);
|
|
139
137
|
for (p.moves, 0..) |m, j| {
|
|
140
|
-
pokemon.moves[j] =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
144
|
-
.pp = @intCast(u8, @min(Move.pp(m) / 5 * 8, 61)),
|
|
145
|
-
};
|
|
138
|
+
pokemon.moves[j].id = m;
|
|
139
|
+
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
140
|
+
pokemon.moves[j].pp = @intCast(@min(Move.pp(m) / 5 * 8, 61));
|
|
146
141
|
}
|
|
147
142
|
pokemon.item = p.item;
|
|
148
143
|
if (p.hp) |hp| {
|
|
@@ -153,16 +148,16 @@ pub const Pokemon = struct {
|
|
|
153
148
|
pokemon.status = p.status;
|
|
154
149
|
pokemon.types = species.types;
|
|
155
150
|
pokemon.level = p.level;
|
|
156
|
-
pokemon.dvs = DVs.from(p.dvs);
|
|
151
|
+
pokemon.dvs = DVs.from(p.species, p.dvs);
|
|
157
152
|
return pokemon;
|
|
158
153
|
}
|
|
159
154
|
|
|
160
155
|
pub fn random(rand: *PSRNG, opt: Options) data.Pokemon {
|
|
161
|
-
const s = @
|
|
156
|
+
const s: Species = @enumFromInt(rand.range(u8, 1, Species.size + 1));
|
|
162
157
|
const species = Species.get(s);
|
|
163
158
|
const lvl = if (rand.chance(u8, 1, 20)) rand.range(u8, 1, 99 + 1) else 100;
|
|
164
159
|
// Pokémon Showdown does not support most items without in-battle held item effects
|
|
165
|
-
const max_item = @
|
|
160
|
+
const max_item = @intFromEnum(if (showdown) Item.FlowerMail else Item.TM50);
|
|
166
161
|
const item = if (rand.chance(u8, 1, 10)) 0 else rand.range(u8, 1, max_item + 1);
|
|
167
162
|
|
|
168
163
|
var stats: Stats(u16) = .{};
|
|
@@ -184,24 +179,23 @@ pub const Pokemon = struct {
|
|
|
184
179
|
for (0..n) |i| {
|
|
185
180
|
var m: Move = .None;
|
|
186
181
|
sample: while (true) {
|
|
187
|
-
m = @
|
|
182
|
+
m = @enumFromInt(rand.range(u8, 1, 164 + 1));
|
|
188
183
|
for (0..i) |j| if (ms[j].id == m) continue :sample;
|
|
189
184
|
break;
|
|
190
185
|
}
|
|
191
|
-
const pp_ups =
|
|
186
|
+
const pp_ups: u8 =
|
|
192
187
|
if (!opt.cleric and rand.chance(u8, 1, 10)) rand.range(u2, 0, 2 + 1) else 3;
|
|
193
188
|
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
194
|
-
const max_pp = @intCast(
|
|
189
|
+
const max_pp: u8 = @intCast(Move.pp(m) + pp_ups * @min(Move.pp(m) / 5, 7));
|
|
195
190
|
ms[i] = .{
|
|
196
191
|
.id = m,
|
|
197
|
-
.pp_ups = pp_ups,
|
|
198
192
|
.pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
|
|
199
193
|
};
|
|
200
194
|
}
|
|
201
195
|
return .{
|
|
202
196
|
.species = s,
|
|
203
197
|
.types = species.types,
|
|
204
|
-
.dvs = DVs.from(dvs),
|
|
198
|
+
.dvs = DVs.from(s, dvs),
|
|
205
199
|
.level = lvl,
|
|
206
200
|
.stats = stats,
|
|
207
201
|
.hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),
|