@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.
- package/build.zig +19 -13
- package/package.json +5 -2
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/protocol.zig +24 -7
- package/src/lib/common/rational.zig +4 -4
- package/src/lib/common/rng.zig +9 -0
- package/src/lib/gen1/calc.zig +2 -2
- package/src/lib/gen1/chance.zig +4 -4
- package/src/lib/gen1/data/moves.zig +10 -10
- package/src/lib/gen1/data/species.zig +2 -2
- package/src/lib/gen1/data/types.zig +5 -5
- package/src/lib/gen1/data.zig +14 -14
- package/src/lib/gen1/helpers.zig +13 -13
- package/src/lib/gen1/mechanics.zig +44 -50
- package/src/lib/gen2/calc.zig +143 -0
- package/src/lib/gen2/chance.zig +1314 -0
- package/src/lib/gen2/data/items.zig +3 -3
- package/src/lib/gen2/data/moves.zig +5 -5
- package/src/lib/gen2/data/species.zig +1 -1
- package/src/lib/gen2/data/types.zig +10 -10
- package/src/lib/gen2/data.zig +17 -27
- package/src/lib/gen2/generated.zig +814 -0
- package/src/lib/gen2/mechanics.zig +1221 -351
|
@@ -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
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|