@pkmn/engine 0.1.0-dev.f3aaee39 → 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/build/pkg/data/protocol.json +69 -12
- package/build/pkg/protocol.js +56 -37
- package/build/pkg/protocol.js.map +1 -1
- package/build.zig +2 -2
- package/package.json +20 -11
- package/src/lib/binding/node.zig +1 -1
- package/src/lib/common/data.zig +4 -16
- package/src/lib/common/protocol.zig +514 -66
- package/src/lib/common/rational.zig +1 -1
- package/src/lib/common/rng.zig +6 -6
- package/src/lib/gen1/README.md +3 -1
- package/src/lib/gen1/calc.zig +2 -3
- package/src/lib/gen1/chance.zig +26 -12
- package/src/lib/gen1/data/moves.zig +168 -170
- package/src/lib/gen1/data/types.zig +1 -1
- package/src/lib/gen1/data.zig +11 -23
- package/src/lib/gen1/helpers.zig +12 -12
- package/src/lib/gen1/mechanics.zig +32 -36
- package/src/lib/gen2/README.md +90 -70
- package/src/lib/gen2/data/moves.zig +634 -562
- package/src/lib/gen2/data/types.zig +1 -1
- package/src/lib/gen2/data.zig +74 -52
- package/src/lib/gen2/helpers.zig +8 -12
- package/src/lib/gen2/mechanics.zig +1142 -7
|
@@ -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
|
@@ -5,6 +5,7 @@ const pkmn = @import("../pkmn.zig");
|
|
|
5
5
|
|
|
6
6
|
const data = @import("../common/data.zig");
|
|
7
7
|
const DEBUG = @import("../common/debug.zig").print;
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
8
9
|
const rng = @import("../common/rng.zig");
|
|
9
10
|
const util = @import("../common/util.zig");
|
|
10
11
|
|
|
@@ -15,6 +16,8 @@ const types = @import("data/types.zig");
|
|
|
15
16
|
|
|
16
17
|
const gen1 = @import("../gen1/data.zig");
|
|
17
18
|
|
|
19
|
+
const calc = @import("calc.zig");
|
|
20
|
+
const chance = @import("chance.zig");
|
|
18
21
|
const mechanics = @import("mechanics.zig");
|
|
19
22
|
|
|
20
23
|
const assert = std.debug.assert;
|
|
@@ -33,6 +36,9 @@ pub const LOGS_SIZE = if (builtin.mode == .ReleaseSmall)
|
|
|
33
36
|
else
|
|
34
37
|
std.math.ceilPowerOfTwo(usize, MAX_LOGS) catch unreachable;
|
|
35
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
|
+
|
|
36
42
|
const Choice = data.Choice;
|
|
37
43
|
const ID = data.ID;
|
|
38
44
|
const Player = data.Player;
|
|
@@ -53,6 +59,9 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
53
59
|
turn: u16 = 0,
|
|
54
60
|
field: Field = .{},
|
|
55
61
|
|
|
62
|
+
_1: u8 = 0,
|
|
63
|
+
_2: if (!showdown) u16 else u32 = 0,
|
|
64
|
+
|
|
56
65
|
pub inline fn side(self: anytype, player: Player) PointerType(@TypeOf(self), Side) {
|
|
57
66
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Self);
|
|
58
67
|
return &self.sides[@intFromEnum(player)];
|
|
@@ -77,35 +86,34 @@ pub fn Battle(comptime RNG: anytype) type {
|
|
|
77
86
|
};
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
// TODO
|
|
81
89
|
test Battle {
|
|
82
|
-
try expectEqual(
|
|
90
|
+
try expectEqual(512, @sizeOf(Battle(PRNG)));
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
const Field = packed struct {
|
|
93
|
+
pub const Field = packed struct(u8) {
|
|
86
94
|
weather: Weather = .None,
|
|
87
95
|
weather_duration: u4 = 0,
|
|
88
|
-
|
|
89
|
-
comptime {
|
|
90
|
-
assert(@sizeOf(Field) == 1);
|
|
91
|
-
}
|
|
92
96
|
};
|
|
93
97
|
|
|
94
|
-
const Weather = enum(u4) {
|
|
98
|
+
pub const Weather = enum(u4) {
|
|
95
99
|
None,
|
|
96
100
|
Rain,
|
|
97
101
|
Sun,
|
|
98
102
|
Sandstorm,
|
|
99
103
|
};
|
|
100
104
|
|
|
101
|
-
const Side = extern struct {
|
|
105
|
+
pub const Side = extern struct {
|
|
102
106
|
pokemon: [6]Pokemon = [_]Pokemon{.{}} ** 6,
|
|
103
|
-
active: ActivePokemon = .{},
|
|
104
|
-
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)
|
|
105
114
|
last_used_move: Move = .None,
|
|
106
|
-
_: u8 = 0,
|
|
107
115
|
|
|
108
|
-
const Conditions = packed struct {
|
|
116
|
+
const Conditions = packed struct(u32) {
|
|
109
117
|
Spikes: bool = false,
|
|
110
118
|
Safeguard: bool = false,
|
|
111
119
|
LightScreen: bool = false,
|
|
@@ -115,13 +123,16 @@ const Side = extern struct {
|
|
|
115
123
|
light_screen: u4 = 0,
|
|
116
124
|
reflect: u4 = 0,
|
|
117
125
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
future_sight: FutureSight = .{},
|
|
127
|
+
|
|
128
|
+
const FutureSight = packed struct(u16) {
|
|
129
|
+
damage: u12 = 0,
|
|
130
|
+
count: u4 = 0,
|
|
131
|
+
};
|
|
121
132
|
};
|
|
122
133
|
|
|
123
134
|
comptime {
|
|
124
|
-
assert(@sizeOf(Side) ==
|
|
135
|
+
assert(@sizeOf(Side) == 248);
|
|
125
136
|
}
|
|
126
137
|
|
|
127
138
|
pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
|
|
@@ -136,17 +147,30 @@ const Side = extern struct {
|
|
|
136
147
|
assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
|
|
137
148
|
return self.get(1);
|
|
138
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
|
+
// }
|
|
139
163
|
};
|
|
140
164
|
|
|
141
165
|
// NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
|
|
142
|
-
const ActivePokemon = extern struct {
|
|
143
|
-
volatiles:
|
|
166
|
+
pub const ActivePokemon = extern struct {
|
|
167
|
+
volatiles: Volatiles align(4) = .{},
|
|
144
168
|
stats: Stats(u16) = .{},
|
|
145
169
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
146
170
|
boosts: Boosts = .{},
|
|
171
|
+
types: Types = .{},
|
|
147
172
|
species: Species = .None,
|
|
148
|
-
|
|
149
|
-
last_move: Move = .None,
|
|
173
|
+
|
|
150
174
|
_: u8 = 0,
|
|
151
175
|
|
|
152
176
|
comptime {
|
|
@@ -161,7 +185,7 @@ const ActivePokemon = extern struct {
|
|
|
161
185
|
}
|
|
162
186
|
};
|
|
163
187
|
|
|
164
|
-
const Pokemon = extern struct {
|
|
188
|
+
pub const Pokemon = extern struct {
|
|
165
189
|
stats: Stats(u16) = .{},
|
|
166
190
|
moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
|
|
167
191
|
types: Types = .{},
|
|
@@ -192,7 +216,7 @@ pub const Gender = enum(u2) {
|
|
|
192
216
|
Unknown,
|
|
193
217
|
};
|
|
194
218
|
|
|
195
|
-
const DVs = packed struct {
|
|
219
|
+
pub const DVs = packed struct(u16) {
|
|
196
220
|
gender: Gender = .Male,
|
|
197
221
|
pow: u6 = 40,
|
|
198
222
|
type: Type = .Dark,
|
|
@@ -200,7 +224,7 @@ const DVs = packed struct {
|
|
|
200
224
|
pub fn init(g: Gender, p: u6, t: Type) DVs {
|
|
201
225
|
assert(p >= 1 and p <= 40);
|
|
202
226
|
assert(t != .Normal and t != .@"???");
|
|
203
|
-
return
|
|
227
|
+
return .{ .gender = g, .pow = p, .type = t };
|
|
204
228
|
}
|
|
205
229
|
|
|
206
230
|
pub inline fn power(dvs: DVs) u8 {
|
|
@@ -219,11 +243,7 @@ const DVs = packed struct {
|
|
|
219
243
|
((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
|
|
220
244
|
var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
|
|
221
245
|
if (t >= @intFromEnum(Type.@"???")) t = t + 1;
|
|
222
|
-
return
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
comptime {
|
|
226
|
-
assert(@sizeOf(DVs) == 2);
|
|
246
|
+
return .{ .gender = g, .pow = p, .type = @enumFromInt(t) };
|
|
227
247
|
}
|
|
228
248
|
};
|
|
229
249
|
|
|
@@ -248,10 +268,12 @@ test DVs {
|
|
|
248
268
|
try expectEqual(Gender.Female, dvs.gender);
|
|
249
269
|
}
|
|
250
270
|
|
|
251
|
-
|
|
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.
|
|
252
274
|
id: Move = .None,
|
|
253
|
-
|
|
254
|
-
pp:
|
|
275
|
+
/// The remaining PP of the move.
|
|
276
|
+
pp: u8 = 0,
|
|
255
277
|
|
|
256
278
|
comptime {
|
|
257
279
|
assert(@sizeOf(MoveSlot) == 2);
|
|
@@ -260,14 +282,16 @@ const MoveSlot = packed struct {
|
|
|
260
282
|
|
|
261
283
|
pub const Status = gen1.Status;
|
|
262
284
|
|
|
263
|
-
const
|
|
285
|
+
pub const Volatiles = packed struct(u128) {
|
|
264
286
|
Bide: bool = false,
|
|
265
287
|
Thrashing: bool = false,
|
|
288
|
+
BeatUp: bool = false,
|
|
266
289
|
Flinch: bool = false,
|
|
267
290
|
Charging: bool = false,
|
|
268
291
|
Underground: bool = false,
|
|
269
292
|
Flying: bool = false,
|
|
270
293
|
Confusion: bool = false,
|
|
294
|
+
|
|
271
295
|
Mist: bool = false,
|
|
272
296
|
FocusEnergy: bool = false,
|
|
273
297
|
Substitute: bool = false,
|
|
@@ -278,27 +302,28 @@ const Volatile = packed struct {
|
|
|
278
302
|
Transform: bool = false,
|
|
279
303
|
|
|
280
304
|
Nightmare: bool = false,
|
|
281
|
-
|
|
305
|
+
Curse: bool = false,
|
|
282
306
|
Protect: bool = false,
|
|
283
307
|
Foresight: bool = false,
|
|
284
308
|
PerishSong: bool = false,
|
|
285
309
|
Endure: bool = false,
|
|
286
310
|
Rollout: bool = false,
|
|
287
311
|
Attract: bool = false,
|
|
312
|
+
|
|
288
313
|
DefenseCurl: bool = false,
|
|
289
314
|
Encore: bool = false,
|
|
290
315
|
LockOn: bool = false,
|
|
291
316
|
DestinyBond: bool = false,
|
|
292
|
-
BeatUp: bool = false,
|
|
293
317
|
|
|
294
318
|
trapped: bool = false,
|
|
295
319
|
switched: bool = false,
|
|
296
320
|
switching: bool = false,
|
|
321
|
+
dirty: bool = false,
|
|
297
322
|
|
|
298
|
-
|
|
323
|
+
frozen: bool = false,
|
|
324
|
+
_: u19 = 0,
|
|
299
325
|
|
|
300
326
|
bind: u4 = 0,
|
|
301
|
-
future_sight: FutureSight = .{},
|
|
302
327
|
bide: u16 = 0,
|
|
303
328
|
disabled: Disabled = .{},
|
|
304
329
|
rage: u8 = 0,
|
|
@@ -306,24 +331,16 @@ const Volatile = packed struct {
|
|
|
306
331
|
toxic: u8 = 0,
|
|
307
332
|
confusion: u4 = 0,
|
|
308
333
|
encore: u4 = 0,
|
|
334
|
+
// TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
|
|
309
335
|
fury_cutter: u4 = 0,
|
|
310
336
|
perish_song: u4 = 0,
|
|
311
337
|
protect: u4 = 0,
|
|
312
|
-
|
|
338
|
+
attacks: u4 = 0, // bide/thrashing/rollout turns
|
|
313
339
|
|
|
314
|
-
const Disabled = packed struct {
|
|
340
|
+
const Disabled = packed struct(u8) {
|
|
315
341
|
move: u4 = 0,
|
|
316
342
|
duration: u4 = 0,
|
|
317
343
|
};
|
|
318
|
-
|
|
319
|
-
const FutureSight = packed struct {
|
|
320
|
-
damage: u12 = 0,
|
|
321
|
-
count: u4 = 0,
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
comptime {
|
|
325
|
-
assert(@sizeOf(Volatile) == 16);
|
|
326
|
-
}
|
|
327
344
|
};
|
|
328
345
|
|
|
329
346
|
pub fn Stats(comptime T: type) type {
|
|
@@ -358,7 +375,7 @@ test Stats {
|
|
|
358
375
|
try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
|
|
359
376
|
}
|
|
360
377
|
|
|
361
|
-
pub const Boosts = packed struct {
|
|
378
|
+
pub const Boosts = packed struct(u32) {
|
|
362
379
|
atk: i4 = 0,
|
|
363
380
|
def: i4 = 0,
|
|
364
381
|
spe: i4 = 0,
|
|
@@ -374,7 +391,7 @@ pub const Boosts = packed struct {
|
|
|
374
391
|
};
|
|
375
392
|
|
|
376
393
|
test Boosts {
|
|
377
|
-
const boosts =
|
|
394
|
+
const boosts: Boosts = .{ .spd = -6 };
|
|
378
395
|
try expectEqual(0, boosts.atk);
|
|
379
396
|
try expectEqual(-6, boosts.spd);
|
|
380
397
|
}
|
|
@@ -404,8 +421,13 @@ test Move {
|
|
|
404
421
|
try expectEqual(251, @intFromEnum(Move.BeatUp));
|
|
405
422
|
const move = Move.get(.DynamicPunch);
|
|
406
423
|
try expectEqual(Move.Effect.ConfusionChance, move.effect);
|
|
407
|
-
try expectEqual(
|
|
408
|
-
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());
|
|
409
431
|
}
|
|
410
432
|
|
|
411
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) },
|
|
@@ -87,7 +87,7 @@ pub const Side = struct {
|
|
|
87
87
|
|
|
88
88
|
for (0..ps.len) |i| {
|
|
89
89
|
side.pokemon[i] = Pokemon.init(ps[i]);
|
|
90
|
-
|
|
90
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
91
91
|
}
|
|
92
92
|
return side;
|
|
93
93
|
}
|
|
@@ -98,7 +98,7 @@ pub const Side = struct {
|
|
|
98
98
|
|
|
99
99
|
for (0..n) |i| {
|
|
100
100
|
side.pokemon[i] = Pokemon.random(rand, opt);
|
|
101
|
-
side.pokemon[i].position = i + 1;
|
|
101
|
+
side.pokemon[i].position = @as(u4, @intCast(i)) + 1;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
return side;
|
|
@@ -135,12 +135,9 @@ pub const Pokemon = struct {
|
|
|
135
135
|
}
|
|
136
136
|
assert(p.moves.len > 0 and p.moves.len <= 4);
|
|
137
137
|
for (p.moves, 0..) |m, j| {
|
|
138
|
-
pokemon.moves[j] =
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// NB: PP can be at most 61 legally (though can overflow to 63)
|
|
142
|
-
.pp = @intCast(@min(Move.pp(m) / 5 * 8, 61)),
|
|
143
|
-
};
|
|
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));
|
|
144
141
|
}
|
|
145
142
|
pokemon.item = p.item;
|
|
146
143
|
if (p.hp) |hp| {
|
|
@@ -151,7 +148,7 @@ pub const Pokemon = struct {
|
|
|
151
148
|
pokemon.status = p.status;
|
|
152
149
|
pokemon.types = species.types;
|
|
153
150
|
pokemon.level = p.level;
|
|
154
|
-
pokemon.dvs = DVs.from(p.dvs);
|
|
151
|
+
pokemon.dvs = DVs.from(p.species, p.dvs);
|
|
155
152
|
return pokemon;
|
|
156
153
|
}
|
|
157
154
|
|
|
@@ -192,14 +189,13 @@ pub const Pokemon = struct {
|
|
|
192
189
|
const max_pp: u8 = @intCast(Move.pp(m) + pp_ups * @min(Move.pp(m) / 5, 7));
|
|
193
190
|
ms[i] = .{
|
|
194
191
|
.id = m,
|
|
195
|
-
.pp_ups = pp_ups,
|
|
196
192
|
.pp = if (opt.cleric) max_pp else rand.range(u8, 0, max_pp + 1),
|
|
197
193
|
};
|
|
198
194
|
}
|
|
199
195
|
return .{
|
|
200
196
|
.species = s,
|
|
201
197
|
.types = species.types,
|
|
202
|
-
.dvs = DVs.from(dvs),
|
|
198
|
+
.dvs = DVs.from(s, dvs),
|
|
203
199
|
.level = lvl,
|
|
204
200
|
.stats = stats,
|
|
205
201
|
.hp = if (opt.cleric) stats.hp else rand.range(u16, 0, stats.hp + 1),
|