@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.
@@ -214,7 +214,7 @@ pub const Pokemon = extern struct {
214
214
  }
215
215
  };
216
216
 
217
- /// Representation of a Generation I & II Pokémon's move slot in a battle.
217
+ /// Representation of a Generation I Pokémon's move slot in a battle.
218
218
  pub const MoveSlot = extern struct {
219
219
  /// The identifier for the move.
220
220
  id: Move = .None,
@@ -222,20 +222,16 @@ pub const MoveSlot = extern struct {
222
222
  pp: u8 = 0,
223
223
 
224
224
  comptime {
225
- assert(@sizeOf(MoveSlot) == @sizeOf(u16));
225
+ assert(@sizeOf(MoveSlot) == 2);
226
226
  }
227
227
  };
228
228
 
229
229
  /// Details required to detect desyncs based on the move last selected/executed by players.
230
- pub const MoveDetails = packed struct {
230
+ pub const MoveDetails = packed struct(if (showdown) u16 else u8) {
231
231
  /// The move slot index of the last move selected in the battle menu.
232
232
  index: if (showdown) u8 else u4 = 0,
233
233
  /// Whether the last move executed was counterable.
234
234
  counterable: if (showdown) u8 else u4 = 0,
235
-
236
- comptime {
237
- assert(@sizeOf(MoveDetails) == if (showdown) 2 else 1);
238
- }
239
235
  };
240
236
 
241
237
  /// Bitfield representation of a Generation I & II Pokémon's major status condition.
@@ -329,7 +325,7 @@ test Status {
329
325
  }
330
326
 
331
327
  /// Bitfield representation of volatile statuses and associated data in Generation I.
332
- pub const Volatiles = packed struct {
328
+ pub const Volatiles = packed struct(u64) {
333
329
  /// Whether the "Bide" volatile status is present.
334
330
  Bide: bool = false,
335
331
  /// Whether the "Thrashing" volatile status is present.
@@ -387,10 +383,6 @@ pub const Volatiles = packed struct {
387
383
  disabled_move: u3 = 0,
388
384
  /// The number of turns toxic damage has been accumulating.
389
385
  toxic: u5 = 0,
390
-
391
- comptime {
392
- assert(@sizeOf(Volatiles) == 8);
393
- }
394
386
  };
395
387
 
396
388
  test Volatiles {
@@ -456,7 +448,7 @@ test Stats {
456
448
  }
457
449
 
458
450
  /// Representation of a Pokémon's boosts in Generation I.
459
- pub const Boosts = packed struct {
451
+ pub const Boosts = packed struct(u32) {
460
452
  /// A Pokémon's Attack boosts.
461
453
  atk: i4 = 0,
462
454
  /// A Pokémon's Defense boosts.
@@ -471,10 +463,6 @@ pub const Boosts = packed struct {
471
463
  evasion: i4 = 0,
472
464
 
473
465
  _: u8 = 0,
474
-
475
- comptime {
476
- assert(@sizeOf(Boosts) == 4);
477
- }
478
466
  };
479
467
 
480
468
  test Boosts {
@@ -490,7 +478,7 @@ test Move {
490
478
  try expectEqual(2, @intFromEnum(Move.KarateChop));
491
479
  const move = Move.get(.Fissure);
492
480
  try expectEqual(Move.Effect.OHKO, move.effect);
493
- try expectEqual(@as(u8, 30), move.accuracy);
481
+ try expectEqual(rng.Gen12.percent(30), move.accuracy);
494
482
  try expectEqual(Type.Ground, move.type);
495
483
 
496
484
  try expect(!Move.Effect.onBegin(.None));
@@ -62,10 +62,6 @@ const MAX_STAT_VALUE = 999;
62
62
  const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
63
63
 
64
64
  pub fn update(battle: anytype, c1: Choice, c2: Choice, options: anytype) !Result {
65
- // FIXME
66
- // std.debug.print("\n", .{});
67
- // DEBUG(battle.turn);
68
- // std.debug.print("// ", .{});
69
65
  assert(c1.type != .Pass or c2.type != .Pass or battle.turn == 0);
70
66
  if (battle.turn == 0) return start(battle, options);
71
67
 
@@ -214,12 +210,9 @@ fn selectMove(
214
210
  }
215
211
  break :ok true;
216
212
  };
217
-
218
213
  assert(struggle);
219
- saveMove(battle, player, choice);
220
- } else {
221
- saveMove(battle, player, choice);
222
214
  }
215
+ saveMove(battle, player, choice);
223
216
 
224
217
  return null;
225
218
  }
@@ -1438,7 +1431,7 @@ fn moveHit(
1438
1431
  const overwritten = overwrite and state > 0;
1439
1432
  assert(!overwritten or (0 < state and state <= 255 and !side.active.volatiles.Bide));
1440
1433
 
1441
- var accuracy = if (overwritten) state else @as(u16, Gen12.percent(move.accuracy));
1434
+ var accuracy = if (overwritten) state else @as(u16, move.accuracy);
1442
1435
  var boost = BOOSTS[@as(u4, @intCast(@as(i8, side.active.boosts.accuracy) + 6))];
1443
1436
  accuracy = accuracy * boost[0] / boost[1];
1444
1437
  boost = BOOSTS[@as(u4, @intCast(@as(i8, -foe.active.boosts.evasion) + 6))];
@@ -1548,21 +1541,19 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !void {
1548
1541
  var volatiles = &side.active.volatiles;
1549
1542
 
1550
1543
  const brn = Status.is(stored.status, .BRN);
1551
- if (brn or Status.is(stored.status, .PSN)) blk: {
1544
+ if (brn or Status.is(stored.status, .PSN)) {
1552
1545
  var damage = @max(stored.stats.hp / 16, 1);
1553
-
1554
1546
  if (volatiles.Toxic) {
1555
1547
  volatiles.toxic += 1;
1556
1548
  damage *= volatiles.toxic;
1557
1549
  }
1550
+ damage = @min(damage, stored.hp);
1558
1551
 
1559
- const amount = @min(damage, stored.hp);
1560
- if (showdown and amount == 0) break :blk;
1561
-
1562
- stored.hp -= amount;
1563
-
1564
- // Pokémon Showdown uses damageOf here but its not relevant in Generation I
1565
- try options.log.damage(ident, stored, if (brn) Damage.Burn else Damage.Poison);
1552
+ if (!showdown or damage > 0) {
1553
+ stored.hp -= damage;
1554
+ // Pokémon Showdown uses damageOf here but its not relevant in Generation I
1555
+ try options.log.damage(ident, stored, if (brn) Damage.Burn else Damage.Poison);
1556
+ }
1566
1557
  }
1567
1558
 
1568
1559
  if (volatiles.LeechSeed) {
@@ -2395,12 +2386,17 @@ pub const Effects = struct {
2395
2386
  }
2396
2387
 
2397
2388
  fn switchAndTeleport(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
2398
- if (!showdown or battle.side(player).last_selected_move == .Teleport) return;
2399
-
2400
2389
  // Whirlwind/Roar should not roll to hit/reset damage but Pokémon Showdown does anyway
2401
- if (try checkHit(battle, player, move, options)) try options.chance.commit(player, .hit);
2402
-
2403
- battle.last_damage = 0;
2390
+ if (showdown) {
2391
+ if (battle.side(player).last_selected_move == .Teleport) return;
2392
+ if (try checkHit(battle, player, move, options)) {
2393
+ try options.chance.commit(player, .hit);
2394
+ }
2395
+ battle.last_damage = 0;
2396
+ } else {
2397
+ try options.log.fail(battle.active(player), .None);
2398
+ try options.log.laststill();
2399
+ }
2404
2400
  }
2405
2401
 
2406
2402
  fn thrashing(battle: anytype, player: Player, options: anytype) void {
@@ -219,6 +219,81 @@ much overhead the preceding representations have after taking into consideration
219
219
  padding](https://en.wikipedia.org/wiki/Data_structure_alignment) and
220
220
  [denormalization](https://en.wikipedia.org/wiki/Denormalization):
221
221
 
222
+ ## Details
223
+
224
+ | pkmn | Pokémon Crystal (pret) |
225
+ | ------------------------- | ------------------------------------------------- |
226
+ | `start` | `DoBattle` |
227
+ | `update` | `BattleTurn` |
228
+ | `selectMove` / `saveMove` | `CheckPlayerLockedIn` / `ParsePlayerAction` |
229
+ | `switchIn` | `SwitchPlayerMon` / `SendOutPlayerMon` |
230
+ | `turnOrder` | `DetermineMoveOrder` |
231
+ | `doTurn` | `DoTurn` |
232
+ | `executeMove` | `DoMove` |
233
+ | `beforeMove` | `CheckTurn` |
234
+ | `canMove` | |
235
+ | `decrementPP` | |
236
+ | `doMove` | |
237
+ | `checkCriticalHit` | `BattleCommand_Critical` |
238
+ | `calcDamage` | `BattleCommand_DamageCalc` / `PlayerAttackDamage` |
239
+ | `adjustDamage` | `BattleCommand_Stab` |
240
+ | `randomizeDamage` | `BattleCommand_DamageVariation` |
241
+ | `specialDamage` | `BattleCommand_ConstantDamage` |
242
+ | `applyDamage` | `BattleCommand_ApplyDamage` |
243
+ | `checkHit` | `BattleCommand_CheckHit` |
244
+ | `checkFaint` | `CheckFaint` |
245
+ | `handleResidual` | `ResidualDamage` |
246
+ | `endTurn` | `EndTurn` |
247
+
248
+ DoBattle
249
+ BattleTurn
250
+ - HandleBerserkGene
251
+ - CheckPlayerLockedIn
252
+ - BattleMenu
253
+ - ParsePlayerAction
254
+ - DetermineMoveOrder
255
+ - CompareMovePriority
256
+ - GetMovePriority
257
+ - Battle_*
258
+ - Handle*Switch
259
+ - SwitchPlayerMon
260
+ - BreakAttraction
261
+ - SendOutPlayerMon
262
+ - SpikesDamage
263
+ - *Turn_EndOpponentProtectEndureDestinyBond
264
+ - EndUserDestinyBond
265
+ - Do*Turn
266
+ - EndOpponentProtectEndureDestinyBond
267
+ - ResidualDamage
268
+ - CheckFaint_*
269
+ - HandleBetweenTurnEffects
270
+ - HandleFutureSight
271
+ - HandleWeather
272
+ - HandleWrap
273
+ - HandlePerishSong
274
+ - HandleLeftovers
275
+ - HandleMysteryBerry
276
+ - HandleDefrost
277
+ - HandleSafeguard
278
+ - HandleScreens
279
+ - HandleStatBoostingHeldItems
280
+ - HandleHealingItems
281
+ - HandleEncore
282
+
283
+ DoTurn
284
+ DoMove = executeMove
285
+ CheckTurn = beforeMove
286
+ BattleCommand_Critical = checkCriticalHit
287
+ BattleCommand_Stab = adjustDamage
288
+ BattleCommand_DamageVariation = randomizeDamage
289
+ BattleCommand_CheckHit = checkHit
290
+ BattleCommand_ApplyDamage = applyDamage
291
+ BattleCommand_CheckFaint
292
+ BattleCommand_BuildOpponentRage = buildRage
293
+ BattleCommand_DamageCalc (PlayerAttackDamage / EnemyAttackDamage) = calcDamage
294
+ BattleCommand_ConstantDamage = specialDamage
295
+ DoEnemyDamage / DoPlayerDamage / DoSubstituteDamage
296
+
222
297
  ==**TODO**==
223
298
 
224
299
  ## Protocol