@pkmn/engine 0.1.0-dev.eaae4aa6 → 0.1.0-dev.f3aab252
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 +187 -72
- package/package.json +18 -16
- package/src/bin/install-pkmn-engine +17 -7
- package/src/lib/common/array.zig +77 -0
- 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 +506 -128
- package/src/lib/gen1/chance.zig +383 -276
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +31 -30
- package/src/lib/gen1/helpers.zig +95 -538
- package/src/lib/gen1/mechanics.zig +265 -162
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +90 -502
- package/src/lib/gen2/data.zig +15 -14
- package/src/lib/gen2/helpers.zig +41 -13
- package/src/lib/gen2/mechanics.zig +158 -103
- 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
|
@@ -6,7 +6,6 @@ const common = @import("../common/data.zig");
|
|
|
6
6
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
7
|
const protocol = @import("../common/protocol.zig");
|
|
8
8
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
9
|
|
|
11
10
|
const data = @import("data.zig");
|
|
12
11
|
const generated = @import("generated.zig");
|
|
@@ -34,17 +33,12 @@ const Start = protocol.Start;
|
|
|
34
33
|
|
|
35
34
|
const Gen12 = rng.Gen12;
|
|
36
35
|
|
|
37
|
-
const PointerType = util.PointerType;
|
|
38
|
-
|
|
39
36
|
const ActivePokemon = data.ActivePokemon;
|
|
40
37
|
const Boosts = data.Boosts;
|
|
41
38
|
const Effectiveness = data.Effectiveness;
|
|
42
39
|
const Item = data.Item;
|
|
43
40
|
const Move = data.Move;
|
|
44
|
-
const MoveSlot = data.MoveSlot;
|
|
45
|
-
const Pokemon = data.Pokemon;
|
|
46
41
|
const Side = data.Side;
|
|
47
|
-
const Species = data.Species;
|
|
48
42
|
const Stats = data.Stats;
|
|
49
43
|
const Status = data.Status;
|
|
50
44
|
const TriAttack = data.TriAttack;
|
|
@@ -106,6 +100,7 @@ pub const State = struct {
|
|
|
106
100
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
101
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
102
|
move: Move, // wCurPlayerMove
|
|
103
|
+
hit: u3 = 0,
|
|
109
104
|
from: Move = .None,
|
|
110
105
|
mslot: u4, // wCurMoveNum
|
|
111
106
|
first: bool, // wEnemyGoesFirst
|
|
@@ -256,7 +251,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
251
|
} else {
|
|
257
252
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
253
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
254
|
|
|
261
255
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
256
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -442,18 +436,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
442
436
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
443
437
|
const before = stored.status;
|
|
444
438
|
// 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
439
|
stored.status -= 1;
|
|
449
|
-
// }
|
|
450
440
|
|
|
451
|
-
|
|
452
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
453
|
-
try options.chance.sleep(player, duration);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
if (duration == 0) {
|
|
441
|
+
if (Status.duration(stored.status) == 0) {
|
|
457
442
|
try log.curestatus(.{ ident, before, .Message });
|
|
458
443
|
stored.status = 0; // clears EXT if present
|
|
459
444
|
resetCant(volatiles);
|
|
@@ -483,12 +468,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
483
468
|
}
|
|
484
469
|
|
|
485
470
|
if (volatiles.disable.duration > 0) {
|
|
486
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
487
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
488
|
-
// } else {
|
|
489
471
|
volatiles.disable.duration -= 1;
|
|
490
|
-
// }
|
|
491
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
492
472
|
|
|
493
473
|
if (volatiles.disable.duration == 0) {
|
|
494
474
|
volatiles.disable.move = 0;
|
|
@@ -498,13 +478,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
498
478
|
|
|
499
479
|
if (volatiles.Confusion) {
|
|
500
480
|
assert(volatiles.confusion > 0);
|
|
501
|
-
|
|
502
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
503
|
-
// if (!extend) volatiles.confusion = 0;
|
|
504
|
-
// } else {
|
|
505
481
|
volatiles.confusion -= 1;
|
|
506
|
-
// }
|
|
507
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
508
482
|
|
|
509
483
|
if (volatiles.confusion == 0) {
|
|
510
484
|
volatiles.Confusion = false;
|
|
@@ -665,7 +639,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
665
639
|
}
|
|
666
640
|
assert(stage <= 4);
|
|
667
641
|
|
|
668
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
642
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
669
643
|
}
|
|
670
644
|
|
|
671
645
|
pub fn calcDamage(
|
|
@@ -733,7 +707,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
733
707
|
|
|
734
708
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
735
709
|
if (state.damage <= 1) return;
|
|
736
|
-
const random = try Rolls.damage(battle, player, options);
|
|
710
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
737
711
|
options.calc.base(player.foe(), state.damage);
|
|
738
712
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
739
713
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -1304,14 +1278,32 @@ pub const Effects = struct {
|
|
|
1304
1278
|
}
|
|
1305
1279
|
|
|
1306
1280
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1281
|
+
assert(!state.miss);
|
|
1282
|
+
var side = battle.side(player);
|
|
1283
|
+
|
|
1284
|
+
const ident = battle.active(player);
|
|
1285
|
+
|
|
1286
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1287
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1288
|
+
return options.log.fail(.{ ident, .None });
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1292
|
+
if (side.stored().hp <= hp) {
|
|
1293
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1294
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1295
|
+
return options.log.fail(.{ ident, .None });
|
|
1296
|
+
} else {
|
|
1297
|
+
side.stored().hp -= hp;
|
|
1298
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1299
|
+
|
|
1300
|
+
const before = side.active.boosts.atk;
|
|
1301
|
+
var i: usize = 0;
|
|
1302
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1303
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1304
|
+
}
|
|
1305
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1306
|
+
}
|
|
1315
1307
|
}
|
|
1316
1308
|
|
|
1317
1309
|
pub const bide = struct {
|
|
@@ -1323,13 +1315,7 @@ pub const Effects = struct {
|
|
|
1323
1315
|
|
|
1324
1316
|
if (!volatiles.Bide) return;
|
|
1325
1317
|
|
|
1326
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1327
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1328
|
-
// } else {
|
|
1329
1318
|
volatiles.attacks -= 1;
|
|
1330
|
-
// }
|
|
1331
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1332
|
-
|
|
1333
1319
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1334
1320
|
|
|
1335
1321
|
volatiles.Bide = false;
|
|
@@ -1596,8 +1582,16 @@ pub const Effects = struct {
|
|
|
1596
1582
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1597
1583
|
}
|
|
1598
1584
|
|
|
1599
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1600
|
-
|
|
1585
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1586
|
+
var side = battle.side(player);
|
|
1587
|
+
var stored = side.stored();
|
|
1588
|
+
|
|
1589
|
+
stored.hp = 0;
|
|
1590
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1591
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1592
|
+
if (!showdown) stored.status = 0;
|
|
1593
|
+
side.active.volatiles.LeechSeed = false;
|
|
1594
|
+
side.active.volatiles.DestinyBond = false;
|
|
1601
1595
|
}
|
|
1602
1596
|
|
|
1603
1597
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1723,8 +1717,26 @@ pub const Effects = struct {
|
|
|
1723
1717
|
foe.active.volatiles.frozen = true;
|
|
1724
1718
|
}
|
|
1725
1719
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1720
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1721
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1722
|
+
var side = battle.side(player);
|
|
1723
|
+
var volatiles = &side.active.volatiles;
|
|
1724
|
+
|
|
1725
|
+
if (state.miss) {
|
|
1726
|
+
volatiles.fury_cutter = 0;
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1731
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1732
|
+
volatiles.fury_cutter += 1;
|
|
1733
|
+
|
|
1734
|
+
if (showdown) {
|
|
1735
|
+
state.bp *= 2;
|
|
1736
|
+
assert(state.bp <= 160);
|
|
1737
|
+
} else {
|
|
1738
|
+
state.damage *|= 2;
|
|
1739
|
+
}
|
|
1728
1740
|
}
|
|
1729
1741
|
|
|
1730
1742
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1885,7 +1897,29 @@ pub const Effects = struct {
|
|
|
1885
1897
|
}
|
|
1886
1898
|
|
|
1887
1899
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1888
|
-
|
|
1900
|
+
var side = battle.side(player);
|
|
1901
|
+
|
|
1902
|
+
if (state.miss) {
|
|
1903
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1904
|
+
try options.log.lastmiss(.{});
|
|
1905
|
+
return options.log.miss(.{battle.active(player)});
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
const move = battle.foe(player).lastMove(false);
|
|
1909
|
+
if (move == .None or move == .Struggle) {
|
|
1910
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
var slot: u4 = 0;
|
|
1914
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1915
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1916
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1917
|
+
}
|
|
1918
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1919
|
+
|
|
1920
|
+
side.active.move(slot).id = move;
|
|
1921
|
+
side.active.move(slot).pp = 5;
|
|
1922
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1889
1923
|
}
|
|
1890
1924
|
|
|
1891
1925
|
pub fn mirrorMove(
|
|
@@ -2393,17 +2427,37 @@ pub const Effects = struct {
|
|
|
2393
2427
|
}
|
|
2394
2428
|
|
|
2395
2429
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2396
|
-
|
|
2397
|
-
// var foe = battle.foe(player);
|
|
2430
|
+
var foe = battle.foe(player);
|
|
2398
2431
|
|
|
2399
|
-
|
|
2432
|
+
const last = foe.lastMove(false);
|
|
2433
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2434
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2435
|
+
}
|
|
2400
2436
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
//
|
|
2406
|
-
|
|
2437
|
+
var slot: u4 = 0;
|
|
2438
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2439
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2440
|
+
}
|
|
2441
|
+
// If the move was called via Metronome it might not exist
|
|
2442
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2443
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2447
|
+
const pp = @min(
|
|
2448
|
+
foe.active.move(slot).pp,
|
|
2449
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2450
|
+
);
|
|
2451
|
+
foe.active.move(slot).pp -= pp;
|
|
2452
|
+
if (!foe.active.volatiles.Transform) {
|
|
2453
|
+
var move_slot = foe.stored().move(slot);
|
|
2454
|
+
assert(move_slot.pp > 0);
|
|
2455
|
+
|
|
2456
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2457
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2407
2461
|
}
|
|
2408
2462
|
|
|
2409
2463
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2790,7 +2844,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2790
2844
|
|
|
2791
2845
|
pub const Rolls = struct {
|
|
2792
2846
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2793
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2847
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2794
2848
|
player == .P1
|
|
2795
2849
|
else if (showdown)
|
|
2796
2850
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2802,7 +2856,7 @@ pub const Rolls = struct {
|
|
|
2802
2856
|
}
|
|
2803
2857
|
|
|
2804
2858
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2805
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2859
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2806
2860
|
val == .true
|
|
2807
2861
|
else if (showdown)
|
|
2808
2862
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2814,7 +2868,7 @@ pub const Rolls = struct {
|
|
|
2814
2868
|
}
|
|
2815
2869
|
|
|
2816
2870
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2817
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2871
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2818
2872
|
val == .true
|
|
2819
2873
|
else if (showdown)
|
|
2820
2874
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2825,20 +2879,22 @@ pub const Rolls = struct {
|
|
|
2825
2879
|
return ok;
|
|
2826
2880
|
}
|
|
2827
2881
|
|
|
2828
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2829
|
-
|
|
2882
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2883
|
+
_ = .{ player, options };
|
|
2884
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2830
2885
|
val == .true
|
|
2831
2886
|
else if (showdown)
|
|
2832
2887
|
battle.rng.chance(u8, rate, 256)
|
|
2833
2888
|
else
|
|
2834
2889
|
battle.rng.next() < rate;
|
|
2835
2890
|
|
|
2836
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2891
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2837
2892
|
return crit;
|
|
2838
2893
|
}
|
|
2839
2894
|
|
|
2840
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2841
|
-
|
|
2895
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2896
|
+
_ = .{ player, options };
|
|
2897
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2842
2898
|
val
|
|
2843
2899
|
else roll: {
|
|
2844
2900
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2849,12 +2905,12 @@ pub const Rolls = struct {
|
|
|
2849
2905
|
};
|
|
2850
2906
|
|
|
2851
2907
|
assert(roll >= 217 and roll <= 255);
|
|
2852
|
-
try options.chance.damage(player, roll);
|
|
2908
|
+
try options.chance.damage(player, i, roll);
|
|
2853
2909
|
return roll;
|
|
2854
2910
|
}
|
|
2855
2911
|
|
|
2856
2912
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2857
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2913
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2858
2914
|
val == .true
|
|
2859
2915
|
else if (showdown)
|
|
2860
2916
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2866,7 +2922,7 @@ pub const Rolls = struct {
|
|
|
2866
2922
|
}
|
|
2867
2923
|
|
|
2868
2924
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2869
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2925
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2870
2926
|
val == .true
|
|
2871
2927
|
else if (showdown)
|
|
2872
2928
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2878,7 +2934,7 @@ pub const Rolls = struct {
|
|
|
2878
2934
|
}
|
|
2879
2935
|
|
|
2880
2936
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2881
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2937
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2882
2938
|
val == .true
|
|
2883
2939
|
else if (showdown)
|
|
2884
2940
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2890,7 +2946,7 @@ pub const Rolls = struct {
|
|
|
2890
2946
|
}
|
|
2891
2947
|
|
|
2892
2948
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2893
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2949
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2894
2950
|
val == .true
|
|
2895
2951
|
else if (showdown)
|
|
2896
2952
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2903,7 +2959,7 @@ pub const Rolls = struct {
|
|
|
2903
2959
|
|
|
2904
2960
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2905
2961
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2906
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2962
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2907
2963
|
val == .true
|
|
2908
2964
|
else if (showdown)
|
|
2909
2965
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2915,7 +2971,7 @@ pub const Rolls = struct {
|
|
|
2915
2971
|
}
|
|
2916
2972
|
|
|
2917
2973
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2918
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2974
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2919
2975
|
val == .true
|
|
2920
2976
|
else if (showdown)
|
|
2921
2977
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2927,7 +2983,7 @@ pub const Rolls = struct {
|
|
|
2927
2983
|
}
|
|
2928
2984
|
|
|
2929
2985
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2930
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2986
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2931
2987
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2932
2988
|
else if (showdown)
|
|
2933
2989
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2943,7 +2999,7 @@ pub const Rolls = struct {
|
|
|
2943
2999
|
}
|
|
2944
3000
|
|
|
2945
3001
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2946
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3002
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2947
3003
|
(val - 1) * 40
|
|
2948
3004
|
else if (showdown) power: {
|
|
2949
3005
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2965,7 +3021,7 @@ pub const Rolls = struct {
|
|
|
2965
3021
|
}
|
|
2966
3022
|
|
|
2967
3023
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2968
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3024
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2969
3025
|
val + 3
|
|
2970
3026
|
else if (showdown) num: {
|
|
2971
3027
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2993,7 +3049,7 @@ pub const Rolls = struct {
|
|
|
2993
3049
|
}
|
|
2994
3050
|
|
|
2995
3051
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
2996
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3052
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
2997
3053
|
val
|
|
2998
3054
|
else if (showdown)
|
|
2999
3055
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3009,13 +3065,12 @@ pub const Rolls = struct {
|
|
|
3009
3065
|
return hits;
|
|
3010
3066
|
}
|
|
3011
3067
|
|
|
3012
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3013
|
-
|
|
3068
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3069
|
+
assert(!showdown);
|
|
3070
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3014
3071
|
val + 1
|
|
3015
|
-
else if (showdown)
|
|
3016
|
-
battle.rng.range(u8, 2, 6)
|
|
3017
3072
|
else
|
|
3018
|
-
(battle.rng.next() & 3) + 2;
|
|
3073
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3019
3074
|
|
|
3020
3075
|
assert(pp >= 2 and pp <= 5);
|
|
3021
3076
|
try options.chance.spite(player, pp);
|
|
@@ -3029,7 +3084,7 @@ pub const Rolls = struct {
|
|
|
3029
3084
|
var i: u8 = 0;
|
|
3030
3085
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3031
3086
|
assert(mtype != .@"???");
|
|
3032
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3087
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3033
3088
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3034
3089
|
else if (showdown) ty: {
|
|
3035
3090
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3063,7 +3118,7 @@ pub const Rolls = struct {
|
|
|
3063
3118
|
assert(count <= 8);
|
|
3064
3119
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3065
3120
|
|
|
3066
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3121
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3067
3122
|
val == .true
|
|
3068
3123
|
else if (showdown)
|
|
3069
3124
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3085,7 +3140,7 @@ pub const Rolls = struct {
|
|
|
3085
3140
|
const moves = active.moves;
|
|
3086
3141
|
|
|
3087
3142
|
// TODO: consider throwing error instead of rerolling?
|
|
3088
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3143
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3089
3144
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3090
3145
|
else
|
|
3091
3146
|
null;
|
|
@@ -3122,7 +3177,7 @@ pub const Rolls = struct {
|
|
|
3122
3177
|
const moves = active.moves;
|
|
3123
3178
|
|
|
3124
3179
|
// TODO: consider throwing error instead of rerolling?
|
|
3125
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3180
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3126
3181
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3127
3182
|
else
|
|
3128
3183
|
null;
|
|
@@ -3166,7 +3221,7 @@ pub const Rolls = struct {
|
|
|
3166
3221
|
assert(n > 0 and n <= 6);
|
|
3167
3222
|
|
|
3168
3223
|
// TODO: consider throwing error instead of rerolling?
|
|
3169
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3224
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3170
3225
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3171
3226
|
break :val null;
|
|
3172
3227
|
} else null;
|
|
@@ -3192,7 +3247,7 @@ pub const Rolls = struct {
|
|
|
3192
3247
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3193
3248
|
|
|
3194
3249
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3195
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3250
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3196
3251
|
@intCast(val)
|
|
3197
3252
|
else if (showdown)
|
|
3198
3253
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3207,7 +3262,7 @@ pub const Rolls = struct {
|
|
|
3207
3262
|
}
|
|
3208
3263
|
|
|
3209
3264
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3210
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3265
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3211
3266
|
val
|
|
3212
3267
|
else if (showdown)
|
|
3213
3268
|
battle.rng.range(u8, 1, max)
|
|
@@ -3233,7 +3288,7 @@ pub const Rolls = struct {
|
|
|
3233
3288
|
}
|
|
3234
3289
|
|
|
3235
3290
|
// TODO: consider throwing error instead of rerolling?
|
|
3236
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3291
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3237
3292
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3238
3293
|
break :val val;
|
|
3239
3294
|
} else null;
|
|
@@ -3265,7 +3320,7 @@ pub const Rolls = struct {
|
|
|
3265
3320
|
}
|
|
3266
3321
|
|
|
3267
3322
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3268
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3323
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3269
3324
|
@intCast(val)
|
|
3270
3325
|
else if (showdown)
|
|
3271
3326
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3277,12 +3332,12 @@ pub const Rolls = struct {
|
|
|
3277
3332
|
};
|
|
3278
3333
|
|
|
3279
3334
|
assert(duration >= 1 and duration <= 6);
|
|
3280
|
-
options.chance.duration(
|
|
3335
|
+
options.chance.duration(player, duration);
|
|
3281
3336
|
return duration + 1;
|
|
3282
3337
|
}
|
|
3283
3338
|
|
|
3284
3339
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3285
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3340
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3286
3341
|
@intCast(val)
|
|
3287
3342
|
else if (showdown)
|
|
3288
3343
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3291,16 +3346,16 @@ pub const Rolls = struct {
|
|
|
3291
3346
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3292
3347
|
|
|
3293
3348
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3294
|
-
options.chance.duration(
|
|
3349
|
+
options.chance.duration(player, duration);
|
|
3295
3350
|
return duration;
|
|
3296
3351
|
}
|
|
3297
3352
|
|
|
3298
3353
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3299
|
-
const duration:
|
|
3354
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3300
3355
|
@intCast(val)
|
|
3301
3356
|
else if (showdown)
|
|
3302
3357
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3303
|
-
battle.rng.range(
|
|
3358
|
+
battle.rng.range(u3, 2, 6)
|
|
3304
3359
|
else duration: {
|
|
3305
3360
|
while (true) {
|
|
3306
3361
|
const r = battle.rng.next() & 7;
|
|
@@ -3309,12 +3364,12 @@ pub const Rolls = struct {
|
|
|
3309
3364
|
};
|
|
3310
3365
|
|
|
3311
3366
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3312
|
-
options.chance.duration(
|
|
3367
|
+
options.chance.duration(player, duration);
|
|
3313
3368
|
return duration;
|
|
3314
3369
|
}
|
|
3315
3370
|
|
|
3316
3371
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3317
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3372
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3318
3373
|
@intCast(val)
|
|
3319
3374
|
else if (showdown)
|
|
3320
3375
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3322,12 +3377,12 @@ pub const Rolls = struct {
|
|
|
3322
3377
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3323
3378
|
|
|
3324
3379
|
assert(duration >= 2 and duration <= 3);
|
|
3325
|
-
options.chance.duration(
|
|
3380
|
+
options.chance.duration(player, duration);
|
|
3326
3381
|
return duration;
|
|
3327
3382
|
}
|
|
3328
3383
|
|
|
3329
3384
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3330
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3385
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3331
3386
|
@intCast(val)
|
|
3332
3387
|
else if (showdown)
|
|
3333
3388
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3336,13 +3391,13 @@ pub const Rolls = struct {
|
|
|
3336
3391
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3337
3392
|
|
|
3338
3393
|
assert(duration >= 2 and duration <= 5);
|
|
3339
|
-
options.chance.duration(
|
|
3394
|
+
options.chance.duration(player, duration);
|
|
3340
3395
|
return duration + 1;
|
|
3341
3396
|
}
|
|
3342
3397
|
|
|
3343
3398
|
// TODO: consider sharing implementation with bindingDuration
|
|
3344
3399
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3345
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3400
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3346
3401
|
@intCast(val)
|
|
3347
3402
|
else if (showdown)
|
|
3348
3403
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3350,7 +3405,7 @@ pub const Rolls = struct {
|
|
|
3350
3405
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3351
3406
|
|
|
3352
3407
|
assert(duration >= 2 and duration <= 5);
|
|
3353
|
-
options.chance.duration(
|
|
3408
|
+
options.chance.duration(player, duration);
|
|
3354
3409
|
return duration + 1;
|
|
3355
3410
|
}
|
|
3356
3411
|
};
|
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 {};
|