@pkmn/engine 0.1.0-dev.d97c6e03 → 0.1.0-dev.dd5b9081
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 +67 -56
- package/build/pkg/addon.d.ts +1 -1
- package/build/pkg/addon.js +7 -6
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +25 -3
- package/build/pkg/gen1.d.ts +5 -4
- package/build/pkg/gen1.js +46 -30
- 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 +4 -2
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +29 -12
- package/build/test/display.js.map +1 -1
- package/build.zig +89 -65
- package/package.json +26 -16
- package/src/bin/install-pkmn-engine +11 -6
- package/src/lib/binding/node.zig +45 -140
- package/src/lib/binding/wasm.zig +5 -3
- package/src/lib/common/battle.zig +17 -0
- package/src/lib/common/data.zig +14 -26
- package/src/lib/common/debug.zig +21 -11
- package/src/lib/common/js.zig +118 -0
- package/src/lib/common/optional.zig +60 -0
- package/src/lib/common/options.zig +12 -3
- package/src/lib/common/protocol.zig +315 -197
- package/src/lib/common/rational.zig +353 -0
- package/src/lib/common/rng.zig +11 -11
- package/src/lib/common/util.zig +12 -0
- package/src/lib/gen1/README.md +258 -155
- package/src/lib/gen1/calc.zig +426 -0
- package/src/lib/gen1/chance.zig +1036 -0
- package/src/lib/gen1/data/moves.zig +178 -180
- package/src/lib/gen1/data/species.zig +2 -2
- package/src/lib/gen1/data/types.zig +3 -3
- package/src/lib/gen1/data.zig +68 -54
- package/src/lib/gen1/helpers.zig +592 -32
- package/src/lib/gen1/mechanics.zig +908 -547
- package/src/lib/gen2/README.md +87 -13
- package/src/lib/gen2/data/items.zig +3 -3
- package/src/lib/gen2/data/moves.zig +624 -563
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +3 -3
- package/src/lib/gen2/data.zig +100 -70
- package/src/lib/gen2/helpers.zig +16 -22
- package/src/lib/gen2/mechanics.zig +714 -10
- package/src/lib/pkmn.zig +65 -6
|
@@ -86,17 +86,17 @@ pub const Type = enum(u8) {
|
|
|
86
86
|
|
|
87
87
|
/// Whether or not this type is considered to be special as opposed to physical.
|
|
88
88
|
pub inline fn special(self: Type) bool {
|
|
89
|
-
return @
|
|
89
|
+
return @intFromEnum(self) >= @intFromEnum(Type.Fire);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/// The `Effectiveness` of type `t2` vs. type `t1`.
|
|
93
93
|
pub inline fn effectiveness(t1: Type, t2: Type) Effectiveness {
|
|
94
|
-
return CHART[@
|
|
94
|
+
return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/// The precedence order of type type.
|
|
98
98
|
pub inline fn precedence(self: Type) u8 {
|
|
99
|
-
return PRECEDENCE[@
|
|
99
|
+
return PRECEDENCE[@intFromEnum(self)];
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
102
|
|
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
|
-
|
|
53
|
-
|
|
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);
|
|
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,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,
|
|
@@ -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
|
-
if (t >= @
|
|
212
|
-
return DVs{ .gender = g, .pow = p, .type = @
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
comptime {
|
|
216
|
-
assert(@sizeOf(DVs) == 2);
|
|
245
|
+
if (t >= @intFromEnum(Type.@"???")) t = t + 1;
|
|
246
|
+
return DVs{ .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,7 +282,7 @@ 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,
|
|
256
288
|
Flinch: bool = false,
|
|
@@ -268,7 +300,7 @@ const Volatile = packed struct {
|
|
|
268
300
|
Transform: bool = false,
|
|
269
301
|
|
|
270
302
|
Nightmare: bool = false,
|
|
271
|
-
|
|
303
|
+
Curse: bool = false,
|
|
272
304
|
Protect: bool = false,
|
|
273
305
|
Foresight: bool = false,
|
|
274
306
|
PerishSong: bool = false,
|
|
@@ -284,11 +316,11 @@ const Volatile = packed struct {
|
|
|
284
316
|
trapped: bool = false,
|
|
285
317
|
switched: bool = false,
|
|
286
318
|
switching: bool = false,
|
|
319
|
+
dirty: bool = false,
|
|
287
320
|
|
|
288
|
-
_:
|
|
321
|
+
_: u20 = 0,
|
|
289
322
|
|
|
290
323
|
bind: u4 = 0,
|
|
291
|
-
future_sight: FutureSight = .{},
|
|
292
324
|
bide: u16 = 0,
|
|
293
325
|
disabled: Disabled = .{},
|
|
294
326
|
rage: u8 = 0,
|
|
@@ -296,24 +328,16 @@ const Volatile = packed struct {
|
|
|
296
328
|
toxic: u8 = 0,
|
|
297
329
|
confusion: u4 = 0,
|
|
298
330
|
encore: u4 = 0,
|
|
331
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
299
332
|
fury_cutter: u4 = 0,
|
|
300
333
|
perish_song: u4 = 0,
|
|
301
334
|
protect: u4 = 0,
|
|
302
|
-
|
|
335
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
303
336
|
|
|
304
|
-
const Disabled = packed struct {
|
|
337
|
+
const Disabled = packed struct(u8) {
|
|
305
338
|
move: u4 = 0,
|
|
306
339
|
duration: u4 = 0,
|
|
307
340
|
};
|
|
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
341
|
};
|
|
318
342
|
|
|
319
343
|
pub fn Stats(comptime T: type) type {
|
|
@@ -327,10 +351,11 @@ pub fn Stats(comptime T: type) type {
|
|
|
327
351
|
|
|
328
352
|
pub fn calc(comptime stat: []const u8, base: T, dv: u4, exp: u16, level: u8) T {
|
|
329
353
|
assert(level > 0 and level <= 100);
|
|
330
|
-
const
|
|
354
|
+
const float: f32 = @floatFromInt(exp);
|
|
355
|
+
const evs: u16 = @min(255, @as(u16, @intFromFloat(@ceil(@sqrt(float)))));
|
|
331
356
|
const core: u32 = (2 *% (@as(u32, base) +% dv)) +% (evs / 4);
|
|
332
357
|
const factor: u32 = if (std.mem.eql(u8, stat, "hp")) level + 10 else 5;
|
|
333
|
-
return @intCast(
|
|
358
|
+
return @intCast(core *% @as(u32, level) / 100 +% factor);
|
|
334
359
|
}
|
|
335
360
|
};
|
|
336
361
|
}
|
|
@@ -347,7 +372,7 @@ test Stats {
|
|
|
347
372
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
348
373
|
}
|
|
349
374
|
|
|
350
|
-
pub const Boosts = packed struct {
|
|
375
|
+
pub const Boosts = packed struct(u32) {
|
|
351
376
|
atk: i4 = 0,
|
|
352
377
|
def: i4 = 0,
|
|
353
378
|
spe: i4 = 0,
|
|
@@ -390,17 +415,22 @@ test Item {
|
|
|
390
415
|
pub const Move = moves.Move;
|
|
391
416
|
|
|
392
417
|
test Move {
|
|
393
|
-
try expectEqual(251, @
|
|
418
|
+
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
394
419
|
const move = Move.get(.DynamicPunch);
|
|
395
420
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
396
|
-
try expectEqual(
|
|
397
|
-
try expectEqual(
|
|
421
|
+
try expectEqual(rng.Gen12.percent(50), move.accuracy);
|
|
422
|
+
try expectEqual(rng.Gen12.percent(100), move.chance);
|
|
423
|
+
try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
|
|
424
|
+
try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
|
|
425
|
+
try expect(!move.effect.isHighCritical());
|
|
426
|
+
try expect(Move.get(.KarateChop).effect.isHighCritical());
|
|
427
|
+
try expect(Move.get(.RazorWind).effect.isHighCritical());
|
|
398
428
|
}
|
|
399
429
|
|
|
400
430
|
pub const Species = species.Species;
|
|
401
431
|
|
|
402
432
|
test Species {
|
|
403
|
-
try expectEqual(152, @
|
|
433
|
+
try expectEqual(152, @intFromEnum(Species.Chikorita));
|
|
404
434
|
try expectEqual(@as(u8, 100), Species.get(.Celebi).stats.spd);
|
|
405
435
|
}
|
|
406
436
|
|
|
@@ -409,7 +439,7 @@ pub const Types = types.Types;
|
|
|
409
439
|
pub const Effectiveness = gen1.Effectiveness;
|
|
410
440
|
|
|
411
441
|
test Types {
|
|
412
|
-
try expectEqual(13, @
|
|
442
|
+
try expectEqual(13, @intFromEnum(Type.Electric));
|
|
413
443
|
|
|
414
444
|
try expect(!Type.Steel.special());
|
|
415
445
|
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),
|