@pkmn/engine 0.1.0-dev.e5b5a998 → 0.1.0-dev.f918c3bd
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 +53 -42
- package/build/pkg/addon.js +3 -2
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +24 -2
- package/build/pkg/data/protocol.json +69 -12
- package/build/pkg/gen1.js +13 -16
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/protocol.js +56 -37
- package/build/pkg/protocol.js.map +1 -1
- package/build.zig +82 -57
- package/package.json +33 -15
- package/src/bin/install-pkmn-engine +10 -5
- package/src/lib/binding/node.zig +46 -141
- package/src/lib/binding/wasm.zig +4 -2
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +15 -27
- package/src/lib/common/debug.zig +20 -11
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +60 -0
- package/src/lib/common/options.zig +9 -0
- package/src/lib/common/protocol.zig +643 -138
- 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 +257 -177
- package/src/lib/gen1/calc.zig +426 -0
- package/src/lib/gen1/chance.zig +1058 -0
- package/src/lib/gen1/data/moves.zig +168 -170
- package/src/lib/gen1/data/types.zig +2 -2
- package/src/lib/gen1/data.zig +61 -51
- package/src/lib/gen1/helpers.zig +588 -17
- package/src/lib/gen1/mechanics.zig +776 -502
- package/src/lib/gen2/README.md +102 -83
- package/src/lib/gen2/data/items.zig +1 -1
- package/src/lib/gen2/data/moves.zig +634 -562
- package/src/lib/gen2/data/types.zig +1 -1
- package/src/lib/gen2/data.zig +100 -67
- package/src/lib/gen2/helpers.zig +15 -21
- package/src/lib/gen2/mechanics.zig +1147 -11
- package/src/lib/pkmn.zig +61 -2
|
@@ -33,7 +33,7 @@ pub const Type = enum(u8) {
|
|
|
33
33
|
Dragon,
|
|
34
34
|
Dark,
|
|
35
35
|
|
|
36
|
-
const CHART
|
|
36
|
+
const CHART: [18][18]Effectiveness = .{
|
|
37
37
|
[_]Effectiveness{ N, N, N, N, N, R, N, I, R, N, N, N, N, N, N, N, N, N }, // Normal
|
|
38
38
|
[_]Effectiveness{ S, N, R, R, N, S, R, I, S, N, N, N, N, N, R, S, N, S }, // Fighting
|
|
39
39
|
[_]Effectiveness{ N, S, N, N, N, R, S, N, R, N, N, N, S, R, N, N, N, N }, // Flying
|
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,6 +16,8 @@ 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;
|
|
@@ -31,12 +36,17 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
|
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
|
|
|
@@ -49,20 +59,25 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
49
59
|
turn: u16 = 0,
|
|
50
60
|
field: Field = .{},
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
_1: u8 = 0,
|
|
63
|
+
_2: if (!showdown) u16 else u32 = 0,
|
|
64
|
+
|
|
65
|
+
pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
66
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
53
67
|
return &self.sides[@intFromEnum(player)];
|
|
54
68
|
}
|
|
55
69
|
|
|
56
|
-
pub inline fn foe(self:
|
|
70
|
+
pub inline fn foe(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
71
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
57
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,34 @@ 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
|
+
_1: u32 = 0,
|
|
110
|
+
_2: u16 = 0,
|
|
111
|
+
// NB: wCurPlayerMove / wCurEnemyMove (?)
|
|
112
|
+
_3: u8 = 0,
|
|
113
|
+
// NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
|
|
99
114
|
last_used_move: Move = .None,
|
|
100
|
-
_: u8 = 0,
|
|
101
115
|
|
|
102
|
-
const Conditions = packed struct {
|
|
116
|
+
const Conditions = packed struct(u32) {
|
|
103
117
|
Spikes: bool = false,
|
|
104
118
|
Safeguard: bool = false,
|
|
105
119
|
LightScreen: bool = false,
|
|
@@ -109,50 +123,69 @@ const Side = extern struct {
|
|
|
109
123
|
light_screen: u4 = 0,
|
|
110
124
|
reflect: u4 = 0,
|
|
111
125
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
126
|
+
future_sight: FutureSight = .{},
|
|
127
|
+
|
|
128
|
+
const FutureSight = packed struct(u16) {
|
|
129
|
+
damage: u12 = 0,
|
|
130
|
+
count: u4 = 0,
|
|
131
|
+
};
|
|
115
132
|
};
|
|
116
133
|
|
|
117
134
|
comptime {
|
|
118
|
-
assert(@sizeOf(Side) ==
|
|
135
|
+
assert(@sizeOf(Side) == 248);
|
|
119
136
|
}
|
|
120
137
|
|
|
121
|
-
pub inline fn get(self:
|
|
138
|
+
pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
139
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
122
140
|
assert(slot > 0 and slot <= 6);
|
|
123
141
|
const id = self.pokemon[slot - 1].position;
|
|
124
142
|
assert(id > 0 and id <= 6);
|
|
125
143
|
return &self.pokemon[id - 1];
|
|
126
144
|
}
|
|
127
145
|
|
|
128
|
-
pub inline fn stored(self:
|
|
146
|
+
pub inline fn stored(self: anytype) PointerType(@TypeOf(self), Pokemon) {
|
|
147
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
129
148
|
return self.get(1);
|
|
130
149
|
}
|
|
150
|
+
|
|
151
|
+
pub inline fn lastMove(self: anytype, encore: bool) Move {
|
|
152
|
+
assert(self.last_used_move != .None or !self.active.volatiles.dirty);
|
|
153
|
+
if (encore) return self.last_used_move;
|
|
154
|
+
return if (self.active.volatiles.dirty) .None else self.last_used_move;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// TODO
|
|
158
|
+
// pub inline fn useMove(self: anytype) void {
|
|
159
|
+
// assert(side.last_selected_move != .None);
|
|
160
|
+
// self.last_used_move = side.last_selected_move;
|
|
161
|
+
// self.active.volatiles.dirty = false;
|
|
162
|
+
// }
|
|
131
163
|
};
|
|
132
164
|
|
|
133
165
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
134
|
-
const ActivePokemon = extern struct {
|
|
135
|
-
volatiles:
|
|
166
|
+
pub const ActivePokemon = extern struct {
|
|
167
|
+
volatiles: Volatiles align(4) = .{},
|
|
136
168
|
stats: Stats(u16) = .{},
|
|
137
169
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
138
170
|
boosts: Boosts = .{},
|
|
171
|
+
types: Types = .{},
|
|
139
172
|
species: Species = .None,
|
|
140
|
-
|
|
141
|
-
last_move: Move = .None,
|
|
173
|
+
|
|
142
174
|
_: u8 = 0,
|
|
143
175
|
|
|
144
176
|
comptime {
|
|
145
177
|
assert(@sizeOf(ActivePokemon) == 44);
|
|
146
178
|
}
|
|
147
179
|
|
|
148
|
-
pub inline fn move(self:
|
|
180
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
181
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
149
182
|
assert(mslot > 0 and mslot <= 4);
|
|
150
183
|
assert(self.moves[mslot - 1].id != .None);
|
|
151
184
|
return &self.moves[mslot - 1];
|
|
152
185
|
}
|
|
153
186
|
};
|
|
154
187
|
|
|
155
|
-
const Pokemon = extern struct {
|
|
188
|
+
pub const Pokemon = extern struct {
|
|
156
189
|
stats: Stats(u16) = .{},
|
|
157
190
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
158
191
|
types: Types = .{},
|
|
@@ -169,7 +202,8 @@ const Pokemon = extern struct {
|
|
|
169
202
|
assert(@sizeOf(Pokemon) == 32);
|
|
170
203
|
}
|
|
171
204
|
|
|
172
|
-
pub inline fn move(self:
|
|
205
|
+
pub inline fn move(self: anytype, mslot: u8) PointerType(@TypeOf(self), MoveSlot) {
|
|
206
|
+
assert(@typeInfo(@TypeOf(self)).Pointer.child == Pokemon);
|
|
173
207
|
assert(mslot > 0 and mslot <= 4);
|
|
174
208
|
assert(self.moves[mslot - 1].id != .None);
|
|
175
209
|
return &self.moves[mslot - 1];
|
|
@@ -182,7 +216,7 @@ pub const Gender = enum(u2) {
|
|
|
182
216
|
Unknown,
|
|
183
217
|
};
|
|
184
218
|
|
|
185
|
-
const DVs = packed struct {
|
|
219
|
+
pub const DVs = packed struct(u16) {
|
|
186
220
|
gender: Gender = .Male,
|
|
187
221
|
pow: u6 = 40,
|
|
188
222
|
type: Type = .Dark,
|
|
@@ -190,7 +224,7 @@ const DVs = packed struct {
|
|
|
190
224
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
191
225
|
assert(p >= 1 and p <= 40);
|
|
192
226
|
assert(t != .Normal and t != .@"???");
|
|
193
|
-
return
|
|
227
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
194
228
|
}
|
|
195
229
|
|
|
196
230
|
pub inline fn power(dvs: DVs) u8 {
|
|
@@ -205,15 +239,11 @@ const DVs = packed struct {
|
|
|
205
239
|
0xFF => .Unknown,
|
|
206
240
|
else => if ((@as(u8, dvs.atk) << 4 | dvs.spe) < ratio) Gender.Female else Gender.Male,
|
|
207
241
|
};
|
|
208
|
-
const p = @intCast(
|
|
242
|
+
const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
|
|
209
243
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
210
244
|
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
|
|
211
245
|
if (t >= @intFromEnum(Type.@"???")) t = t + 1;
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
comptime {
|
|
216
|
-
assert(@sizeOf(DVs) == 2);
|
|
246
|
+
return .{ .gender = g, .pow = p, .type = @enumFromInt(t) };
|
|
217
247
|
}
|
|
218
248
|
};
|
|
219
249
|
|
|
@@ -238,10 +268,12 @@ test DVs {
|
|
|
238
268
|
try expectEqual(Gender.Female, dvs.gender);
|
|
239
269
|
}
|
|
240
270
|
|
|
241
|
-
|
|
271
|
+
/// Representation of a Generation II Pokémon's move slot in a battle.
|
|
272
|
+
pub const MoveSlot = extern struct {
|
|
273
|
+
/// The identifier for the move.
|
|
242
274
|
id: Move = .None,
|
|
243
|
-
|
|
244
|
-
pp:
|
|
275
|
+
/// The remaining PP of the move.
|
|
276
|
+
pp: u8 = 0,
|
|
245
277
|
|
|
246
278
|
comptime {
|
|
247
279
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -250,14 +282,16 @@ const MoveSlot = packed struct {
|
|
|
250
282
|
|
|
251
283
|
pub const Status = gen1.Status;
|
|
252
284
|
|
|
253
|
-
const
|
|
285
|
+
pub const Volatiles = packed struct(u128) {
|
|
254
286
|
Bide: bool = false,
|
|
255
287
|
Thrashing: bool = false,
|
|
288
|
+
BeatUp: bool = false,
|
|
256
289
|
Flinch: bool = false,
|
|
257
290
|
Charging: bool = false,
|
|
258
291
|
Underground: bool = false,
|
|
259
292
|
Flying: bool = false,
|
|
260
293
|
Confusion: bool = false,
|
|
294
|
+
|
|
261
295
|
Mist: bool = false,
|
|
262
296
|
FocusEnergy: bool = false,
|
|
263
297
|
Substitute: bool = false,
|
|
@@ -268,27 +302,28 @@ const Volatile = packed struct {
|
|
|
268
302
|
Transform: bool = false,
|
|
269
303
|
|
|
270
304
|
Nightmare: bool = false,
|
|
271
|
-
|
|
305
|
+
Curse: bool = false,
|
|
272
306
|
Protect: bool = false,
|
|
273
307
|
Foresight: bool = false,
|
|
274
308
|
PerishSong: bool = false,
|
|
275
309
|
Endure: bool = false,
|
|
276
310
|
Rollout: bool = false,
|
|
277
311
|
Attract: bool = false,
|
|
312
|
+
|
|
278
313
|
DefenseCurl: bool = false,
|
|
279
314
|
Encore: bool = false,
|
|
280
315
|
LockOn: bool = false,
|
|
281
316
|
DestinyBond: bool = false,
|
|
282
|
-
BeatUp: bool = false,
|
|
283
317
|
|
|
284
318
|
trapped: bool = false,
|
|
285
319
|
switched: bool = false,
|
|
286
320
|
switching: bool = false,
|
|
321
|
+
dirty: bool = false,
|
|
287
322
|
|
|
288
|
-
|
|
323
|
+
frozen: bool = false,
|
|
324
|
+
_: u19 = 0,
|
|
289
325
|
|
|
290
326
|
bind: u4 = 0,
|
|
291
|
-
future_sight: FutureSight = .{},
|
|
292
327
|
bide: u16 = 0,
|
|
293
328
|
disabled: Disabled = .{},
|
|
294
329
|
rage: u8 = 0,
|
|
@@ -296,24 +331,16 @@ const Volatile = packed struct {
|
|
|
296
331
|
toxic: u8 = 0,
|
|
297
332
|
confusion: u4 = 0,
|
|
298
333
|
encore: u4 = 0,
|
|
334
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
299
335
|
fury_cutter: u4 = 0,
|
|
300
336
|
perish_song: u4 = 0,
|
|
301
337
|
protect: u4 = 0,
|
|
302
|
-
|
|
338
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
303
339
|
|
|
304
|
-
const Disabled = packed struct {
|
|
340
|
+
const Disabled = packed struct(u8) {
|
|
305
341
|
move: u4 = 0,
|
|
306
342
|
duration: u4 = 0,
|
|
307
343
|
};
|
|
308
|
-
|
|
309
|
-
const FutureSight = packed struct {
|
|
310
|
-
damage: u12 = 0,
|
|
311
|
-
count: u4 = 0,
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
comptime {
|
|
315
|
-
assert(@sizeOf(Volatile) == 16);
|
|
316
|
-
}
|
|
317
344
|
};
|
|
318
345
|
|
|
319
346
|
pub fn Stats(comptime T: type) type {
|
|
@@ -327,10 +354,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
327
354
|
|
|
328
355
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
356
|
assert(level > 0 and level <= 100);
|
|
330
|
-
const
|
|
357
|
+
const float: f32 = @floatFromInt(exp);
|
|
358
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
331
359
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
360
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
|
-
return @intCast(
|
|
361
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
334
362
|
}
|
|
335
363
|
};
|
|
336
364
|
}
|
|
@@ -347,7 +375,7 @@ test Stats {
|
|
|
347
375
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
348
376
|
}
|
|
349
377
|
|
|
350
|
-
pub const Boosts = packed struct {
|
|
378
|
+
pub const Boosts = packed struct(u32) {
|
|
351
379
|
atk: i4 = 0,
|
|
352
380
|
def: i4 = 0,
|
|
353
381
|
spe: i4 = 0,
|
|
@@ -363,7 +391,7 @@ pub const Boosts = packed struct {
|
|
|
363
391
|
};
|
|
364
392
|
|
|
365
393
|
test Boosts {
|
|
366
|
-
const boosts =
|
|
394
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
367
395
|
try expectEqual(0, boosts.atk);
|
|
368
396
|
try expectEqual(-6, boosts.spd);
|
|
369
397
|
}
|
|
@@ -393,8 +421,13 @@ test Move {
|
|
|
393
421
|
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
394
422
|
const move = Move.get(.DynamicPunch);
|
|
395
423
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
|
-
try expectEqual(
|
|
397
|
-
try expectEqual(
|
|
424
|
+
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
425
|
+
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
426
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
427
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
428
|
+
try expect(!move.effect.isHighCritical());
|
|
429
|
+
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
430
|
+
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
398
431
|
}
|
|
399
432
|
|
|
400
433
|
pub const Species = species.Species;
|
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,12 +148,12 @@ 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 = @enumFromInt(
|
|
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
|
|
@@ -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 = @enumFromInt(
|
|
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),
|