@pkmn/engine 0.1.0-dev.eaae4aa6 → 0.1.0-dev.f34af416
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/LICENSE +0 -2
- package/README.md +15 -11
- package/build/pkg/addon.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +2 -3
- package/build/pkg/index.d.ts +1 -1
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +4 -4
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +8 -3
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +2 -3
- package/build/tools/debug.js.map +1 -1
- package/build.zig +189 -72
- package/package.json +17 -16
- package/src/bin/install-pkmn-engine +17 -7
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +51 -44
- package/src/lib/common/rational.zig +114 -24
- package/src/lib/common/rng.zig +10 -10
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +508 -128
- package/src/lib/gen1/chance.zig +383 -276
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +29 -28
- package/src/lib/gen1/helpers.zig +96 -536
- package/src/lib/gen1/mechanics.zig +266 -160
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +91 -500
- package/src/lib/gen2/data.zig +15 -14
- package/src/lib/gen2/helpers.zig +41 -9
- package/src/lib/gen2/mechanics.zig +158 -97
- package/src/lib/pkmn.zig +10 -16
- package/src/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
|
@@ -106,6 +106,7 @@ pub const State = struct {
|
|
|
106
106
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
107
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
108
|
move: Move, // wCurPlayerMove
|
|
109
|
+
hit: u3 = 0,
|
|
109
110
|
from: Move = .None,
|
|
110
111
|
mslot: u4, // wCurMoveNum
|
|
111
112
|
first: bool, // wEnemyGoesFirst
|
|
@@ -256,7 +257,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
257
|
} else {
|
|
257
258
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
259
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
260
|
|
|
261
261
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
262
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -442,18 +442,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
442
442
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
443
443
|
const before = stored.status;
|
|
444
444
|
// Even if the EXT bit is set this will still correctly modify the sleep duration
|
|
445
|
-
// if (options.calc.modify(player, .sleep)) |extend| {
|
|
446
|
-
// if (!extend) stored.status = 0;
|
|
447
|
-
// } else {
|
|
448
445
|
stored.status -= 1;
|
|
449
|
-
// }
|
|
450
|
-
|
|
451
|
-
const duration = Status.duration(stored.status);
|
|
452
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
453
|
-
try options.chance.sleep(player, duration);
|
|
454
|
-
}
|
|
455
446
|
|
|
456
|
-
if (duration == 0) {
|
|
447
|
+
if (Status.duration(stored.status) == 0) {
|
|
457
448
|
try log.curestatus(.{ ident, before, .Message });
|
|
458
449
|
stored.status = 0; // clears EXT if present
|
|
459
450
|
resetCant(volatiles);
|
|
@@ -483,12 +474,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
483
474
|
}
|
|
484
475
|
|
|
485
476
|
if (volatiles.disable.duration > 0) {
|
|
486
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
487
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
488
|
-
// } else {
|
|
489
477
|
volatiles.disable.duration -= 1;
|
|
490
|
-
// }
|
|
491
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
492
478
|
|
|
493
479
|
if (volatiles.disable.duration == 0) {
|
|
494
480
|
volatiles.disable.move = 0;
|
|
@@ -498,13 +484,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
498
484
|
|
|
499
485
|
if (volatiles.Confusion) {
|
|
500
486
|
assert(volatiles.confusion > 0);
|
|
501
|
-
|
|
502
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
503
|
-
// if (!extend) volatiles.confusion = 0;
|
|
504
|
-
// } else {
|
|
505
487
|
volatiles.confusion -= 1;
|
|
506
|
-
// }
|
|
507
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
508
488
|
|
|
509
489
|
if (volatiles.confusion == 0) {
|
|
510
490
|
volatiles.Confusion = false;
|
|
@@ -665,7 +645,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
665
645
|
}
|
|
666
646
|
assert(stage <= 4);
|
|
667
647
|
|
|
668
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
648
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
669
649
|
}
|
|
670
650
|
|
|
671
651
|
pub fn calcDamage(
|
|
@@ -733,7 +713,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
733
713
|
|
|
734
714
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
735
715
|
if (state.damage <= 1) return;
|
|
736
|
-
const random = try Rolls.damage(battle, player, options);
|
|
716
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
737
717
|
options.calc.base(player.foe(), state.damage);
|
|
738
718
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
739
719
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -1304,14 +1284,32 @@ pub const Effects = struct {
|
|
|
1304
1284
|
}
|
|
1305
1285
|
|
|
1306
1286
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1287
|
+
assert(!state.miss);
|
|
1288
|
+
var side = battle.side(player);
|
|
1289
|
+
|
|
1290
|
+
const ident = battle.active(player);
|
|
1291
|
+
|
|
1292
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1293
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1294
|
+
return options.log.fail(.{ ident, .None });
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1298
|
+
if (side.stored().hp <= hp) {
|
|
1299
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1300
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1301
|
+
return options.log.fail(.{ ident, .None });
|
|
1302
|
+
} else {
|
|
1303
|
+
side.stored().hp -= hp;
|
|
1304
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1305
|
+
|
|
1306
|
+
const before = side.active.boosts.atk;
|
|
1307
|
+
var i: usize = 0;
|
|
1308
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1309
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1310
|
+
}
|
|
1311
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1312
|
+
}
|
|
1315
1313
|
}
|
|
1316
1314
|
|
|
1317
1315
|
pub const bide = struct {
|
|
@@ -1323,13 +1321,7 @@ pub const Effects = struct {
|
|
|
1323
1321
|
|
|
1324
1322
|
if (!volatiles.Bide) return;
|
|
1325
1323
|
|
|
1326
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1327
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1328
|
-
// } else {
|
|
1329
1324
|
volatiles.attacks -= 1;
|
|
1330
|
-
// }
|
|
1331
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1332
|
-
|
|
1333
1325
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1334
1326
|
|
|
1335
1327
|
volatiles.Bide = false;
|
|
@@ -1596,8 +1588,16 @@ pub const Effects = struct {
|
|
|
1596
1588
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1597
1589
|
}
|
|
1598
1590
|
|
|
1599
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1600
|
-
|
|
1591
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1592
|
+
var side = battle.side(player);
|
|
1593
|
+
var stored = side.stored();
|
|
1594
|
+
|
|
1595
|
+
stored.hp = 0;
|
|
1596
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1597
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1598
|
+
if (!showdown) stored.status = 0;
|
|
1599
|
+
side.active.volatiles.LeechSeed = false;
|
|
1600
|
+
side.active.volatiles.DestinyBond = false;
|
|
1601
1601
|
}
|
|
1602
1602
|
|
|
1603
1603
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1723,8 +1723,26 @@ pub const Effects = struct {
|
|
|
1723
1723
|
foe.active.volatiles.frozen = true;
|
|
1724
1724
|
}
|
|
1725
1725
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1726
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1727
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1728
|
+
var side = battle.side(player);
|
|
1729
|
+
var volatiles = &side.active.volatiles;
|
|
1730
|
+
|
|
1731
|
+
if (state.miss) {
|
|
1732
|
+
volatiles.fury_cutter = 0;
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1737
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1738
|
+
volatiles.fury_cutter += 1;
|
|
1739
|
+
|
|
1740
|
+
if (showdown) {
|
|
1741
|
+
state.bp *= 2;
|
|
1742
|
+
assert(state.bp <= 160);
|
|
1743
|
+
} else {
|
|
1744
|
+
state.damage *|= 2;
|
|
1745
|
+
}
|
|
1728
1746
|
}
|
|
1729
1747
|
|
|
1730
1748
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1885,7 +1903,29 @@ pub const Effects = struct {
|
|
|
1885
1903
|
}
|
|
1886
1904
|
|
|
1887
1905
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1888
|
-
|
|
1906
|
+
var side = battle.side(player);
|
|
1907
|
+
|
|
1908
|
+
if (state.miss) {
|
|
1909
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1910
|
+
try options.log.lastmiss(.{});
|
|
1911
|
+
return options.log.miss(.{battle.active(player)});
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
const move = battle.foe(player).lastMove(false);
|
|
1915
|
+
if (move == .None or move == .Struggle) {
|
|
1916
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
var slot: u4 = 0;
|
|
1920
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1921
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1922
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1923
|
+
}
|
|
1924
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1925
|
+
|
|
1926
|
+
side.active.move(slot).id = move;
|
|
1927
|
+
side.active.move(slot).pp = 5;
|
|
1928
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1889
1929
|
}
|
|
1890
1930
|
|
|
1891
1931
|
pub fn mirrorMove(
|
|
@@ -2393,17 +2433,37 @@ pub const Effects = struct {
|
|
|
2393
2433
|
}
|
|
2394
2434
|
|
|
2395
2435
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2396
|
-
|
|
2397
|
-
// var foe = battle.foe(player);
|
|
2436
|
+
var foe = battle.foe(player);
|
|
2398
2437
|
|
|
2399
|
-
|
|
2438
|
+
const last = foe.lastMove(false);
|
|
2439
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2440
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2441
|
+
}
|
|
2400
2442
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
//
|
|
2406
|
-
|
|
2443
|
+
var slot: u4 = 0;
|
|
2444
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2445
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2446
|
+
}
|
|
2447
|
+
// If the move was called via Metronome it might not exist
|
|
2448
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2449
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2453
|
+
const pp = @min(
|
|
2454
|
+
foe.active.move(slot).pp,
|
|
2455
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2456
|
+
);
|
|
2457
|
+
foe.active.move(slot).pp -= pp;
|
|
2458
|
+
if (!foe.active.volatiles.Transform) {
|
|
2459
|
+
var move_slot = foe.stored().move(slot);
|
|
2460
|
+
assert(move_slot.pp > 0);
|
|
2461
|
+
|
|
2462
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2463
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2407
2467
|
}
|
|
2408
2468
|
|
|
2409
2469
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2790,7 +2850,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2790
2850
|
|
|
2791
2851
|
pub const Rolls = struct {
|
|
2792
2852
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2793
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2853
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2794
2854
|
player == .P1
|
|
2795
2855
|
else if (showdown)
|
|
2796
2856
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2802,7 +2862,7 @@ pub const Rolls = struct {
|
|
|
2802
2862
|
}
|
|
2803
2863
|
|
|
2804
2864
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2805
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2865
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2806
2866
|
val == .true
|
|
2807
2867
|
else if (showdown)
|
|
2808
2868
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2814,7 +2874,7 @@ pub const Rolls = struct {
|
|
|
2814
2874
|
}
|
|
2815
2875
|
|
|
2816
2876
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2817
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2877
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2818
2878
|
val == .true
|
|
2819
2879
|
else if (showdown)
|
|
2820
2880
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2825,20 +2885,22 @@ pub const Rolls = struct {
|
|
|
2825
2885
|
return ok;
|
|
2826
2886
|
}
|
|
2827
2887
|
|
|
2828
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2829
|
-
|
|
2888
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2889
|
+
_ = .{ player, options };
|
|
2890
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2830
2891
|
val == .true
|
|
2831
2892
|
else if (showdown)
|
|
2832
2893
|
battle.rng.chance(u8, rate, 256)
|
|
2833
2894
|
else
|
|
2834
2895
|
battle.rng.next() < rate;
|
|
2835
2896
|
|
|
2836
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2897
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2837
2898
|
return crit;
|
|
2838
2899
|
}
|
|
2839
2900
|
|
|
2840
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2841
|
-
|
|
2901
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2902
|
+
_ = .{ player, options };
|
|
2903
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2842
2904
|
val
|
|
2843
2905
|
else roll: {
|
|
2844
2906
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2849,12 +2911,12 @@ pub const Rolls = struct {
|
|
|
2849
2911
|
};
|
|
2850
2912
|
|
|
2851
2913
|
assert(roll >= 217 and roll <= 255);
|
|
2852
|
-
try options.chance.damage(player, roll);
|
|
2914
|
+
try options.chance.damage(player, i, roll);
|
|
2853
2915
|
return roll;
|
|
2854
2916
|
}
|
|
2855
2917
|
|
|
2856
2918
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2857
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2919
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2858
2920
|
val == .true
|
|
2859
2921
|
else if (showdown)
|
|
2860
2922
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2866,7 +2928,7 @@ pub const Rolls = struct {
|
|
|
2866
2928
|
}
|
|
2867
2929
|
|
|
2868
2930
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2869
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2931
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2870
2932
|
val == .true
|
|
2871
2933
|
else if (showdown)
|
|
2872
2934
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2878,7 +2940,7 @@ pub const Rolls = struct {
|
|
|
2878
2940
|
}
|
|
2879
2941
|
|
|
2880
2942
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2881
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2943
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2882
2944
|
val == .true
|
|
2883
2945
|
else if (showdown)
|
|
2884
2946
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2890,7 +2952,7 @@ pub const Rolls = struct {
|
|
|
2890
2952
|
}
|
|
2891
2953
|
|
|
2892
2954
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2893
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2955
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2894
2956
|
val == .true
|
|
2895
2957
|
else if (showdown)
|
|
2896
2958
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2903,7 +2965,7 @@ pub const Rolls = struct {
|
|
|
2903
2965
|
|
|
2904
2966
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2905
2967
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2906
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2968
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2907
2969
|
val == .true
|
|
2908
2970
|
else if (showdown)
|
|
2909
2971
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2915,7 +2977,7 @@ pub const Rolls = struct {
|
|
|
2915
2977
|
}
|
|
2916
2978
|
|
|
2917
2979
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2918
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2980
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2919
2981
|
val == .true
|
|
2920
2982
|
else if (showdown)
|
|
2921
2983
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2927,7 +2989,7 @@ pub const Rolls = struct {
|
|
|
2927
2989
|
}
|
|
2928
2990
|
|
|
2929
2991
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2930
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2992
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2931
2993
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2932
2994
|
else if (showdown)
|
|
2933
2995
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2943,7 +3005,7 @@ pub const Rolls = struct {
|
|
|
2943
3005
|
}
|
|
2944
3006
|
|
|
2945
3007
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2946
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3008
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2947
3009
|
(val - 1) * 40
|
|
2948
3010
|
else if (showdown) power: {
|
|
2949
3011
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2965,7 +3027,7 @@ pub const Rolls = struct {
|
|
|
2965
3027
|
}
|
|
2966
3028
|
|
|
2967
3029
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2968
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3030
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2969
3031
|
val + 3
|
|
2970
3032
|
else if (showdown) num: {
|
|
2971
3033
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2993,7 +3055,7 @@ pub const Rolls = struct {
|
|
|
2993
3055
|
}
|
|
2994
3056
|
|
|
2995
3057
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
2996
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3058
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
2997
3059
|
val
|
|
2998
3060
|
else if (showdown)
|
|
2999
3061
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3009,13 +3071,12 @@ pub const Rolls = struct {
|
|
|
3009
3071
|
return hits;
|
|
3010
3072
|
}
|
|
3011
3073
|
|
|
3012
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3013
|
-
|
|
3074
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3075
|
+
assert(!showdown);
|
|
3076
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3014
3077
|
val + 1
|
|
3015
|
-
else if (showdown)
|
|
3016
|
-
battle.rng.range(u8, 2, 6)
|
|
3017
3078
|
else
|
|
3018
|
-
(battle.rng.next() & 3) + 2;
|
|
3079
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3019
3080
|
|
|
3020
3081
|
assert(pp >= 2 and pp <= 5);
|
|
3021
3082
|
try options.chance.spite(player, pp);
|
|
@@ -3029,7 +3090,7 @@ pub const Rolls = struct {
|
|
|
3029
3090
|
var i: u8 = 0;
|
|
3030
3091
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3031
3092
|
assert(mtype != .@"???");
|
|
3032
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3093
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3033
3094
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3034
3095
|
else if (showdown) ty: {
|
|
3035
3096
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3063,7 +3124,7 @@ pub const Rolls = struct {
|
|
|
3063
3124
|
assert(count <= 8);
|
|
3064
3125
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3065
3126
|
|
|
3066
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3127
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3067
3128
|
val == .true
|
|
3068
3129
|
else if (showdown)
|
|
3069
3130
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3085,7 +3146,7 @@ pub const Rolls = struct {
|
|
|
3085
3146
|
const moves = active.moves;
|
|
3086
3147
|
|
|
3087
3148
|
// TODO: consider throwing error instead of rerolling?
|
|
3088
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3149
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3089
3150
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3090
3151
|
else
|
|
3091
3152
|
null;
|
|
@@ -3122,7 +3183,7 @@ pub const Rolls = struct {
|
|
|
3122
3183
|
const moves = active.moves;
|
|
3123
3184
|
|
|
3124
3185
|
// TODO: consider throwing error instead of rerolling?
|
|
3125
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3186
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3126
3187
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3127
3188
|
else
|
|
3128
3189
|
null;
|
|
@@ -3166,7 +3227,7 @@ pub const Rolls = struct {
|
|
|
3166
3227
|
assert(n > 0 and n <= 6);
|
|
3167
3228
|
|
|
3168
3229
|
// TODO: consider throwing error instead of rerolling?
|
|
3169
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3230
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3170
3231
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3171
3232
|
break :val null;
|
|
3172
3233
|
} else null;
|
|
@@ -3192,7 +3253,7 @@ pub const Rolls = struct {
|
|
|
3192
3253
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3193
3254
|
|
|
3194
3255
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3195
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3256
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3196
3257
|
@intCast(val)
|
|
3197
3258
|
else if (showdown)
|
|
3198
3259
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3207,7 +3268,7 @@ pub const Rolls = struct {
|
|
|
3207
3268
|
}
|
|
3208
3269
|
|
|
3209
3270
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3210
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3271
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3211
3272
|
val
|
|
3212
3273
|
else if (showdown)
|
|
3213
3274
|
battle.rng.range(u8, 1, max)
|
|
@@ -3233,7 +3294,7 @@ pub const Rolls = struct {
|
|
|
3233
3294
|
}
|
|
3234
3295
|
|
|
3235
3296
|
// TODO: consider throwing error instead of rerolling?
|
|
3236
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3297
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3237
3298
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3238
3299
|
break :val val;
|
|
3239
3300
|
} else null;
|
|
@@ -3265,7 +3326,7 @@ pub const Rolls = struct {
|
|
|
3265
3326
|
}
|
|
3266
3327
|
|
|
3267
3328
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3268
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3329
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3269
3330
|
@intCast(val)
|
|
3270
3331
|
else if (showdown)
|
|
3271
3332
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3277,12 +3338,12 @@ pub const Rolls = struct {
|
|
|
3277
3338
|
};
|
|
3278
3339
|
|
|
3279
3340
|
assert(duration >= 1 and duration <= 6);
|
|
3280
|
-
options.chance.duration(
|
|
3341
|
+
options.chance.duration(player, duration);
|
|
3281
3342
|
return duration + 1;
|
|
3282
3343
|
}
|
|
3283
3344
|
|
|
3284
3345
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3285
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3346
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3286
3347
|
@intCast(val)
|
|
3287
3348
|
else if (showdown)
|
|
3288
3349
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3291,16 +3352,16 @@ pub const Rolls = struct {
|
|
|
3291
3352
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3292
3353
|
|
|
3293
3354
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3294
|
-
options.chance.duration(
|
|
3355
|
+
options.chance.duration(player, duration);
|
|
3295
3356
|
return duration;
|
|
3296
3357
|
}
|
|
3297
3358
|
|
|
3298
3359
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3299
|
-
const duration:
|
|
3360
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3300
3361
|
@intCast(val)
|
|
3301
3362
|
else if (showdown)
|
|
3302
3363
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3303
|
-
battle.rng.range(
|
|
3364
|
+
battle.rng.range(u3, 2, 6)
|
|
3304
3365
|
else duration: {
|
|
3305
3366
|
while (true) {
|
|
3306
3367
|
const r = battle.rng.next() & 7;
|
|
@@ -3309,12 +3370,12 @@ pub const Rolls = struct {
|
|
|
3309
3370
|
};
|
|
3310
3371
|
|
|
3311
3372
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3312
|
-
options.chance.duration(
|
|
3373
|
+
options.chance.duration(player, duration);
|
|
3313
3374
|
return duration;
|
|
3314
3375
|
}
|
|
3315
3376
|
|
|
3316
3377
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3317
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3378
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3318
3379
|
@intCast(val)
|
|
3319
3380
|
else if (showdown)
|
|
3320
3381
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3322,12 +3383,12 @@ pub const Rolls = struct {
|
|
|
3322
3383
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3323
3384
|
|
|
3324
3385
|
assert(duration >= 2 and duration <= 3);
|
|
3325
|
-
options.chance.duration(
|
|
3386
|
+
options.chance.duration(player, duration);
|
|
3326
3387
|
return duration;
|
|
3327
3388
|
}
|
|
3328
3389
|
|
|
3329
3390
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3330
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3391
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3331
3392
|
@intCast(val)
|
|
3332
3393
|
else if (showdown)
|
|
3333
3394
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3336,13 +3397,13 @@ pub const Rolls = struct {
|
|
|
3336
3397
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3337
3398
|
|
|
3338
3399
|
assert(duration >= 2 and duration <= 5);
|
|
3339
|
-
options.chance.duration(
|
|
3400
|
+
options.chance.duration(player, duration);
|
|
3340
3401
|
return duration + 1;
|
|
3341
3402
|
}
|
|
3342
3403
|
|
|
3343
3404
|
// TODO: consider sharing implementation with bindingDuration
|
|
3344
3405
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3345
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3406
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3346
3407
|
@intCast(val)
|
|
3347
3408
|
else if (showdown)
|
|
3348
3409
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3350,7 +3411,7 @@ pub const Rolls = struct {
|
|
|
3350
3411
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3351
3412
|
|
|
3352
3413
|
assert(duration >= 2 and duration <= 5);
|
|
3353
|
-
options.chance.duration(
|
|
3414
|
+
options.chance.duration(player, duration);
|
|
3354
3415
|
return duration + 1;
|
|
3355
3416
|
}
|
|
3356
3417
|
};
|
package/src/lib/pkmn.zig
CHANGED
|
@@ -22,13 +22,16 @@ pub const Choice = @import("common/data.zig").Choice;
|
|
|
22
22
|
/// considered terminal.
|
|
23
23
|
pub const Result = @import("common/data.zig").Result;
|
|
24
24
|
|
|
25
|
+
// TODO
|
|
26
|
+
pub const Array = @import("common/array.zig").Array;
|
|
27
|
+
// TODO
|
|
28
|
+
pub const Optional = @import("common/optional.zig").Optional;
|
|
29
|
+
|
|
25
30
|
/// Pokémon Showdown's RNG (backed by a Generation V & VI RNG).
|
|
26
31
|
pub const PSRNG = @import("common/rng.zig").PSRNG;
|
|
27
32
|
|
|
28
33
|
/// Namespace for helpers related to protocol message logging.
|
|
29
|
-
pub const protocol = if (options.internal) struct {
|
|
30
|
-
pub usingnamespace @import("common/protocol.zig");
|
|
31
|
-
} else struct {
|
|
34
|
+
pub const protocol = if (options.internal) @import("common/protocol.zig") else struct {
|
|
32
35
|
/// Logs protocol information to its `Writer` during a battle update when
|
|
33
36
|
/// `options.log` is enabled.
|
|
34
37
|
pub const Log = @import("common/protocol.zig").Log;
|
|
@@ -82,11 +85,7 @@ pub const gen1 = struct {
|
|
|
82
85
|
/// via `overrides` and tracks `summaries` of information relevant to damage calculation.
|
|
83
86
|
pub const Calc = @import("gen1/calc.zig").Calc;
|
|
84
87
|
/// Namespace for types associated with supported Generation I Pokémon damage calc features.
|
|
85
|
-
pub const calc = if (options.internal) struct {
|
|
86
|
-
pub usingnamespace @import("gen1/calc.zig");
|
|
87
|
-
} else struct {
|
|
88
|
-
/// TODO
|
|
89
|
-
pub const Overrides = @import("gen1/calc.zig").Overrides;
|
|
88
|
+
pub const calc = if (options.internal) @import("gen1/calc.zig") else struct {
|
|
90
89
|
/// Information relevant to damage calculation that occured during a Generation I
|
|
91
90
|
/// battle `update`.
|
|
92
91
|
pub const Summaries = @import("gen1/calc.zig").Summaries;
|
|
@@ -114,10 +113,7 @@ pub const gen2 = struct {
|
|
|
114
113
|
pub const NULL = @import("gen2/chance.zig").NULL;
|
|
115
114
|
};
|
|
116
115
|
pub const Calc = @import("gen2/calc.zig").Calc;
|
|
117
|
-
pub const calc = if (options.internal) struct {
|
|
118
|
-
pub usingnamespace @import("gen2/calc.zig");
|
|
119
|
-
} else struct {
|
|
120
|
-
pub const Overrides = @import("gen2/calc.zig").Overrides;
|
|
116
|
+
pub const calc = if (options.internal) @import("gen2/calc.zig") else struct {
|
|
121
117
|
pub const Summaries = @import("gen2/calc.zig").Summaries;
|
|
122
118
|
pub const Summary = @import("gen2/calc.zig").Summary;
|
|
123
119
|
pub const NULL = @import("gen2/calc.zig").NULL;
|
|
@@ -127,10 +123,8 @@ pub const gen2 = struct {
|
|
|
127
123
|
|
|
128
124
|
// Internal APIs used by other pkmn libraries, not actually part of the public API.
|
|
129
125
|
// NOTE: `pub usingnamespace struct { ... }` here results in a (false?) dependency loop
|
|
130
|
-
pub const js = if (options.internal)
|
|
131
|
-
pub usingnamespace @import("common/js.zig");
|
|
132
|
-
} else struct {};
|
|
126
|
+
pub const js = if (options.internal) @import("common/js.zig") else {};
|
|
133
127
|
pub const bindings = if (options.internal) struct {
|
|
134
128
|
pub const node = @import("node.zig");
|
|
135
129
|
pub const wasm = @import("wasm.zig");
|
|
136
|
-
} else
|
|
130
|
+
} else {};
|