@pkmn/engine 0.1.0-dev.f3aaee39 → 0.1.0-dev.f6c1a379

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.
@@ -14,15 +14,15 @@ const I = Effectiveness.Immune;
14
14
 
15
15
  /// Representation of a Generation II type in Pokémon.
16
16
  pub const Type = enum(u8) {
17
+ Ground,
18
+ Rock,
19
+ Steel,
17
20
  Normal,
18
21
  Fighting,
19
22
  Flying,
20
23
  Poison,
21
- Ground,
22
- Rock,
23
24
  Bug,
24
25
  Ghost,
25
- Steel,
26
26
  @"???",
27
27
  Fire,
28
28
  Water,
@@ -33,37 +33,37 @@ pub const Type = enum(u8) {
33
33
  Dragon,
34
34
  Dark,
35
35
 
36
- const CHART = [18][18]Effectiveness{
37
- [_]Effectiveness{ N, N, N, N, N, R, N, I, R, N, N, N, N, N, N, N, N, N }, // Normal
38
- [_]Effectiveness{ S, N, R, R, N, S, R, I, S, N, N, N, N, N, R, S, N, S }, // Fighting
39
- [_]Effectiveness{ N, S, N, N, N, R, S, N, R, N, N, N, S, R, N, N, N, N }, // Flying
40
- [_]Effectiveness{ N, N, N, R, R, R, N, R, I, N, N, N, S, N, N, N, N, N }, // Poison
41
- [_]Effectiveness{ N, N, I, S, N, S, R, N, S, N, S, N, R, S, N, N, N, N }, // Ground
42
- [_]Effectiveness{ N, R, S, N, R, N, S, N, R, N, S, N, N, N, N, S, N, N }, // Rock
43
- [_]Effectiveness{ N, R, R, R, N, N, N, R, R, N, R, N, S, N, S, N, N, S }, // Bug
44
- [_]Effectiveness{ I, N, N, N, N, N, N, S, R, N, N, N, N, N, S, N, N, R }, // Ghost
45
- [_]Effectiveness{ N, N, N, N, N, S, N, N, R, N, R, R, N, R, N, S, N, N }, // Steel
36
+ const CHART: [18][18]Effectiveness = .{
37
+ [_]Effectiveness{ N, S, S, N, N, I, S, R, N, N, S, N, R, S, N, N, N, N }, // Ground
38
+ [_]Effectiveness{ R, N, R, N, R, S, N, S, N, N, S, N, N, N, N, S, N, N }, // Rock
39
+ [_]Effectiveness{ N, S, R, N, N, N, N, N, N, N, R, R, N, R, N, S, N, N }, // Steel
40
+ [_]Effectiveness{ N, R, R, N, N, N, N, N, I, N, N, N, N, N, N, N, N, N }, // Normal
41
+ [_]Effectiveness{ N, S, S, S, N, R, R, R, I, N, N, N, N, N, R, S, N, S }, // Fighting
42
+ [_]Effectiveness{ N, R, R, N, S, N, N, S, N, N, N, N, S, R, N, N, N, N }, // Flying
43
+ [_]Effectiveness{ R, R, I, N, N, N, R, N, R, N, N, N, S, N, N, N, N, N }, // Poison
44
+ [_]Effectiveness{ N, N, R, N, R, R, R, N, R, N, R, N, S, N, S, N, N, S }, // Bug
45
+ [_]Effectiveness{ N, N, R, I, N, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Ghost
46
46
  [_]Effectiveness{ N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N }, // ???
47
- [_]Effectiveness{ N, N, N, N, N, R, S, N, S, N, R, R, S, N, N, S, R, N }, // Fire
48
- [_]Effectiveness{ N, N, N, N, S, S, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
49
- [_]Effectiveness{ N, N, R, R, S, S, R, N, R, N, R, S, R, N, N, N, R, N }, // Grass
50
- [_]Effectiveness{ N, N, S, N, I, N, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
51
- [_]Effectiveness{ N, S, N, S, N, N, N, N, R, N, N, N, N, N, R, N, N, I }, // Psychic
52
- [_]Effectiveness{ N, N, S, N, S, N, N, N, R, N, R, R, S, N, N, R, S, N }, // Ice
53
- [_]Effectiveness{ N, N, N, N, N, N, N, N, R, N, N, N, N, N, N, N, S, N }, // Dragon
54
- [_]Effectiveness{ N, R, N, N, N, N, N, S, R, N, N, N, N, N, S, N, N, R }, // Dark
47
+ [_]Effectiveness{ N, R, S, N, N, N, N, S, N, N, R, R, S, N, N, S, R, N }, // Fire
48
+ [_]Effectiveness{ S, S, N, N, N, N, N, N, N, N, S, R, R, N, N, N, R, N }, // Water
49
+ [_]Effectiveness{ S, S, R, N, N, R, R, R, N, N, R, S, R, N, N, N, R, N }, // Grass
50
+ [_]Effectiveness{ I, N, N, N, N, S, N, N, N, N, N, S, R, R, N, N, R, N }, // Electric
51
+ [_]Effectiveness{ N, N, R, N, S, N, S, N, N, N, N, N, N, N, R, N, N, I }, // Psychic
52
+ [_]Effectiveness{ S, N, R, N, N, S, N, N, N, N, R, R, S, N, N, R, S, N }, // Ice
53
+ [_]Effectiveness{ N, N, R, N, N, N, N, N, N, N, N, N, N, N, N, N, S, N }, // Dragon
54
+ [_]Effectiveness{ N, N, R, N, R, N, N, N, S, N, N, N, N, N, S, N, N, R }, // Dark
55
55
  };
56
56
 
57
57
  const PRECEDENCE = [_]u8{
58
+ 9, // Ground
59
+ 13, // Rock
60
+ 17, // Steel
58
61
  1, // Normal
59
62
  7, // Fighting
60
63
  10, // Flying
61
64
  8, // Poison
62
- 9, // Ground
63
- 13, // Rock
64
65
  12, // Bug
65
66
  14, // Ghost
66
- 17, // Steel
67
67
  0, // ???
68
68
  2, // Fire
69
69
  3, // Water
@@ -75,10 +75,89 @@ pub const Type = enum(u8) {
75
75
  16, // Dark
76
76
  };
77
77
 
78
+ const INDEXES = [_]u8{
79
+ 4, // Ground
80
+ 5, // Rock
81
+ 9, // Steel
82
+ 0, // Normal
83
+ 1, // Fighting
84
+ 2, // Flying
85
+ 3, // Poison
86
+ 7, // Bug
87
+ 8, // Ghost
88
+ 19, // ???
89
+ 20, // Fire
90
+ 21, // Water
91
+ 22, // Grass
92
+ 23, // Electric
93
+ 24, // Psychic
94
+ 25, // Ice
95
+ 26, // Dragon
96
+ 27, // Dark
97
+ };
98
+
99
+ const CONVERSION2 = [_]Type{
100
+ .Normal,
101
+ .Fighting,
102
+ .Flying,
103
+ .Poison,
104
+ .Ground,
105
+ .Rock,
106
+ .@"???", // placeholder
107
+ .Bug,
108
+ .Ghost,
109
+ .Steel,
110
+ };
111
+
112
+ const HIDDEN_POWER = [_]Type{
113
+ .Fighting,
114
+ .Flying,
115
+ .Poison,
116
+ .Ground,
117
+ .Rock,
118
+ .Bug,
119
+ .Ghost,
120
+ .Steel,
121
+ .Fire,
122
+ .Water,
123
+ .Grass,
124
+ .Electric,
125
+ .Psychic,
126
+ .Ice,
127
+ .Dragon,
128
+ .Dark,
129
+ };
130
+
131
+ /// Order of Pokémon Showdown's types.
132
+ pub const SHOWDOWN = [_]Type{
133
+ .Fire,
134
+ .Ice,
135
+ .Steel,
136
+ .Electric,
137
+ .Ghost,
138
+ .Grass,
139
+ .Dark,
140
+ .Bug,
141
+ .Dragon,
142
+ .Fighting,
143
+ .Flying,
144
+ .Ground,
145
+ .Normal,
146
+ .Poison,
147
+ .Psychic,
148
+ .Rock,
149
+ .Water,
150
+ .@"???",
151
+ };
152
+
78
153
  comptime {
79
154
  assert(@bitSizeOf(Type) == 8);
80
155
  assert(@sizeOf(@TypeOf(CHART)) == 324);
81
156
  assert(@sizeOf(@TypeOf(PRECEDENCE)) == 18);
157
+ assert(@sizeOf(@TypeOf(INDEXES)) == 18);
158
+ assert(@sizeOf(@TypeOf(CONVERSION2)) == 10);
159
+ assert(@sizeOf(@TypeOf(HIDDEN_POWER)) == 16);
160
+ assert(@sizeOf(@TypeOf(SHOWDOWN)) == 18);
82
161
  }
83
162
 
84
163
  /// The number of types in this generation.
@@ -94,10 +173,28 @@ pub const Type = enum(u8) {
94
173
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
95
174
  }
96
175
 
97
- /// The precedence order of type type.
176
+ /// The precedence order of Type `type`.
98
177
  pub inline fn precedence(self: Type) u8 {
99
178
  return PRECEDENCE[@intFromEnum(self)];
100
179
  }
180
+
181
+ /// The internal index of this Type used by Present.
182
+ pub inline fn present(self: Type) u8 {
183
+ return INDEXES[@intFromEnum(self)];
184
+ }
185
+
186
+ /// The Type corresponding to a random roll of `num` for Conversion 2.
187
+ pub inline fn conversion2(num: u8) Type {
188
+ assert(num != 6);
189
+ assert(num < 10 or num >= 20);
190
+ assert(num <= 27);
191
+ return if (num < 10) CONVERSION2[num] else @enumFromInt(num - 10);
192
+ }
193
+
194
+ /// The Type corresponding to a Hidden Power `index`.
195
+ pub inline fn hiddenPower(index: u8) Type {
196
+ return HIDDEN_POWER[index];
197
+ }
101
198
  };
102
199
 
103
200
  /// Representation of a Pokémon's typing.
@@ -120,4 +217,9 @@ pub const Types = extern struct {
120
217
  pub inline fn includes(self: Types, t: Type) bool {
121
218
  return self.type1 == t or self.type2 == t;
122
219
  }
220
+
221
+ /// Whether this typing is immune to damage from Sandstorm.
222
+ pub inline fn sandstormImmune(self: Types) u8 {
223
+ return self.type1 <= @intFromEnum(Type.Steel) or self.type2 <= @intFromEnum(Type.Steel);
224
+ }
123
225
  };
@@ -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;
@@ -45,6 +51,7 @@ const showdown = pkmn.options.showdown;
45
51
  pub const PRNG = rng.PRNG(2);
46
52
 
47
53
  pub fn Battle(comptime RNG: anytype) type {
54
+ const padding = if (!showdown) 3 else 5;
48
55
  return extern struct {
49
56
  const Self = @This();
50
57
 
@@ -53,6 +60,8 @@ pub fn Battle(comptime RNG: anytype) type {
53
60
  turn: u16 = 0,
54
61
  field: Field = .{},
55
62
 
63
+ _: [padding]u8 = .{0} ** padding,
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,32 @@ pub fn Battle(comptime RNG: anytype) type {
77
86
  };
78
87
  }
79
88
 
80
- // TODO
81
89
  test Battle {
82
- try expectEqual(496, @sizeOf(Battle(PRNG)));
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
+ // NB: wLastPlayerCounterMove / wLastEnemyCounterMove (wLastPlayerMove / wLastEnemyMove)
105
110
  last_used_move: Move = .None,
106
- _: u8 = 0,
107
111
 
108
- const Conditions = packed struct {
112
+ _: [7]u8 = .{0} ** 7,
113
+
114
+ const Conditions = packed struct(u32) {
109
115
  Spikes: bool = false,
110
116
  Safeguard: bool = false,
111
117
  LightScreen: bool = false,
@@ -115,13 +121,16 @@ const Side = extern struct {
115
121
  light_screen: u4 = 0,
116
122
  reflect: u4 = 0,
117
123
 
118
- comptime {
119
- assert(@sizeOf(Conditions) == 2);
120
- }
124
+ future_sight: FutureSight = .{},
125
+
126
+ const FutureSight = packed struct(u16) {
127
+ damage: u12 = 0,
128
+ count: u4 = 0,
129
+ };
121
130
  };
122
131
 
123
132
  comptime {
124
- assert(@sizeOf(Side) == 240);
133
+ assert(@sizeOf(Side) == 248);
125
134
  }
126
135
 
127
136
  pub inline fn get(self: anytype, slot: u8) PointerType(@TypeOf(self), Pokemon) {
@@ -136,17 +145,30 @@ const Side = extern struct {
136
145
  assert(@typeInfo(@TypeOf(self)).Pointer.child == Side);
137
146
  return self.get(1);
138
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
+ // }
139
161
  };
140
162
 
141
163
  // NOTE: DVs (Gender & Hidden Power) and Happiness are stored only in Pokemon
142
- const ActivePokemon = extern struct {
143
- volatiles: Volatile align(4) = .{},
164
+ pub const ActivePokemon = extern struct {
165
+ volatiles: Volatiles align(4) = .{},
144
166
  stats: Stats(u16) = .{},
145
167
  moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
146
168
  boosts: Boosts = .{},
169
+ types: Types = .{},
147
170
  species: Species = .None,
148
- item: Item = .None,
149
- last_move: Move = .None,
171
+
150
172
  _: u8 = 0,
151
173
 
152
174
  comptime {
@@ -161,7 +183,7 @@ const ActivePokemon = extern struct {
161
183
  }
162
184
  };
163
185
 
164
- const Pokemon = extern struct {
186
+ pub const Pokemon = extern struct {
165
187
  stats: Stats(u16) = .{},
166
188
  moves: [4]MoveSlot = [_]MoveSlot{.{}} ** 4,
167
189
  types: Types = .{},
@@ -192,7 +214,7 @@ pub const Gender = enum(u2) {
192
214
  Unknown,
193
215
  };
194
216
 
195
- const DVs = packed struct {
217
+ pub const DVs = packed struct(u16) {
196
218
  gender: Gender = .Male,
197
219
  pow: u6 = 40,
198
220
  type: Type = .Dark,
@@ -200,7 +222,7 @@ const DVs = packed struct {
200
222
  pub fn init(g: Gender, p: u6, t: Type) DVs {
201
223
  assert(p >= 1 and p <= 40);
202
224
  assert(t != .Normal and t != .@"???");
203
- return DVs{ .gender = g, .pow = p, .type = t };
225
+ return .{ .gender = g, .pow = p, .type = t };
204
226
  }
205
227
 
206
228
  pub inline fn power(dvs: DVs) u8 {
@@ -217,13 +239,8 @@ const DVs = packed struct {
217
239
  };
218
240
  const p: u6 = @intCast(1 + ((5 * @as(u8, (dvs.atk & 0b1000) | ((dvs.def & 0b1000)) >> 1 |
219
241
  ((dvs.spe & 0b1000)) >> 2 | ((dvs.spc & 0b1000)) >> 3) + (dvs.spc & 0b0011)) >> 1));
220
- var t = @as(u8, dvs.def & 0b0011) + (@as(u8, dvs.atk & 0b0011) << 2) + 1;
221
- if (t >= @intFromEnum(Type.@"???")) t = t + 1;
222
- return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
223
- }
224
-
225
- comptime {
226
- 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) };
227
244
  }
228
245
  };
229
246
 
@@ -248,10 +265,12 @@ test DVs {
248
265
  try expectEqual(Gender.Female, dvs.gender);
249
266
  }
250
267
 
251
- 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.
252
271
  id: Move = .None,
253
- pp_ups: u2 = 3,
254
- pp: u6 = 0,
272
+ /// The remaining PP of the move.
273
+ pp: u8 = 0,
255
274
 
256
275
  comptime {
257
276
  assert(@sizeOf(MoveSlot) == 2);
@@ -260,14 +279,16 @@ const MoveSlot = packed struct {
260
279
 
261
280
  pub const Status = gen1.Status;
262
281
 
263
- const Volatile = packed struct {
282
+ pub const Volatiles = packed struct(u128) {
264
283
  Bide: bool = false,
265
284
  Thrashing: bool = false,
285
+ BeatUp: bool = false,
266
286
  Flinch: bool = false,
267
287
  Charging: bool = false,
268
288
  Underground: bool = false,
269
289
  Flying: bool = false,
270
290
  Confusion: bool = false,
291
+
271
292
  Mist: bool = false,
272
293
  FocusEnergy: bool = false,
273
294
  Substitute: bool = false,
@@ -278,27 +299,29 @@ const Volatile = packed struct {
278
299
  Transform: bool = false,
279
300
 
280
301
  Nightmare: bool = false,
281
- Cure: bool = false,
302
+ Curse: bool = false,
282
303
  Protect: bool = false,
283
304
  Foresight: bool = false,
284
305
  PerishSong: bool = false,
285
306
  Endure: bool = false,
286
307
  Rollout: bool = false,
287
308
  Attract: bool = false,
309
+
288
310
  DefenseCurl: bool = false,
289
311
  Encore: bool = false,
290
312
  LockOn: bool = false,
291
313
  DestinyBond: bool = false,
292
- BeatUp: bool = false,
293
314
 
294
315
  trapped: bool = false,
295
316
  switched: bool = false,
296
317
  switching: bool = false,
318
+ dirty: bool = false,
297
319
 
298
- _: u5 = 0,
320
+ frozen: bool = false,
321
+ _: u15 = 0,
299
322
 
323
+ bind_reason: u4 = 0,
300
324
  bind: u4 = 0,
301
- future_sight: FutureSight = .{},
302
325
  bide: u16 = 0,
303
326
  disabled: Disabled = .{},
304
327
  rage: u8 = 0,
@@ -306,24 +329,16 @@ const Volatile = packed struct {
306
329
  toxic: u8 = 0,
307
330
  confusion: u4 = 0,
308
331
  encore: u4 = 0,
332
+ // TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
309
333
  fury_cutter: u4 = 0,
310
334
  perish_song: u4 = 0,
311
335
  protect: u4 = 0,
312
- rollout: u4 = 0,
336
+ attacks: u4 = 0, // bide/thrashing/rollout turns
313
337
 
314
- const Disabled = packed struct {
338
+ const Disabled = packed struct(u8) {
315
339
  move: u4 = 0,
316
340
  duration: u4 = 0,
317
341
  };
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
342
  };
328
343
 
329
344
  pub fn Stats(comptime T: type) type {
@@ -358,7 +373,7 @@ test Stats {
358
373
  try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
359
374
  }
360
375
 
361
- pub const Boosts = packed struct {
376
+ pub const Boosts = packed struct(u32) {
362
377
  atk: i4 = 0,
363
378
  def: i4 = 0,
364
379
  spe: i4 = 0,
@@ -374,7 +389,7 @@ pub const Boosts = packed struct {
374
389
  };
375
390
 
376
391
  test Boosts {
377
- const boosts = Boosts{ .spd = -6 };
392
+ const boosts: Boosts = .{ .spd = -6 };
378
393
  try expectEqual(0, boosts.atk);
379
394
  try expectEqual(-6, boosts.spd);
380
395
  }
@@ -387,15 +402,10 @@ test Item {
387
402
  try expectEqual(Type.Normal, Item.boost(.PolkadotBow).?);
388
403
  try expectEqual(Type.Dark, Item.boost(.BlackGlasses).?);
389
404
 
390
- try expect(!Item.mail(.UpGrade));
391
- try expect(Item.mail(.FlowerMail));
392
- try expect(Item.mail(.MirageMail));
393
- try expect(!Item.mail(.TownMap));
394
-
395
- try expect(!Item.berry(.LightBall));
396
- try expect(Item.berry(.PSNCureBerry));
397
- try expect(Item.berry(.GoldBerry));
398
- try expect(!Item.berry(.MasterBall));
405
+ try expect(!Item.isMail(.UpGrade));
406
+ try expect(Item.isMail(.FlowerMail));
407
+ try expect(Item.isMail(.MirageMail));
408
+ try expect(!Item.isMail(.TownMap));
399
409
  }
400
410
 
401
411
  pub const Move = moves.Move;
@@ -404,8 +414,13 @@ test Move {
404
414
  try expectEqual(251, @intFromEnum(Move.BeatUp));
405
415
  const move = Move.get(.DynamicPunch);
406
416
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
407
- try expectEqual(@as(u8, 50), move.accuracy);
408
- try expectEqual(@as(u8, 5), move.pp);
417
+ try expectEqual(rng.Gen12.percent(50), move.accuracy);
418
+ try expectEqual(rng.Gen12.percent(100), move.chance);
419
+ try expectEqual(@as(u8, 5), Move.pp(.DynamicPunch));
420
+ try expectEqual(@as(i8, -1), Move.get(.Counter).priority);
421
+ try expect(!move.effect.isHighCritical());
422
+ try expect(Move.get(.KarateChop).effect.isHighCritical());
423
+ try expect(Move.get(.RazorWind).effect.isHighCritical());
409
424
  }
410
425
 
411
426
  pub const Species = species.Species;
@@ -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) },
@@ -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
- // FIXME side.order[i] = i + 1;
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
- .id = m,
140
- .pp_ups = 3,
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),