@pkmn/engine 0.1.0-dev.dc33f678 → 0.1.0-dev.dfd46bbb

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.
@@ -1440,9 +1440,8 @@ fn checkFaint(
1440
1440
  const foe_fainted = foe.stored().hp == 0;
1441
1441
 
1442
1442
  const player_out = findFirstAlive(side) == 0;
1443
- const foe_out = findFirstAlive(foe) == 0;
1444
- const tie = player_out and foe_out;
1445
- const more = tie or player_out or foe_out;
1443
+ const foe_out = foe_fainted and findFirstAlive(foe) == 0;
1444
+ const more = player_out or foe_out;
1446
1445
 
1447
1446
  if (try faint(battle, player, !(more or foe_fainted), options)) |r| return r;
1448
1447
  if (foe_fainted) if (try faint(battle, player.foe(), !more, options)) |r| return r;
@@ -1450,7 +1449,7 @@ fn checkFaint(
1450
1449
  assert(!side.active.volatiles.MultiHit);
1451
1450
  assert(!foe.active.volatiles.MultiHit);
1452
1451
 
1453
- if (tie) {
1452
+ if (player_out and foe_out) {
1454
1453
  try options.log.tie();
1455
1454
  return Result.Tie;
1456
1455
  } else if (player_out) {
@@ -1653,7 +1652,7 @@ fn disabled(side: *Side, ident: ID, options: anytype) !bool {
1653
1652
  return false;
1654
1653
  }
1655
1654
 
1656
- inline fn buildRage(battle: anytype, who: Player, options: anytype) !void {
1655
+ fn buildRage(battle: anytype, who: Player, options: anytype) !void {
1657
1656
  const side = battle.side(who);
1658
1657
  if (side.active.volatiles.Rage and side.active.boosts.atk < 6) {
1659
1658
  try Effects.boost(battle, who, Move.get(.Rage), options);
@@ -1671,7 +1670,7 @@ fn handleThrashing(battle: anytype, active: *ActivePokemon, player: Player, opti
1671
1670
  return true;
1672
1671
  }
1673
1672
 
1674
- inline fn onBegin(
1673
+ fn onBegin(
1675
1674
  battle: anytype,
1676
1675
  player: Player,
1677
1676
  move: Move.Data,
@@ -1701,7 +1700,7 @@ inline fn onBegin(
1701
1700
  };
1702
1701
  }
1703
1702
 
1704
- inline fn onEnd(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
1703
+ fn onEnd(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
1705
1704
  assert(move.effect.onEnd());
1706
1705
  return switch (move.effect) {
1707
1706
  .Bide => Effects.bide(battle, player, options),
@@ -1717,7 +1716,7 @@ inline fn onEnd(battle: anytype, player: Player, move: Move.Data, options: anyty
1717
1716
  };
1718
1717
  }
1719
1718
 
1720
- inline fn alwaysHappens(
1719
+ fn alwaysHappens(
1721
1720
  battle: anytype,
1722
1721
  player: Player,
1723
1722
  move: Move.Data,
@@ -1736,7 +1735,7 @@ inline fn alwaysHappens(
1736
1735
  };
1737
1736
  }
1738
1737
 
1739
- inline fn moveEffect(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
1738
+ fn moveEffect(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
1740
1739
  return switch (move.effect) {
1741
1740
  .BurnChance1, .BurnChance2 => Effects.burnChance(battle, player, move, options),
1742
1741
  .ConfusionChance => Effects.confusion(battle, player, move, options),
@@ -1901,8 +1900,7 @@ pub const Effects = struct {
1901
1900
  }
1902
1901
 
1903
1902
  fn drainHP(battle: anytype, player: Player, options: anytype) !void {
1904
- var side = battle.side(player);
1905
- var stored = side.stored();
1903
+ var stored = battle.side(player).stored();
1906
1904
 
1907
1905
  if (battle.last_damage == 0) {
1908
1906
  assert(showdown);
@@ -2027,7 +2025,7 @@ pub const Effects = struct {
2027
2025
  // GLITCH: HP recovery move failure glitches
2028
2026
  const delta = stored.stats.hp - stored.hp;
2029
2027
  if (delta == 0 or (delta & 255 == 255 and stored.hp % 256 != 0)) {
2030
- return try options.log.fail(ident, .None);
2028
+ return options.log.fail(ident, .None);
2031
2029
  }
2032
2030
 
2033
2031
  const rest = side.last_selected_move == .Rest;
@@ -2109,7 +2107,7 @@ pub const Effects = struct {
2109
2107
  // Invulnerable foes or 1/256 miss can trigger |-miss| instead of |-fail|
2110
2108
  if (!try checkHit(battle, player, move, options)) return;
2111
2109
  try options.chance.commit(player, .hit);
2112
- return try options.log.fail(battle.active(player.foe()), .None);
2110
+ return options.log.fail(battle.active(player.foe()), .None);
2113
2111
  }
2114
2112
  }
2115
2113
  if (!try checkHit(battle, player, move, options)) return;
@@ -2327,8 +2325,7 @@ pub const Effects = struct {
2327
2325
  fn substitute(battle: anytype, player: Player, residual: *bool, options: anytype) !void {
2328
2326
  var side = battle.side(player);
2329
2327
  if (side.active.volatiles.Substitute) {
2330
- try options.log.fail(battle.active(player), .Substitute);
2331
- return;
2328
+ return options.log.fail(battle.active(player), .Substitute);
2332
2329
  }
2333
2330
 
2334
2331
  assert(side.stored().stats.hp <= 1023);
@@ -2339,10 +2336,7 @@ pub const Effects = struct {
2339
2336
  // the target's HP is exactly divisible by 4 (here we're using an inlined divCeil routine to
2340
2337
  // avoid having to convert to floating point)
2341
2338
  const required_hp = if (showdown) @divFloor(side.stored().stats.hp - 1, 4) + 1 else hp;
2342
- if (side.stored().hp < required_hp) {
2343
- try options.log.fail(battle.active(player), .Weak);
2344
- return;
2345
- }
2339
+ if (side.stored().hp < required_hp) return options.log.fail(battle.active(player), .Weak);
2346
2340
 
2347
2341
  // GLITCH: can leave the user with 0 HP (faints later) because didn't check '<=' above
2348
2342
  side.stored().hp -= hp;
@@ -2434,7 +2428,7 @@ pub const Effects = struct {
2434
2428
  switch (move.effect) {
2435
2429
  .AttackUp1, .AttackUp2, .Rage => {
2436
2430
  assert(boosts.atk >= -6 and boosts.atk <= 6);
2437
- if (boosts.atk == 6) return try log.fail(ident, .None);
2431
+ if (boosts.atk == 6) return log.fail(ident, .None);
2438
2432
  const n: u2 = if (move.effect == .AttackUp2) 2 else 1;
2439
2433
  boosts.atk = @as(i4, @intCast(@min(6, @as(i8, boosts.atk) + n)));
2440
2434
  const reason = if (move.effect == .Rage) Boost.Rage else Boost.Attack;
@@ -2445,7 +2439,7 @@ pub const Effects = struct {
2445
2439
  try log.boost(ident, Boost.Attack, -1);
2446
2440
  if (move.effect == .Rage) return;
2447
2441
  }
2448
- return try log.fail(ident, .None);
2442
+ return log.fail(ident, .None);
2449
2443
  }
2450
2444
  var mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.atk) + 6))];
2451
2445
  const stat = unmodifiedStats(battle, side).atk;
@@ -2456,7 +2450,7 @@ pub const Effects = struct {
2456
2450
  },
2457
2451
  .DefenseUp1, .DefenseUp2 => {
2458
2452
  assert(boosts.def >= -6 and boosts.def <= 6);
2459
- if (boosts.def == 6) return try log.fail(ident, .None);
2453
+ if (boosts.def == 6) return log.fail(ident, .None);
2460
2454
  const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
2461
2455
  boosts.def = @intCast(@min(6, @as(i8, boosts.def) + n));
2462
2456
  if (stats.def == MAX_STAT_VALUE) {
@@ -2465,7 +2459,7 @@ pub const Effects = struct {
2465
2459
  try log.boost(ident, .Defense, n);
2466
2460
  try log.boost(ident, .Defense, -1);
2467
2461
  }
2468
- return try log.fail(ident, .None);
2462
+ return log.fail(ident, .None);
2469
2463
  }
2470
2464
  var mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.def) + 6))];
2471
2465
  const stat = unmodifiedStats(battle, side).def;
@@ -2474,7 +2468,7 @@ pub const Effects = struct {
2474
2468
  },
2475
2469
  .SpeedUp2 => {
2476
2470
  assert(boosts.spe >= -6 and boosts.spe <= 6);
2477
- if (boosts.spe == 6) return try log.fail(ident, .None);
2471
+ if (boosts.spe == 6) return log.fail(ident, .None);
2478
2472
  boosts.spe = @intCast(@min(6, @as(i8, boosts.spe) + 2));
2479
2473
  if (stats.spe == MAX_STAT_VALUE) {
2480
2474
  boosts.spe -= 1;
@@ -2482,7 +2476,7 @@ pub const Effects = struct {
2482
2476
  try log.boost(ident, .Speed, 2);
2483
2477
  try log.boost(ident, .Speed, -1);
2484
2478
  }
2485
- return try log.fail(ident, .None);
2479
+ return log.fail(ident, .None);
2486
2480
  }
2487
2481
  var mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spe) + 6))];
2488
2482
  const stat = unmodifiedStats(battle, side).spe;
@@ -2491,7 +2485,7 @@ pub const Effects = struct {
2491
2485
  },
2492
2486
  .SpecialUp1, .SpecialUp2 => {
2493
2487
  assert(boosts.spc >= -6 and boosts.spc <= 6);
2494
- if (boosts.spc == 6) return try log.fail(ident, .None);
2488
+ if (boosts.spc == 6) return log.fail(ident, .None);
2495
2489
  const n: u2 = if (move.effect == .SpecialUp2) 2 else 1;
2496
2490
  boosts.spc = @intCast(@min(6, @as(i8, boosts.spc) + n));
2497
2491
  if (stats.spc == MAX_STAT_VALUE) {
@@ -2502,7 +2496,7 @@ pub const Effects = struct {
2502
2496
  try log.boost(ident, .SpecialDefense, n);
2503
2497
  try log.boost(ident, .SpecialDefense, -1);
2504
2498
  }
2505
- return try log.fail(ident, .None);
2499
+ return log.fail(ident, .None);
2506
2500
  }
2507
2501
  var mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spc) + 6))];
2508
2502
  const stat = unmodifiedStats(battle, side).spc;
@@ -2512,7 +2506,7 @@ pub const Effects = struct {
2512
2506
  },
2513
2507
  .EvasionUp1 => {
2514
2508
  assert(boosts.evasion >= -6 and boosts.evasion <= 6);
2515
- if (boosts.evasion == 6) return try log.fail(ident, .None);
2509
+ if (boosts.evasion == 6) return log.fail(ident, .None);
2516
2510
  boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
2517
2511
  try log.boost(ident, .Evasion, 1);
2518
2512
  },
@@ -2555,7 +2549,7 @@ pub const Effects = struct {
2555
2549
  try log.boost(foe_ident, .Attack, -1);
2556
2550
  try log.boost(foe_ident, .Attack, 1);
2557
2551
  }
2558
- return try log.fail(foe_ident, .None);
2552
+ return log.fail(foe_ident, .None);
2559
2553
  }
2560
2554
  var mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.atk) + 6))];
2561
2555
  const stat = unmodifiedStats(battle, foe).atk;
@@ -2573,7 +2567,7 @@ pub const Effects = struct {
2573
2567
  try log.boost(foe_ident, .Defense, -@as(i8, n));
2574
2568
  try log.boost(foe_ident, .Defense, 1);
2575
2569
  }
2576
- return try log.fail(foe_ident, .None);
2570
+ return log.fail(foe_ident, .None);
2577
2571
  }
2578
2572
  var mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.def) + 6))];
2579
2573
  const stat = unmodifiedStats(battle, foe).def;
@@ -2590,7 +2584,7 @@ pub const Effects = struct {
2590
2584
  try log.boost(foe_ident, .Speed, -1);
2591
2585
  try log.boost(foe_ident, .Speed, 1);
2592
2586
  }
2593
- return try log.fail(foe_ident, .None);
2587
+ return log.fail(foe_ident, .None);
2594
2588
  }
2595
2589
  var mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spe) + 6))];
2596
2590
  const stat = unmodifiedStats(battle, foe).spe;
@@ -2610,7 +2604,7 @@ pub const Effects = struct {
2610
2604
  try log.boost(foe_ident, .SpecialDefense, -1);
2611
2605
  try log.boost(foe_ident, .SpecialDefense, 1);
2612
2606
  }
2613
- return try log.fail(foe_ident, .None);
2607
+ return log.fail(foe_ident, .None);
2614
2608
  }
2615
2609
  var mod = BOOSTS[@as(u4, @intCast(@as(i8, boosts.spc) + 6))];
2616
2610
  const stat = unmodifiedStats(battle, foe).spc;
@@ -2646,7 +2640,7 @@ fn statusModify(status: u8, stats: *Stats(u16)) void {
2646
2640
  }
2647
2641
  }
2648
2642
 
2649
- inline fn isForced(active: anytype) bool {
2643
+ fn isForced(active: anytype) bool {
2650
2644
  return active.volatiles.Recharging or active.volatiles.Rage or
2651
2645
  active.volatiles.Thrashing or active.volatiles.Charging;
2652
2646
  }
@@ -2702,7 +2696,7 @@ fn clearVolatiles(battle: anytype, who: Player, clear: bool, options: anytype) !
2702
2696
  }
2703
2697
 
2704
2698
  pub const Rolls = struct {
2705
- inline fn speedTie(battle: anytype, options: anytype) !bool {
2699
+ fn speedTie(battle: anytype, options: anytype) !bool {
2706
2700
  const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
2707
2701
  player == .P1
2708
2702
  else if (showdown)
@@ -2714,7 +2708,7 @@ pub const Rolls = struct {
2714
2708
  return p1;
2715
2709
  }
2716
2710
 
2717
- inline fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2711
+ fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
2718
2712
  const crit = if (options.calc.overridden(player, .critical_hit)) |val|
2719
2713
  val == .true
2720
2714
  else if (showdown)
@@ -2726,7 +2720,7 @@ pub const Rolls = struct {
2726
2720
  return crit;
2727
2721
  }
2728
2722
 
2729
- inline fn damage(battle: anytype, player: anytype, options: anytype) !u8 {
2723
+ fn damage(battle: anytype, player: Player, options: anytype) !u8 {
2730
2724
  const roll = if (options.calc.overridden(player, .damage)) |val|
2731
2725
  val
2732
2726
  else roll: {
@@ -2742,7 +2736,7 @@ pub const Rolls = struct {
2742
2736
  return roll;
2743
2737
  }
2744
2738
 
2745
- inline fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) bool {
2739
+ fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) bool {
2746
2740
  const ok = if (options.calc.overridden(player, .hit)) |val|
2747
2741
  val == .true
2748
2742
  else if (showdown)
@@ -2754,7 +2748,7 @@ pub const Rolls = struct {
2754
2748
  return ok;
2755
2749
  }
2756
2750
 
2757
- inline fn confused(battle: anytype, player: Player, options: anytype) !bool {
2751
+ fn confused(battle: anytype, player: Player, options: anytype) !bool {
2758
2752
  const cfz = if (options.calc.overridden(player, .confused)) |val|
2759
2753
  val == .true
2760
2754
  else if (showdown)
@@ -2766,7 +2760,7 @@ pub const Rolls = struct {
2766
2760
  return cfz;
2767
2761
  }
2768
2762
 
2769
- inline fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2763
+ fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
2770
2764
  const par = if (options.calc.overridden(player, .paralyzed)) |val|
2771
2765
  val == .true
2772
2766
  else if (showdown)
@@ -2778,7 +2772,7 @@ pub const Rolls = struct {
2778
2772
  return par;
2779
2773
  }
2780
2774
 
2781
- inline fn confusionChance(battle: anytype, player: Player, options: anytype) !bool {
2775
+ fn confusionChance(battle: anytype, player: Player, options: anytype) !bool {
2782
2776
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2783
2777
  val == .true
2784
2778
  else if (showdown)
@@ -2790,7 +2784,7 @@ pub const Rolls = struct {
2790
2784
  return proc;
2791
2785
  }
2792
2786
 
2793
- inline fn secondaryChance(battle: anytype, player: Player, low: bool, options: anytype) !bool {
2787
+ fn secondaryChance(battle: anytype, player: Player, low: bool, options: anytype) !bool {
2794
2788
  const rate: u8 = if (low) 26 else 77;
2795
2789
 
2796
2790
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
@@ -2804,7 +2798,7 @@ pub const Rolls = struct {
2804
2798
  return proc;
2805
2799
  }
2806
2800
 
2807
- inline fn poisonChance(battle: anytype, player: Player, low: bool, options: anytype) !bool {
2801
+ fn poisonChance(battle: anytype, player: Player, low: bool, options: anytype) !bool {
2808
2802
  const rate: u8 = if (low) 52 else 103;
2809
2803
 
2810
2804
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
@@ -2818,7 +2812,7 @@ pub const Rolls = struct {
2818
2812
  return proc;
2819
2813
  }
2820
2814
 
2821
- inline fn unboost(battle: anytype, player: Player, options: anytype) !bool {
2815
+ fn unboost(battle: anytype, player: Player, options: anytype) !bool {
2822
2816
  const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
2823
2817
  val == .true
2824
2818
  else if (showdown)
@@ -2830,7 +2824,7 @@ pub const Rolls = struct {
2830
2824
  return proc;
2831
2825
  }
2832
2826
 
2833
- inline fn metronome(battle: anytype, player: Player, options: anytype) !Move {
2827
+ fn metronome(battle: anytype, player: Player, options: anytype) !Move {
2834
2828
  const move: Move = if (options.calc.overridden(player, .metronome)) |val|
2835
2829
  val
2836
2830
  else if (showdown)
@@ -2848,7 +2842,7 @@ pub const Rolls = struct {
2848
2842
  return move;
2849
2843
  }
2850
2844
 
2851
- inline fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
2845
+ fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
2852
2846
  const power = if (options.calc.overridden(player, .psywave)) |val|
2853
2847
  val - 1
2854
2848
  else if (showdown)
@@ -2865,7 +2859,7 @@ pub const Rolls = struct {
2865
2859
  return power;
2866
2860
  }
2867
2861
 
2868
- inline fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
2862
+ fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
2869
2863
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2870
2864
  @intCast(val)
2871
2865
  else if (showdown)
@@ -2882,7 +2876,7 @@ pub const Rolls = struct {
2882
2876
  return duration;
2883
2877
  }
2884
2878
 
2885
- inline fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
2879
+ fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
2886
2880
  const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
2887
2881
  val
2888
2882
  else if (showdown)
@@ -2895,7 +2889,7 @@ pub const Rolls = struct {
2895
2889
  return duration;
2896
2890
  }
2897
2891
 
2898
- inline fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
2892
+ fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
2899
2893
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2900
2894
  @intCast(val)
2901
2895
  else if (showdown)
@@ -2908,7 +2902,7 @@ pub const Rolls = struct {
2908
2902
  return duration;
2909
2903
  }
2910
2904
 
2911
- inline fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
2905
+ fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
2912
2906
  const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
2913
2907
  @intCast(val)
2914
2908
  else if (showdown)
@@ -2923,7 +2917,7 @@ pub const Rolls = struct {
2923
2917
 
2924
2918
  const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
2925
2919
 
2926
- inline fn distribution(
2920
+ fn distribution(
2927
2921
  battle: anytype,
2928
2922
  comptime effect: Move.Effect,
2929
2923
  player: Player,
@@ -0,0 +1,143 @@
1
+ const std = @import("std");
2
+
3
+ const pkmn = @import("../pkmn.zig");
4
+
5
+ const common = @import("../common/data.zig");
6
+ const util = @import("../common/util.zig");
7
+
8
+ const chance = @import("chance.zig");
9
+
10
+ const assert = std.debug.assert;
11
+
12
+ const enabled = pkmn.options.calc;
13
+
14
+ const Player = common.Player;
15
+
16
+ const PointerType = util.PointerType;
17
+
18
+ const Actions = chance.Actions;
19
+ const Action = chance.Action;
20
+
21
+ /// Information relevant to damage calculation that occured during a Generation II battle `update`.
22
+ pub const Summaries = extern struct {
23
+ /// Relevant information for Player 1.
24
+ p1: Summary = .{},
25
+ /// Relevant information for Player 2.
26
+ p2: Summary = .{},
27
+
28
+ comptime {
29
+ assert(@sizeOf(Summaries) == 12);
30
+ }
31
+
32
+ /// Returns the `Summary` for the given `player`.
33
+ pub fn get(self: anytype, player: Player) PointerType(@TypeOf(self), Summary) {
34
+ assert(@typeInfo(@TypeOf(self)).Pointer.child == Summaries);
35
+ return if (player == .P1) &self.p1 else &self.p2;
36
+ }
37
+ };
38
+
39
+ /// Information relevant to damage calculation that occured during a Generation II battle `update`
40
+ /// for a single player.
41
+ pub const Summary = extern struct {
42
+ /// The computed raw damage values.
43
+ damage: Damage = .{},
44
+
45
+ /// Intermediate raw damage values computed during a calculation.
46
+ pub const Damage = extern struct {
47
+ /// The base computed damage before the damage roll is applied.
48
+ base: u16 = 0,
49
+ /// The final computed damage that gets applied to the Pokémon. May exceed the target's HP
50
+ // (to determine the *actual* damage done compare the target's stored HP before and after).
51
+ final: u16 = 0,
52
+ /// Whether higher damage will saturate / result in the same outcome (e.g. additional damage
53
+ /// gets ignored due to it already breaking a Substitute or causing the target to faint).
54
+ capped: bool = false,
55
+
56
+ // NOTE: 15 bits padding
57
+
58
+ comptime {
59
+ assert(@sizeOf(Damage) == 6);
60
+ }
61
+ };
62
+
63
+ comptime {
64
+ assert(@sizeOf(Summary) == 6);
65
+ }
66
+ };
67
+
68
+ /// TODO
69
+ pub const Overrides = extern struct {
70
+ /// TODO
71
+ actions: Actions = .{},
72
+ };
73
+
74
+ /// Allows for forcing the value of specific RNG events during a Generation II battle `update` via
75
+ /// `overrides` and tracks `summaries` of information relevant to damage calculation.
76
+ pub const Calc = struct {
77
+ /// Overrides the normal behavior of the RNG during an `update` to force specific outcomes.
78
+ overrides: Overrides = .{},
79
+ /// Information relevant to damage calculation.
80
+ summaries: Summaries = .{},
81
+
82
+ pub fn overridden(
83
+ self: Calc,
84
+ player: Player,
85
+ comptime field: Action.Field,
86
+ ) ?std.meta.FieldType(Action, field) {
87
+ if (!enabled) return null;
88
+
89
+ const overrides =
90
+ if (player == .P1) self.overrides.actions.p1 else self.overrides.actions.p2;
91
+ const val = @field(overrides, @tagName(field));
92
+ return if (switch (@typeInfo(@TypeOf(val))) {
93
+ .Enum => val != .None,
94
+ .Int => val != 0,
95
+ else => unreachable,
96
+ }) val else null;
97
+ }
98
+
99
+ pub fn base(self: *Calc, player: Player, val: u16) void {
100
+ if (!enabled) return;
101
+
102
+ self.summaries.get(player).damage.base = val;
103
+ }
104
+
105
+ pub fn final(self: *Calc, player: Player, val: u16) void {
106
+ if (!enabled) return;
107
+
108
+ self.summaries.get(player).damage.final = val;
109
+ }
110
+
111
+ pub fn capped(self: *Calc, player: Player) void {
112
+ if (!enabled) return;
113
+
114
+ self.summaries.get(player).damage.capped = true;
115
+ }
116
+ };
117
+
118
+ /// Null object pattern implementation of Generation II `Calc` which does nothing, though damage
119
+ /// calculator support should additionally be turned off entirely via `options.calc`.
120
+ pub const NULL: Null = .{};
121
+
122
+ const Null = struct {
123
+ pub fn overridden(
124
+ self: Null,
125
+ player: Player,
126
+ comptime field: Action.Field,
127
+ ) ?std.meta.FieldType(Action, field) {
128
+ _ = .{ self, player };
129
+ return null;
130
+ }
131
+
132
+ pub fn base(self: Null, player: Player, val: u16) void {
133
+ _ = .{ self, player, val };
134
+ }
135
+
136
+ pub fn final(self: Null, player: Player, val: u16) void {
137
+ _ = .{ self, player, val };
138
+ }
139
+
140
+ pub fn capped(self: Null, player: Player) void {
141
+ _ = .{ self, player };
142
+ }
143
+ };