@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.
Files changed (57) hide show
  1. package/README.md +98 -59
  2. package/build/pkg/addon.d.ts +7 -0
  3. package/build/pkg/addon.js +112 -0
  4. package/build/pkg/addon.js.map +1 -0
  5. package/build/pkg/data/ids.json +12 -12
  6. package/build/pkg/data/layout.json +25 -3
  7. package/build/pkg/data/protocol.json +69 -12
  8. package/build/pkg/data.d.ts +3 -4
  9. package/build/pkg/data.js +3 -5
  10. package/build/pkg/data.js.map +1 -1
  11. package/build/pkg/gen1.d.ts +5 -4
  12. package/build/pkg/gen1.js +47 -31
  13. package/build/pkg/gen1.js.map +1 -1
  14. package/build/pkg/index.d.ts +15 -12
  15. package/build/pkg/index.js.map +1 -1
  16. package/build/pkg/protocol.d.ts +4 -2
  17. package/build/pkg/protocol.js +60 -39
  18. package/build/pkg/protocol.js.map +1 -1
  19. package/build/test/display.js +400 -0
  20. package/build/test/display.js.map +1 -0
  21. package/build/tools/debug.js +133 -0
  22. package/build/tools/debug.js.map +1 -0
  23. package/build.zig +98 -79
  24. package/package.json +65 -36
  25. package/src/bin/install-pkmn-engine +11 -6
  26. package/src/bin/pkmn-debug +62 -0
  27. package/src/lib/binding/node.zig +47 -142
  28. package/src/lib/binding/wasm.zig +6 -4
  29. package/src/lib/common/battle.zig +17 -0
  30. package/src/lib/common/data.zig +15 -27
  31. package/src/lib/common/debug.zig +21 -11
  32. package/src/lib/common/js.zig +118 -0
  33. package/src/lib/common/optional.zig +67 -0
  34. package/src/lib/common/options.zig +12 -3
  35. package/src/lib/common/protocol.zig +765 -245
  36. package/src/lib/common/rational.zig +353 -0
  37. package/src/lib/common/rng.zig +15 -15
  38. package/src/lib/common/util.zig +12 -0
  39. package/src/lib/gen1/README.md +268 -154
  40. package/src/lib/gen1/calc.zig +426 -0
  41. package/src/lib/gen1/chance.zig +1060 -0
  42. package/src/lib/gen1/data/moves.zig +192 -180
  43. package/src/lib/gen1/data/species.zig +2 -2
  44. package/src/lib/gen1/data/types.zig +5 -5
  45. package/src/lib/gen1/data.zig +78 -64
  46. package/src/lib/gen1/helpers.zig +577 -32
  47. package/src/lib/gen1/mechanics.zig +1023 -694
  48. package/src/lib/gen2/README.md +106 -85
  49. package/src/lib/gen2/data/items.zig +12 -18
  50. package/src/lib/gen2/data/moves.zig +660 -566
  51. package/src/lib/gen2/data/species.zig +1 -1
  52. package/src/lib/gen2/data/types.zig +130 -28
  53. package/src/lib/gen2/data.zig +135 -87
  54. package/src/lib/gen2/helpers.zig +16 -22
  55. package/src/lib/gen2/mechanics.zig +2594 -11
  56. package/src/lib/pkmn.zig +68 -9
  57. package/src/test/display/pkmn.html.tmpl +368 -0
@@ -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 options = @import("../common/options.zig");
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 MAX_OPTIONS: usize = 9; // move 1..4, switch 2..6
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 OPTIONS_SIZE = if (builtin.mode == .ReleaseSmall)
26
- MAX_OPTIONS
30
+ pub const CHOICES_SIZE = if (builtin.mode == .ReleaseSmall)
31
+ MAX_CHOICES
27
32
  else
28
- std.math.ceilPowerOfTwo(usize, MAX_OPTIONS) catch unreachable;
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 showdown = options.showdown;
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
- pub inline fn side(self: *Self, player: Player) *Side {
53
- return &self.sides[@enumToInt(player)];
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: *Self, player: Player) *Side {
57
- return &self.sides[@enumToInt(player.foe())];
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(u3, self.side(player).pokemon[0].position));
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, log: anytype) !Result {
65
- return mechanics.update(self, c1, c2, log);
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(496, @sizeOf(Battle(PRNG)));
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
- const Conditions = packed struct {
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
- comptime {
113
- assert(@sizeOf(Conditions) == 2);
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) == 240);
133
+ assert(@sizeOf(Side) == 248);
119
134
  }
120
135
 
121
- pub inline fn get(self: *const Side, slot: u8) *Pokemon {
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: *Side) *Pokemon {
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: Volatile align(4) = .{},
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
- item: Item = .None,
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: *ActivePokemon, mslot: u8) *MoveSlot {
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: *Pokemon, mslot: u8) *MoveSlot {
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 DVs{ .gender = g, .pow = p, .type = t };
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(u6, 1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
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) + 1;
211
- if (t >= @enumToInt(Type.@"???")) t = t + 1;
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
- const MoveSlot = packed struct {
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
- pp_ups: u2 = 3,
244
- pp: u6 = 0,
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
- const Volatile = packed struct {
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
- Cure: bool = false,
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
- _: u5 = 0,
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
- rollout: u4 = 0,
353
+ attacks: u4 = 0, // bide/thrashing/rollout turns
303
354
 
304
- const Disabled = packed struct {
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 FutureSight = packed struct {
310
- damage: u12 = 0,
311
- count: u4 = 0,
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 evs = @min(255, @floatToInt(u16, @ceil(@sqrt(@intToFloat(f32, exp)))));
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(T, core *% @as(u32, level) / 100 +% factor);
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 = Boosts{ .spd = -6 };
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.mail(.UpGrade));
380
- try expect(Item.mail(.FlowerMail));
381
- try expect(Item.mail(.MirageMail));
382
- try expect(!Item.mail(.TownMap));
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, @enumToInt(Move.BeatUp));
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(@as(u8, 50), move.accuracy);
397
- try expectEqual(@as(u8, 5), move.pp);
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, @enumToInt(Species.Chikorita));
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, @enumToInt(Type.Electric));
460
+ try expectEqual(13, @intFromEnum(Type.Electric));
413
461
 
414
462
  try expect(!Type.Steel.special());
415
463
  try expect(Type.Dark.special());
@@ -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(1, rolls.len)) {
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
- // FIXME side.order[i] = i + 1;
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
- .id = m,
142
- .pp_ups = 3,
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 = @intToEnum(Species, rand.range(u8, 1, Species.size + 1));
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 = @enumToInt(if (showdown) Item.FlowerMail else Item.TM50);
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 = @intToEnum(Move, rand.range(u8, 1, 164 + 1));
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(u8, Move.pp(m) + @as(u8, pp_ups) * @min(Move.pp(m) / 5, 7));
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),