@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.
@@ -30,7 +30,7 @@ pub const Type = enum(u4) {
30
30
  Ice,
31
31
  Dragon,
32
32
 
33
- const CHART = [15][15]Effectiveness{
33
+ const CHART: [15][15]Effectiveness = .{
34
34
  [_]Effectiveness{ N, N, N, N, N, R, N, I, N, N, N, N, N, N, N }, // Normal
35
35
  [_]Effectiveness{ S, N, R, R, N, S, R, I, N, N, N, N, R, S, N }, // Fighting
36
36
  [_]Effectiveness{ N, S, N, N, N, R, S, N, N, N, S, R, N, N, N }, // Flying
@@ -99,7 +99,7 @@ pub const Type = enum(u4) {
99
99
  return CHART[@intFromEnum(t1)][@intFromEnum(t2)];
100
100
  }
101
101
 
102
- /// The precedence order of type `t2` vs. type `t1`.
102
+ /// The precedence order of Type `t2` vs. Type `t1`.
103
103
  pub fn precedence(t1: Type, t2: Type) u8 {
104
104
  for (PRECEDENCE, 0..) |matchup, i| {
105
105
  if (matchup.type1 == t1 and matchup.type2 == t2) return @intCast(i);
@@ -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,14 +383,10 @@ 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 {
397
- var volatiles = Volatiles{};
389
+ var volatiles: Volatiles = .{};
398
390
  volatiles.Confusion = true;
399
391
  volatiles.confusion = 2;
400
392
  volatiles.Thrashing = true;
@@ -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,14 +463,10 @@ 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 {
481
- const boosts = Boosts{ .spc = -6 };
469
+ const boosts: Boosts = .{ .spc = -6 };
482
470
  try expectEqual(0, boosts.atk);
483
471
  try expectEqual(-6, boosts.spc);
484
472
  }
@@ -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));
@@ -628,16 +616,16 @@ pub const DVs = struct {
628
616
  };
629
617
 
630
618
  test DVs {
631
- var dvs = DVs{ .spc = 15, .spe = 15 };
619
+ var dvs: DVs = .{ .spc = 15, .spe = 15 };
632
620
  try expectEqual(@as(u4, 15), dvs.hp());
633
- dvs = DVs{
621
+ dvs = .{
634
622
  .atk = 5,
635
623
  .def = 15,
636
624
  .spe = 13,
637
625
  .spc = 13,
638
626
  };
639
627
  try expectEqual(@as(u4, 15), dvs.hp());
640
- dvs = DVs{
628
+ dvs = .{
641
629
  .def = 3,
642
630
  .spe = 10,
643
631
  .spc = 11,
@@ -478,13 +478,13 @@ pub const Rolls = struct {
478
478
  };
479
479
 
480
480
  test "Rolls.speedTie" {
481
- const actions = Actions{ .p1 = .{ .speed_tie = .P2 } };
481
+ const actions: Actions = .{ .p1 = .{ .speed_tie = .P2 } };
482
482
  try expectEqualSlices(Optional(Player), &.{ .P1, .P2 }, Rolls.speedTie(actions.p1));
483
483
  try expectEqualSlices(Optional(Player), &.{.None}, Rolls.speedTie(actions.p2));
484
484
  }
485
485
 
486
486
  test "Rolls.damage" {
487
- const actions = Actions{ .p2 = .{ .damage = 221 } };
487
+ const actions: Actions = .{ .p2 = .{ .damage = 221 } };
488
488
  try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p1, .None));
489
489
  try expectEqual(Rolls.Range{ .min = 217, .max = 256 }, Rolls.damage(actions.p2, .None));
490
490
  try expectEqual(Rolls.Range{ .min = 0, .max = 1 }, Rolls.damage(actions.p2, .false));
@@ -502,7 +502,7 @@ test "Rolls.coalesce" {
502
502
  }
503
503
 
504
504
  test "Rolls.hit" {
505
- const actions = Actions{ .p2 = .{ .hit = .true } };
505
+ const actions: Actions = .{ .p2 = .{ .hit = .true } };
506
506
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.hit(actions.p1, .None));
507
507
  try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.hit(actions.p2, .None));
508
508
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.hit(actions.p2, .true));
@@ -510,7 +510,7 @@ test "Rolls.hit" {
510
510
  }
511
511
 
512
512
  test "Rolls.secondaryChance" {
513
- const actions = Actions{ .p1 = .{ .secondary_chance = .true } };
513
+ const actions: Actions = .{ .p1 = .{ .secondary_chance = .true } };
514
514
  try expectEqualSlices(
515
515
  Optional(bool),
516
516
  &.{ .false, .true },
@@ -521,7 +521,7 @@ test "Rolls.secondaryChance" {
521
521
  }
522
522
 
523
523
  test "Rolls.criticalHit" {
524
- const actions = Actions{ .p1 = .{ .critical_hit = .true } };
524
+ const actions: Actions = .{ .p1 = .{ .critical_hit = .true } };
525
525
  try expectEqualSlices(
526
526
  Optional(bool),
527
527
  &.{ .false, .true },
@@ -532,14 +532,14 @@ test "Rolls.criticalHit" {
532
532
  }
533
533
 
534
534
  test "Rolls.confused" {
535
- const actions = Actions{ .p2 = .{ .confused = .true } };
535
+ const actions: Actions = .{ .p2 = .{ .confused = .true } };
536
536
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p1, 1));
537
537
  try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.confused(actions.p2, 1));
538
538
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.confused(actions.p2, 0));
539
539
  }
540
540
 
541
541
  test "Rolls.paralyzed" {
542
- const actions = Actions{ .p2 = .{ .paralyzed = .true } };
542
+ const actions: Actions = .{ .p2 = .{ .paralyzed = .true } };
543
543
  try expectEqualSlices(Optional(bool), &.{.None}, Rolls.paralyzed(actions.p1, .None));
544
544
  try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.paralyzed(actions.p2, .None));
545
545
  try expectEqualSlices(Optional(bool), &.{ .false, .true }, Rolls.paralyzed(actions.p2, .false));
@@ -547,7 +547,7 @@ test "Rolls.paralyzed" {
547
547
  }
548
548
 
549
549
  test "Rolls.duration" {
550
- const actions = Actions{ .p2 = .{ .duration = 3 } };
550
+ const actions: Actions = .{ .p2 = .{ .duration = 3 } };
551
551
  try expectEqualSlices(u1, &.{0}, Rolls.duration(actions.p1, .None));
552
552
  try expectEqualSlices(u1, &.{1}, Rolls.duration(actions.p2, .None));
553
553
  try expectEqualSlices(u1, &.{0}, Rolls.duration(actions.p2, .false));
@@ -757,27 +757,27 @@ test "Rolls.binding" {
757
757
  }
758
758
 
759
759
  test "Rolls.moveSlot" {
760
- const actions = Actions{ .p2 = .{ .move_slot = 3 } };
760
+ const actions: Actions = .{ .p2 = .{ .move_slot = 3 } };
761
761
  try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p1, .None));
762
762
  try expectEqualSlices(u4, &.{ 1, 2, 3, 4 }, Rolls.moveSlot(actions.p2, .None));
763
763
  try expectEqualSlices(u4, &.{0}, Rolls.moveSlot(actions.p2, .false));
764
764
  }
765
765
 
766
766
  test "Rolls.multiHit" {
767
- const actions = Actions{ .p2 = .{ .multi_hit = 3 } };
767
+ const actions: Actions = .{ .p2 = .{ .multi_hit = 3 } };
768
768
  try expectEqualSlices(u4, &.{0}, Rolls.multiHit(actions.p1, .None));
769
769
  try expectEqualSlices(u4, &.{ 2, 3, 4, 5 }, Rolls.multiHit(actions.p2, .None));
770
770
  try expectEqualSlices(u4, &.{0}, Rolls.multiHit(actions.p2, .false));
771
771
  }
772
772
 
773
773
  test "Rolls.metronome" {
774
- const actions = Actions{ .p2 = .{ .metronome = .Surf } };
774
+ const actions: Actions = .{ .p2 = .{ .metronome = .Surf } };
775
775
  try expectEqualSlices(Move, &.{.None}, Rolls.metronome(actions.p1));
776
776
  try expectEqual(@as(Move, @enumFromInt(24)), Rolls.metronome(actions.p2)[23]);
777
777
  }
778
778
 
779
779
  test "Rolls.psywave" {
780
- const actions = Actions{ .p2 = .{ .psywave = 79 } };
780
+ const actions: Actions = .{ .p2 = .{ .psywave = 79 } };
781
781
  var side = Side.init(&[_]Pokemon{.{
782
782
  .species = .Bulbasaur,
783
783
  .level = 100,
@@ -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))];
@@ -1499,8 +1492,8 @@ fn checkFaint(
1499
1492
  }
1500
1493
 
1501
1494
  const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
1502
- if (player == .P1) return Result{ .p1 = .Switch, .p2 = foe_choice };
1503
- return Result{ .p1 = foe_choice, .p2 = .Switch };
1495
+ if (player == .P1) return .{ .p1 = .Switch, .p2 = foe_choice };
1496
+ return .{ .p1 = foe_choice, .p2 = .Switch };
1504
1497
  }
1505
1498
 
1506
1499
  fn faint(battle: anytype, player: Player, done: bool, options: anytype) !?Result {
@@ -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 {
@@ -2705,15 +2701,15 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
2705
2701
  }
2706
2702
  if (volatiles.Mist) {
2707
2703
  volatiles.Mist = false;
2708
- try log.end(ident, .Mist);
2704
+ try log.end(ident, .MistSilent);
2709
2705
  }
2710
2706
  if (volatiles.FocusEnergy) {
2711
2707
  volatiles.FocusEnergy = false;
2712
- try log.end(ident, .FocusEnergy);
2708
+ try log.end(ident, .FocusEnergySilent);
2713
2709
  }
2714
2710
  if (volatiles.LeechSeed) {
2715
2711
  volatiles.LeechSeed = false;
2716
- try log.end(ident, .LeechSeed);
2712
+ try log.end(ident, .LeechSeedSilent);
2717
2713
  }
2718
2714
  if (!showdown and volatiles.Toxic) {
2719
2715
  volatiles.Toxic = false;
@@ -2721,17 +2717,17 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
2721
2717
  }
2722
2718
  if (volatiles.LightScreen) {
2723
2719
  volatiles.LightScreen = false;
2724
- try log.end(ident, .LightScreen);
2720
+ try log.end(ident, .LightScreenSilent);
2725
2721
  }
2726
2722
  if (volatiles.Reflect) {
2727
2723
  volatiles.Reflect = false;
2728
- try log.end(ident, .Reflect);
2724
+ try log.end(ident, .ReflectSilent);
2729
2725
  }
2730
2726
  if (showdown and volatiles.Toxic) {
2731
2727
  volatiles.Toxic = false;
2732
2728
  // Pokémon Showdown erroneously clears the toxic counter
2733
2729
  volatiles.toxic = 0;
2734
- try log.end(ident, .Toxic);
2730
+ try log.end(ident, .ToxicSilent);
2735
2731
  }
2736
2732
  }
2737
2733
 
@@ -3039,11 +3035,11 @@ test "RNG agreement" {
3039
3035
  expected[i] = @intCast(i * 0x1000000);
3040
3036
  }
3041
3037
 
3042
- var spe = rng.FixedRNG(1, expected.len){ .rolls = expected };
3043
- var cfz = rng.FixedRNG(1, expected.len){ .rolls = expected };
3044
- var par = rng.FixedRNG(1, expected.len){ .rolls = expected };
3045
- var brn = rng.FixedRNG(1, expected.len){ .rolls = expected };
3046
- var eff = rng.FixedRNG(1, expected.len){ .rolls = expected };
3038
+ var spe: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
3039
+ var cfz: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
3040
+ var par: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
3041
+ var brn: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
3042
+ var eff: rng.FixedRNG(1, expected.len) = .{ .rolls = expected };
3047
3043
 
3048
3044
  for (0..expected.len) |i| {
3049
3045
  try expectEqual(spe.range(u8, 0, 2) == 0, i < Gen12.percent(50) + 1);
@@ -32,25 +32,27 @@ The following information is required to simulate a Generation II Pokémon battl
32
32
  | `pokemon.species` | `party_struct.Species` | `pokemon.baseSpecies` |
33
33
  | `active.species` | `battle_struct.Species` | `pokemon.species` |
34
34
  | `active.stats` | `TODO modified` | `pokemon.storedStats` |
35
- | `active.trapped FIXME` | `PlayerTrappingMove` | `pokemon.trapped` |
36
- | `active.switching` | `PlayerIsSwitching` | `pokemon.switchFlag` |
37
35
  | `active.boosts` | `PlayerStatLevels` | `pokemon.boosts` |
38
36
  | `active.volatiles` | `PlayerBattleStatus{1,2,3}` | `pokemon.volatiles` |
39
- | `volatiles.data.bide` | `PlayerDamageTaken` | `volatiles.bide.totalDamage` |
40
- | `volatiles.data.substitute` | `PlayerSubstituteHP` | `volatiles.substitute.hp` |
41
- | `volatiles.data.rollout` | `PlayerRolloutCount` | `volatiles.rollout.hitCount` |
42
- | `volatiles.data.confusion` | `PlayerConfuseCount` | `volatiles.confusion.duration` |
43
- | `volatiles.data.toxic` | `PlayerToxicCount` | `volatiles.residualdmg.counter` |
44
- | `volatiles.data.disabled` | `PlayerDisableCount` | `moveSlots.disabled` |
45
- | `volatiles.data.encore` | `PlayerEncoreCount` | `volatiles.encore.duration` |
46
- | `volatiles.data.perish_song` | `PlayerPerishCount` | `volatiles.perishsong.duration` |
47
- | `volatiles.data.fury_cutter` | `PlayerFuryCutterCount` | `volatiles.furycutter.multiplier` |
48
- | `volatiles.data.protect` | `PlayerProtectCount` | `volatiles.stall.counter` |
49
- | `volatiles.data.future_sight` | `PlayerFutureSight{Damage,Count}` | `slotConditions[i].futuremove` |
50
- | `volatiles.data.rage` | `PlayerRageCounter` | **BUG** |
51
- | `volatiles.data.bind` | `PlayerWrapCount` | `volatiles.partiallytrapped.duration` |
37
+ | `volatiles.bide` | `PlayerDamageTaken` | `volatiles.bide.totalDamage` |
38
+ | `volatiles.substitute` | `PlayerSubstituteHP` | `volatiles.substitute.hp` |
39
+ | `volatiles.rollout` | `PlayerRolloutCount` | `volatiles.rollout.hitCount` |
40
+ | `volatiles.confusion` | `PlayerConfuseCount` | `volatiles.confusion.duration` |
41
+ | `volatiles.toxic` | `PlayerToxicCount` | `volatiles.residualdmg.counter` |
42
+ | `volatiles.disabled` | `PlayerDisableCount` | `moveSlots.disabled` |
43
+ | `volatiles.encore` | `PlayerEncoreCount` | `volatiles.encore.duration` |
44
+ | `volatiles.perish_song` | `PlayerPerishCount` | `volatiles.perishsong.duration` |
45
+ | `volatiles.fury_cutter` | `PlayerFuryCutterCount` | `volatiles.furycutter.multiplier` |
46
+ | `volatiles.protect` | `PlayerProtectCount` | `volatiles.stall.counter` |
47
+ | `volatiles.future_sight` | `PlayerFutureSight{Damage,Count}` | `slotConditions[i].futuremove` |
48
+ | `volatiles.rage` | `PlayerRageCounter` | **BUG** |
49
+ | `volatiles.bind` | `PlayerWrapCount` | `volatiles.partiallytrapped.duration` |
50
+ | `volatiles.bind_reason` | `PlayerTrappingMove` | `volatiles.effectState.sourceEffect` |
52
51
  | | `PlayerCharging` | |
53
- | | `PlayerJustGotFrozen` | |
52
+ | `volatiles.trapped FIXME` | `FIXME` | `pokemon.trapped` |
53
+ | `volatiles.switched` | `TODO` | `pokemon.switchFlag` |
54
+ | `volatiles.switching` | `PlayerIsSwitching` | `pokemon.switchFlag` |
55
+ | `volatiles.frozen` | `PlayerJustGotFrozen` | `volatiles.frozen` |
54
56
  | | | |
55
57
  | `active.last_used_move` | `PlayerUsedMove` | `side.lastMove` |
56
58
 
@@ -219,6 +221,81 @@ much overhead the preceding representations have after taking into consideration
219
221
  padding](https://en.wikipedia.org/wiki/Data_structure_alignment) and
220
222
  [denormalization](https://en.wikipedia.org/wiki/Denormalization):
221
223
 
224
+ ## Details
225
+
226
+ | pkmn | Pokémon Crystal (pret) |
227
+ | ------------------------- | ------------------------------------------------- |
228
+ | `start` | `DoBattle` |
229
+ | `update` | `BattleTurn` |
230
+ | `selectMove` / `saveMove` | `CheckPlayerLockedIn` / `ParsePlayerAction` |
231
+ | `switchIn` | `SwitchPlayerMon` / `SendOutPlayerMon` |
232
+ | `turnOrder` | `DetermineMoveOrder` |
233
+ | `doTurn` | `DoTurn` |
234
+ | `executeMove` | `DoMove` |
235
+ | `beforeMove` | `CheckTurn` |
236
+ | `canMove` | |
237
+ | `decrementPP` | |
238
+ | `doMove` | |
239
+ | `checkCriticalHit` | `BattleCommand_Critical` |
240
+ | `calcDamage` | `BattleCommand_DamageCalc` / `PlayerAttackDamage` |
241
+ | `adjustDamage` | `BattleCommand_Stab` |
242
+ | `randomizeDamage` | `BattleCommand_DamageVariation` |
243
+ | `specialDamage` | `BattleCommand_ConstantDamage` |
244
+ | `applyDamage` | `BattleCommand_ApplyDamage` |
245
+ | `checkHit` | `BattleCommand_CheckHit` |
246
+ | `checkFaint` | `CheckFaint` |
247
+ | `handleResidual` | `ResidualDamage` |
248
+ | `endTurn` | `EndTurn` |
249
+
250
+ DoBattle
251
+ BattleTurn
252
+ - HandleBerserkGene
253
+ - CheckPlayerLockedIn
254
+ - BattleMenu
255
+ - ParsePlayerAction
256
+ - DetermineMoveOrder
257
+ - CompareMovePriority
258
+ - GetMovePriority
259
+ - Battle_*
260
+ - Handle*Switch
261
+ - SwitchPlayerMon
262
+ - BreakAttraction
263
+ - SendOutPlayerMon
264
+ - SpikesDamage
265
+ - *Turn_EndOpponentProtectEndureDestinyBond
266
+ - EndUserDestinyBond
267
+ - Do*Turn
268
+ - EndOpponentProtectEndureDestinyBond
269
+ - ResidualDamage
270
+ - CheckFaint_*
271
+ - HandleBetweenTurnEffects
272
+ - HandleFutureSight
273
+ - HandleWeather
274
+ - HandleWrap
275
+ - HandlePerishSong
276
+ - HandleLeftovers
277
+ - HandleMysteryBerry
278
+ - HandleDefrost
279
+ - HandleSafeguard
280
+ - HandleScreens
281
+ - HandleStatBoostingHeldItems
282
+ - HandleHealingItems
283
+ - HandleEncore
284
+
285
+ DoTurn
286
+ DoMove = executeMove
287
+ CheckTurn = beforeMove
288
+ BattleCommand_Critical = checkCriticalHit
289
+ BattleCommand_Stab = adjustDamage
290
+ BattleCommand_DamageVariation = randomizeDamage
291
+ BattleCommand_CheckHit = checkHit
292
+ BattleCommand_ApplyDamage = applyDamage
293
+ BattleCommand_CheckFaint
294
+ BattleCommand_BuildOpponentRage = buildRage
295
+ BattleCommand_DamageCalc (PlayerAttackDamage / EnemyAttackDamage) = calcDamage
296
+ BattleCommand_ConstantDamage = specialDamage
297
+ DoEnemyDamage / DoPlayerDamage / DoSubstituteDamage
298
+
222
299
  ==**TODO**==
223
300
 
224
301
  ## Protocol
@@ -226,62 +303,7 @@ padding](https://en.wikipedia.org/wiki/Data_structure_alignment) and
226
303
  TODO
227
304
 
228
305
  ```txt
229
- -activate: _, damage, move, name, number, of
230
- -anim: _
231
- -block: _
232
- -boost: _, from, silent
233
- -clearallboost: _
234
- -copyboost: from
235
- -crit: _
236
- -curestatus: msg
237
- -cureteam: from
238
- -damage: _, from, of, partiallytrapped
239
- -end: _, from, of, partiallytrapped, silent
240
- -enditem: _, eat
241
- -fail: _, block, weak
242
- -fieldactivate: _
243
- -heal: _, from, of, silent
244
- -hint: _
245
- -hitcount: _
246
- -immune: _, ohko
247
- -item: from, of
248
- -miss: _
249
- -mustrecharge: _
250
- -ohko: _
251
- -prepare: _
252
- -resisted: _
253
- -setboost: from
254
- -sethp: from, silent
255
- -sideend: _, from, of
256
- -sidestart: _
257
- -singlemove: _
258
- -singleturn: _
259
- -start: _, of, silent
260
- -status: _, from, silent
261
- -supereffective: _
262
- -transform: _
263
- -unboost: _
264
- -weather: _, upkeep
265
- cant: _
266
- debug: _
267
- done: _
268
- drag: _
269
- faint: _
270
- gametype: _
271
- gen: _
272
- move: _, from, miss, still
273
- player: _
274
- request: _
275
- rule: _
276
- start: _
277
- switch: _, from
278
- t:: _
279
- teamsize: _
280
- tie: _
281
- tier: _
282
- turn: _
283
- upkeep: _
284
- win: _
306
+ -activate: _, move, name, number
285
307
  ```
286
308
 
287
309
  ## Bugs
@@ -11,15 +11,15 @@ const Type = data.Type;
11
11
  /// Representation of a held item in Generation II Pokémon.
12
12
  pub const Item = enum(u8) {
13
13
  None,
14
+ SoftSand, // Ground
15
+ HardStone, // Rock
16
+ MetalCoat, // Steel
14
17
  PinkBow, // Normal
15
18
  BlackBelt, // Fighting
16
19
  SharpBeak, // Flying
17
20
  PoisonBarb, // Poison
18
- SoftSand, // Ground
19
- HardStone, // Rock
20
21
  SilverPowder, // Bug
21
22
  SpellTag, // Ghost
22
- MetalCoat, // Steel
23
23
  PolkadotBow, // ??? (Normal)
24
24
  Charcoal, // Fire
25
25
  MysticWater, // Water
@@ -36,21 +36,21 @@ pub const Item = enum(u8) {
36
36
  Stick,
37
37
  ThickClub,
38
38
  FocusBand, // FocusBand
39
- BerryJuice, // Berry
40
39
  ScopeLens, // CriticalUp
41
40
  Leftovers, // Leftovers
42
41
  BerserkGene,
43
42
  LightBall,
43
+ Berry, // Berry
44
+ BerryJuice, // Berry
45
+ GoldBerry, // Berry
46
+ MintBerry, // HealSleep
44
47
  PSNCureBerry, // HealPoison
45
48
  PRZCureBerry, // HealParalyze
46
- BurntBerry, // HealFreeze
47
49
  IceBerry, // HealBurn
48
- BitterBerry, // HealConfusion
49
- MintBerry, // HealSleep
50
+ BurntBerry, // HealFreeze
50
51
  MiracleBerry, // HealStatus
52
+ BitterBerry, // HealConfusion
51
53
  MysteryBerry, // RestorePP
52
- Berry, // Berry
53
- GoldBerry, // Berry
54
54
  MasterBall,
55
55
  UltraBall,
56
56
  GreatBall,
@@ -222,14 +222,8 @@ pub const Item = enum(u8) {
222
222
  return if (@intFromEnum(item) <= 18) @enumFromInt(@intFromEnum(item) - 1) else null;
223
223
  }
224
224
 
225
- /// Whether or not this item is a Berry.
226
- pub inline fn berry(item: Item) bool {
227
- assert(item != .None);
228
- return @intFromEnum(item) > 30 and @intFromEnum(item) <= 40;
229
- }
230
-
231
225
  /// Whether or not this item is Mail.
232
- pub inline fn mail(item: Item) bool {
226
+ pub inline fn isMail(item: Item) bool {
233
227
  assert(item != .None);
234
228
  return @intFromEnum(item) > 60 and @intFromEnum(item) <= 70;
235
229
  }