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

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.
@@ -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(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
+ _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
- comptime {
119
- assert(@sizeOf(Conditions) == 2);
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) == 240);
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: Volatile align(4) = .{},
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
- item: Item = .None,
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,
@@ -221,10 +245,6 @@ const DVs = packed struct {
221
245
  if (t >= @intFromEnum(Type.@"???")) t = t + 1;
222
246
  return DVs{ .gender = g, .pow = p, .type = @enumFromInt(t) };
223
247
  }
224
-
225
- comptime {
226
- assert(@sizeOf(DVs) == 2);
227
- }
228
248
  };
229
249
 
230
250
  test DVs {
@@ -248,10 +268,12 @@ test DVs {
248
268
  try expectEqual(Gender.Female, dvs.gender);
249
269
  }
250
270
 
251
- const MoveSlot = packed struct {
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
- pp_ups: u2 = 3,
254
- pp: u6 = 0,
275
+ /// The remaining PP of the move.
276
+ pp: u8 = 0,
255
277
 
256
278
  comptime {
257
279
  assert(@sizeOf(MoveSlot) == 2);
@@ -260,7 +282,7 @@ const MoveSlot = packed struct {
260
282
 
261
283
  pub const Status = gen1.Status;
262
284
 
263
- const Volatile = packed struct {
285
+ pub const Volatiles = packed struct(u128) {
264
286
  Bide: bool = false,
265
287
  Thrashing: bool = false,
266
288
  Flinch: bool = false,
@@ -278,7 +300,7 @@ const Volatile = packed struct {
278
300
  Transform: bool = false,
279
301
 
280
302
  Nightmare: bool = false,
281
- Cure: bool = false,
303
+ Curse: bool = false,
282
304
  Protect: bool = false,
283
305
  Foresight: bool = false,
284
306
  PerishSong: bool = false,
@@ -294,11 +316,11 @@ const Volatile = packed struct {
294
316
  trapped: bool = false,
295
317
  switched: bool = false,
296
318
  switching: bool = false,
319
+ dirty: bool = false,
297
320
 
298
- _: u5 = 0,
321
+ _: u20 = 0,
299
322
 
300
323
  bind: u4 = 0,
301
- future_sight: FutureSight = .{},
302
324
  bide: u16 = 0,
303
325
  disabled: Disabled = .{},
304
326
  rage: u8 = 0,
@@ -306,24 +328,16 @@ const Volatile = packed struct {
306
328
  toxic: u8 = 0,
307
329
  confusion: u4 = 0,
308
330
  encore: u4 = 0,
331
+ // TODO merge with attacks (ie rollout/thrashing)? need to be zeroed at same time
309
332
  fury_cutter: u4 = 0,
310
333
  perish_song: u4 = 0,
311
334
  protect: u4 = 0,
312
- rollout: u4 = 0,
335
+ attacks: u4 = 0, // bide/thrashing/rollout turns
313
336
 
314
- const Disabled = packed struct {
337
+ const Disabled = packed struct(u8) {
315
338
  move: u4 = 0,
316
339
  duration: u4 = 0,
317
340
  };
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
341
  };
328
342
 
329
343
  pub fn Stats(comptime T: type) type {
@@ -358,7 +372,7 @@ test Stats {
358
372
  try expectEqual(@as(u16, 279), Stats(u16).calc("spe", base.spe, 15, 63001, 100));
359
373
  }
360
374
 
361
- pub const Boosts = packed struct {
375
+ pub const Boosts = packed struct(u32) {
362
376
  atk: i4 = 0,
363
377
  def: i4 = 0,
364
378
  spe: i4 = 0,
@@ -404,8 +418,13 @@ test Move {
404
418
  try expectEqual(251, @intFromEnum(Move.BeatUp));
405
419
  const move = Move.get(.DynamicPunch);
406
420
  try expectEqual(Move.Effect.ConfusionChance, move.effect);
407
- try expectEqual(@as(u8, 50), move.accuracy);
408
- try expectEqual(@as(u8, 5), move.pp);
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());
409
428
  }
410
429
 
411
430
  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),