@pkmn/engine 0.1.0-dev.53787794 → 0.1.0-dev.54a1aa85
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/README.md +5 -4
- package/build/pkg/addon/browser.d.ts +1 -1
- package/build/pkg/addon/browser.js +2 -1
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +2 -1
- package/build/pkg/addon/node.js.map +1 -1
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +44 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +23 -3
- package/build/pkg/addon.js +8 -17
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/gen1.d.ts +13 -17
- package/build/pkg/gen1.js +6 -10
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +35 -100
- package/build/pkg/index.js +6 -68
- package/build/pkg/index.js.map +1 -1
- package/build/tools/debug.js +2 -2
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +21 -8
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/index.js +17 -8
- package/build/tools/display/index.js.map +1 -1
- package/build/tools/display/ui.jsx +7 -7
- package/build/tools/display/ui.jsx.map +1 -1
- package/build.zig +112 -68
- package/package.json +24 -16
- package/src/bin/install-pkmn-engine +1 -1
- package/src/lib/common/debug.zig +11 -6
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +4 -8
- package/src/lib/common/rational.zig +7 -5
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/gen1/calc.zig +96 -98
- package/src/lib/gen1/chance.zig +37 -21
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +1 -3
- package/src/lib/gen1/data/types.zig +1 -2
- package/src/lib/gen1/data.zig +7 -11
- package/src/lib/gen1/helpers.zig +5 -18
- package/src/lib/gen1/mechanics.zig +108 -90
- package/src/lib/gen2/calc.zig +9 -17
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +1 -2
- package/src/lib/gen2/data.zig +11 -15
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +5 -12
- package/src/lib/gen2/mechanics.zig +41 -38
- package/src/lib/node.zig +2 -4
- package/src/lib/pkmn.zig +4 -0
- package/src/lib/wasm.zig +36 -1
- package/src/tools/display/ui.css +12 -9
|
@@ -1,36 +1,29 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
1
|
+
const DEBUG = @import("../common/debug.zig").print;
|
|
4
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const chance = @import("chance.zig");
|
|
5
5
|
const common = @import("../common/data.zig");
|
|
6
|
-
const
|
|
6
|
+
const data = @import("data.zig");
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
7
8
|
const protocol = @import("../common/protocol.zig");
|
|
8
9
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
|
|
10
|
-
const data = @import("data.zig");
|
|
11
|
-
const chance = @import("chance.zig");
|
|
10
|
+
const std = @import("std");
|
|
12
11
|
|
|
13
12
|
const assert = std.debug.assert;
|
|
14
|
-
|
|
15
|
-
const expectEqual = std.testing.expectEqual;
|
|
16
|
-
|
|
17
|
-
const showdown = pkmn.options.showdown;
|
|
18
|
-
|
|
19
|
-
const Choice = common.Choice;
|
|
20
|
-
const ID = common.ID;
|
|
21
|
-
const Player = common.Player;
|
|
22
|
-
const Result = common.Result;
|
|
23
|
-
|
|
24
13
|
const Boost = protocol.Boost;
|
|
14
|
+
const Choice = common.Choice;
|
|
25
15
|
const Damage = protocol.Damage;
|
|
16
|
+
const Effectiveness = data.Effectiveness;
|
|
17
|
+
const expectEqual = std.testing.expectEqual;
|
|
26
18
|
const Fail = protocol.Fail;
|
|
27
|
-
const Heal = protocol.Heal;
|
|
28
|
-
|
|
29
19
|
const Gen12 = rng.Gen12;
|
|
30
|
-
|
|
31
|
-
const
|
|
20
|
+
const Heal = protocol.Heal;
|
|
21
|
+
const ID = common.ID;
|
|
32
22
|
const Move = data.Move;
|
|
33
23
|
const MoveSlot = data.MoveSlot;
|
|
24
|
+
const Player = common.Player;
|
|
25
|
+
const Result = common.Result;
|
|
26
|
+
const showdown = pkmn.options.showdown;
|
|
34
27
|
const Side = data.Side;
|
|
35
28
|
const Species = data.Species;
|
|
36
29
|
const Stats = data.Stats;
|
|
@@ -173,7 +166,10 @@ fn selectMove(
|
|
|
173
166
|
if (foe_choice.type == .Switch) {
|
|
174
167
|
const last = side.active.move(battle.lastMove(player).index);
|
|
175
168
|
if (last.id == .Metronome) side.last_selected_move = last.id;
|
|
176
|
-
if (last.id == .MirrorMove)
|
|
169
|
+
if (last.id == .MirrorMove) {
|
|
170
|
+
if (!pkmn.options.mod) return Result.Error;
|
|
171
|
+
side.last_selected_move = last.id;
|
|
172
|
+
}
|
|
177
173
|
}
|
|
178
174
|
}
|
|
179
175
|
return null;
|
|
@@ -341,7 +337,6 @@ fn doTurn(
|
|
|
341
337
|
player_choice,
|
|
342
338
|
player_rewrap,
|
|
343
339
|
player_skip,
|
|
344
|
-
true,
|
|
345
340
|
&residual,
|
|
346
341
|
options,
|
|
347
342
|
)) |r| return r;
|
|
@@ -364,7 +359,6 @@ fn doTurn(
|
|
|
364
359
|
foe_choice,
|
|
365
360
|
foe_rewrap,
|
|
366
361
|
foe_skip,
|
|
367
|
-
false,
|
|
368
362
|
&residual,
|
|
369
363
|
options,
|
|
370
364
|
)) |r| return r;
|
|
@@ -392,7 +386,6 @@ fn executeMove(
|
|
|
392
386
|
choice: Choice,
|
|
393
387
|
rewrap: bool,
|
|
394
388
|
skip: bool,
|
|
395
|
-
first: bool,
|
|
396
389
|
residual: *bool,
|
|
397
390
|
options: anytype,
|
|
398
391
|
) !?Result {
|
|
@@ -427,7 +420,7 @@ fn executeMove(
|
|
|
427
420
|
mslot = @intCast(battle.lastMove(player).index);
|
|
428
421
|
const stored = side.stored();
|
|
429
422
|
// GLITCH: Struggle bypass PP underflow via Hyper Beam / Trapping-switch auto selection
|
|
430
|
-
auto = isForced(&side.active) or
|
|
423
|
+
auto = pkmn.options.mod or isForced(&side.active) or
|
|
431
424
|
side.active.volatiles.Binding or side.active.volatiles.Bide or
|
|
432
425
|
side.last_selected_move == .HyperBeam or
|
|
433
426
|
Status.is(stored.status, .FRZ) or Status.is(stored.status, .SLP);
|
|
@@ -452,7 +445,7 @@ fn executeMove(
|
|
|
452
445
|
|
|
453
446
|
var skip_can = false;
|
|
454
447
|
var skip_pp = false;
|
|
455
|
-
switch (try beforeMove(battle, player, skip,
|
|
448
|
+
switch (try beforeMove(battle, player, skip, residual, options)) {
|
|
456
449
|
.done => return null,
|
|
457
450
|
.skip_can => skip_can = true,
|
|
458
451
|
.skip_pp => skip_pp = true,
|
|
@@ -460,6 +453,25 @@ fn executeMove(
|
|
|
460
453
|
.err => return @as(?Result, Result.Error),
|
|
461
454
|
}
|
|
462
455
|
|
|
456
|
+
// Pokémon Showdown incorrectly implements PP deduction when handling the Hyper Beam automatic
|
|
457
|
+
// selection glitch - if the move was proced due to Metronome / Mirror Move it either subtracts
|
|
458
|
+
// from the Hyper Beam slot if present or skips PP deduction entirely...
|
|
459
|
+
if (showdown and !skip_pp and side.last_selected_move == .HyperBeam and
|
|
460
|
+
side.last_selected_move != side.active.move(battle.lastMove(player).index).id)
|
|
461
|
+
{
|
|
462
|
+
assert(mslot == 1);
|
|
463
|
+
const has_beam = has_beam: {
|
|
464
|
+
for (side.active.moves, 0..) |m, i| {
|
|
465
|
+
if (m.id == .HyperBeam) {
|
|
466
|
+
mslot = @intCast(i + 1);
|
|
467
|
+
break :has_beam true;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
break :has_beam false;
|
|
471
|
+
};
|
|
472
|
+
if (!has_beam) skip_pp = true;
|
|
473
|
+
}
|
|
474
|
+
|
|
463
475
|
const can = skip_can or
|
|
464
476
|
try canMove(battle, player, mslot, auto, skip_pp, .None, residual, options);
|
|
465
477
|
if (!can) return null;
|
|
@@ -473,7 +485,6 @@ fn beforeMove(
|
|
|
473
485
|
battle: anytype,
|
|
474
486
|
player: Player,
|
|
475
487
|
skip: bool,
|
|
476
|
-
first: bool,
|
|
477
488
|
residual: *bool,
|
|
478
489
|
options: anytype,
|
|
479
490
|
) !BeforeMove {
|
|
@@ -558,24 +569,17 @@ fn beforeMove(
|
|
|
558
569
|
// This can only happen if a Pokémon started the battle frozen/sleeping and was thawed/woken
|
|
559
570
|
// before the side had a selected a move - we simply need to assume this leads to a desync
|
|
560
571
|
if (side.last_selected_move == .None) {
|
|
561
|
-
assert(!
|
|
572
|
+
assert(!pkmn.options.mod);
|
|
562
573
|
return .err;
|
|
563
574
|
}
|
|
564
575
|
|
|
565
576
|
if (volatiles.Confusion) {
|
|
566
577
|
assert(volatiles.confusion > 0);
|
|
567
578
|
if (options.calc.overridden(player, .confusion)) |obs| switch (obs) {
|
|
568
|
-
.started =>
|
|
569
|
-
if (!first or (options.calc.overridden(player, .confused) orelse .None) != .None) {
|
|
570
|
-
volatiles.confusion -= 1;
|
|
571
|
-
} else {
|
|
572
|
-
volatiles.confusion = 0;
|
|
573
|
-
}
|
|
574
|
-
},
|
|
579
|
+
.started, .ended => volatiles.confusion = 0,
|
|
575
580
|
.continuing => if (volatiles.confusion > 1) {
|
|
576
581
|
volatiles.confusion -= 1;
|
|
577
582
|
},
|
|
578
|
-
.ended => volatiles.confusion = 0,
|
|
579
583
|
.overwritten => {},
|
|
580
584
|
else => unreachable,
|
|
581
585
|
} else {
|
|
@@ -703,7 +707,7 @@ fn beforeMove(
|
|
|
703
707
|
try options.chance.attacking(player, if (volatiles.attacks == 0) .ended else .continuing);
|
|
704
708
|
|
|
705
709
|
if (volatiles.attacks == 0) {
|
|
706
|
-
const overwritten =
|
|
710
|
+
const overwritten = volatiles.Confusion;
|
|
707
711
|
|
|
708
712
|
volatiles.Thrashing = false;
|
|
709
713
|
volatiles.Confusion = true;
|
|
@@ -714,7 +718,11 @@ fn beforeMove(
|
|
|
714
718
|
// can be expected to recognize this pattern and pass the information only to the
|
|
715
719
|
// correct player)
|
|
716
720
|
try log.start(.{ battle.active(player), .ConfusionSilent });
|
|
717
|
-
options.chance.observe(.confusion, player, if (overwritten)
|
|
721
|
+
options.chance.observe(.confusion, player, if (overwritten)
|
|
722
|
+
if (pkmn.options.overwrite) .overwritten else .continuing
|
|
723
|
+
else
|
|
724
|
+
.started);
|
|
725
|
+
if (!overwritten) options.calc.confusion(player);
|
|
718
726
|
}
|
|
719
727
|
|
|
720
728
|
// This shouldn't actually set last_used_move, but Pokémon Showdown sets last
|
|
@@ -780,9 +788,14 @@ fn canMove(
|
|
|
780
788
|
|
|
781
789
|
if (move.effect == .Thrashing) {
|
|
782
790
|
Effects.thrashing(battle, player, options);
|
|
783
|
-
} else if (
|
|
784
|
-
// Pokémon Showdown handles this after hit/miss checks and damage calculation
|
|
785
|
-
|
|
791
|
+
} else if (move.effect == .Binding) {
|
|
792
|
+
// Pokémon Showdown handles this after hit/miss checks and damage calculation, though clears
|
|
793
|
+
// Recharging in onTryMove (required for the Hyper Beam automatic selection glitch)
|
|
794
|
+
if (showdown) {
|
|
795
|
+
battle.foe(player).active.volatiles.Recharging = false;
|
|
796
|
+
} else {
|
|
797
|
+
try Effects.binding(battle, player, false, options);
|
|
798
|
+
}
|
|
786
799
|
}
|
|
787
800
|
|
|
788
801
|
return true;
|
|
@@ -1042,22 +1055,20 @@ fn doMove(
|
|
|
1042
1055
|
try Effects.poison(battle, player, Move.get(.PoisonSting), options);
|
|
1043
1056
|
}
|
|
1044
1057
|
try log.hitcount(.{ battle.active(player.foe()), hit });
|
|
1045
|
-
} else if (showdown) {
|
|
1058
|
+
} else if (showdown and move.effect == .Binding) {
|
|
1046
1059
|
// This should be handled much earlier but Pokémon Showdown does it here... ¯\_(ツ)_/¯
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
try buildRage(battle, player.foe(), options);
|
|
1058
|
-
}
|
|
1059
|
-
return null;
|
|
1060
|
+
try Effects.binding(battle, player, rewrap, options);
|
|
1061
|
+
if (immune) {
|
|
1062
|
+
battle.last_damage = 0;
|
|
1063
|
+
assert(foe.stored().hp > 0);
|
|
1064
|
+
// Pokémon Showdown logs |-damage| here instead of |-immune| because logic...
|
|
1065
|
+
if (sub) {
|
|
1066
|
+
try log.activate(.{ battle.active(player.foe()), .Substitute });
|
|
1067
|
+
} else {
|
|
1068
|
+
try log.damage(.{ battle.active(player.foe()), foe.stored(), .None });
|
|
1069
|
+
try buildRage(battle, player.foe(), options);
|
|
1060
1070
|
}
|
|
1071
|
+
return null;
|
|
1061
1072
|
}
|
|
1062
1073
|
}
|
|
1063
1074
|
|
|
@@ -1138,7 +1149,7 @@ fn calcDamage(
|
|
|
1138
1149
|
if ((!showdown or !cfz) and (atk > 255 or def > 255)) {
|
|
1139
1150
|
atk = @max((atk / 4) & 255, 1);
|
|
1140
1151
|
// GLITCH: not adjusted to be a min of 1 on cartridge (can lead to division-by-zero freeze)
|
|
1141
|
-
def = @max((def / 4) & 255, if (
|
|
1152
|
+
def = @max((def / 4) & 255, if (pkmn.options.mod) 1 else 0);
|
|
1142
1153
|
}
|
|
1143
1154
|
|
|
1144
1155
|
const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
|
|
@@ -1159,7 +1170,7 @@ fn calcDamage(
|
|
|
1159
1170
|
// encountered so we need to make sure that in the event of a miss we commit the status of the
|
|
1160
1171
|
// crit roll. The alternative to doing this extra work is to *always* commit the crit roll but
|
|
1161
1172
|
// that results in counter intuitive probabilities in the common case
|
|
1162
|
-
if (pkmn.options.chance and !
|
|
1173
|
+
if (pkmn.options.chance and !pkmn.options.mod) {
|
|
1163
1174
|
atk = attack(side, !crit, special);
|
|
1164
1175
|
def = defense(target, !crit, special, false);
|
|
1165
1176
|
if ((atk > 255 or def > 255) and (def / 4) & 255 == 0) options.chance.glitch();
|
|
@@ -1244,13 +1255,13 @@ fn specialDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1244
1255
|
.Psywave => power: {
|
|
1245
1256
|
const max: u8 = @intCast(@as(u16, side.stored().level) * 3 / 2);
|
|
1246
1257
|
// GLITCH: Psywave infinite glitch loop
|
|
1247
|
-
if (!
|
|
1258
|
+
if (!pkmn.options.mod and max <= 1) return Result.Error;
|
|
1248
1259
|
break :power try Rolls.psywave(battle, player, max, options);
|
|
1249
1260
|
},
|
|
1250
1261
|
else => unreachable,
|
|
1251
1262
|
};
|
|
1252
1263
|
|
|
1253
|
-
if (battle.last_damage == 0) return if (
|
|
1264
|
+
if (battle.last_damage == 0) return if (pkmn.options.mod) null else Result.Error;
|
|
1254
1265
|
|
|
1255
1266
|
const sub = showdown and foe.active.volatiles.Substitute;
|
|
1256
1267
|
_ = try applyDamage(battle, player.foe(), player.foe(), .None, options);
|
|
@@ -1287,12 +1298,16 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1287
1298
|
|
|
1288
1299
|
if (!used or !selected) {
|
|
1289
1300
|
// GLITCH: Counter desync (covered by Desync Clause Mod on Pokémon Showdown)
|
|
1290
|
-
if (!
|
|
1301
|
+
if (!pkmn.options.mod) return Result.Error;
|
|
1291
1302
|
try options.log.fail(.{ battle.active(player), .None });
|
|
1292
1303
|
return null;
|
|
1293
1304
|
}
|
|
1294
1305
|
|
|
1295
|
-
|
|
1306
|
+
// NOTE: llvm/llvm-project#58557
|
|
1307
|
+
battle.last_damage = if (comptime builtin.target.isWasm())
|
|
1308
|
+
@max(battle.last_damage *% 2, std.math.maxInt(u16))
|
|
1309
|
+
else
|
|
1310
|
+
battle.last_damage *| 2;
|
|
1296
1311
|
|
|
1297
1312
|
// Pokémon Showdown calls moveHit before Counter
|
|
1298
1313
|
if (!showdown and !try checkHit(battle, player, move, options)) return null;
|
|
@@ -1534,7 +1549,7 @@ fn faint(battle: anytype, player: Player, done: bool, cap: bool, options: anytyp
|
|
|
1534
1549
|
assert(!foe_volatiles.MultiHit);
|
|
1535
1550
|
if (foe_volatiles.Bide) {
|
|
1536
1551
|
assert(!foe_volatiles.Thrashing and !foe_volatiles.Rage);
|
|
1537
|
-
foe_volatiles.state = if (
|
|
1552
|
+
foe_volatiles.state = if (pkmn.options.mod) 0 else foe_volatiles.state & 255;
|
|
1538
1553
|
if (foe_volatiles.state != 0) return Result.Error;
|
|
1539
1554
|
}
|
|
1540
1555
|
|
|
@@ -1625,12 +1640,12 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
|
|
|
1625
1640
|
|
|
1626
1641
|
battle.turn += 1;
|
|
1627
1642
|
|
|
1628
|
-
if (
|
|
1643
|
+
if (pkmn.options.mod and pkmn.options.ebc and checkEBC(battle)) {
|
|
1629
1644
|
try options.log.tie(.{});
|
|
1630
1645
|
return Result.Tie;
|
|
1631
1646
|
}
|
|
1632
1647
|
|
|
1633
|
-
if (
|
|
1648
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1634
1649
|
try options.log.tie(.{});
|
|
1635
1650
|
return Result.Tie;
|
|
1636
1651
|
} else if (battle.turn >= 65535) {
|
|
@@ -1879,6 +1894,7 @@ pub const Effects = struct {
|
|
|
1879
1894
|
|
|
1880
1895
|
foe.active.volatiles.confusion = Rolls.confusionDuration(battle, player, options);
|
|
1881
1896
|
options.chance.observe(.confusion, player.foe(), .started);
|
|
1897
|
+
options.calc.confusion(player.foe());
|
|
1882
1898
|
|
|
1883
1899
|
try options.log.start(.{ battle.active(player.foe()), .Confusion });
|
|
1884
1900
|
}
|
|
@@ -1924,12 +1940,11 @@ pub const Effects = struct {
|
|
|
1924
1940
|
|
|
1925
1941
|
// Technically this is still considered simply a "miss" on the cartridge,
|
|
1926
1942
|
// but diverging from Pokémon Showdown here would mostly just be pedantic
|
|
1927
|
-
if (n == 0) {
|
|
1943
|
+
if (n == 0 or (pkmn.options.mod and err)) {
|
|
1928
1944
|
try options.log.fail(.{ foe_ident, .None });
|
|
1929
1945
|
return null;
|
|
1930
1946
|
} else if (err) {
|
|
1931
1947
|
// GLITCH: Transform + Mirror Move / Metronome PP softlock
|
|
1932
|
-
assert(!showdown);
|
|
1933
1948
|
return Result.Error;
|
|
1934
1949
|
}
|
|
1935
1950
|
|
|
@@ -2007,7 +2022,7 @@ pub const Effects = struct {
|
|
|
2007
2022
|
if (foe.active.types.includes(move.type)) return;
|
|
2008
2023
|
if (!try Rolls.secondaryChance(battle, player, true, options)) return;
|
|
2009
2024
|
// Freeze Clause Mod
|
|
2010
|
-
if (
|
|
2025
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
2011
2026
|
|
|
2012
2027
|
foe_stored.status = Status.init(.FRZ);
|
|
2013
2028
|
// GLITCH: Hyper Beam recharging status is not cleared
|
|
@@ -2348,7 +2363,7 @@ pub const Effects = struct {
|
|
|
2348
2363
|
}
|
|
2349
2364
|
|
|
2350
2365
|
// Sleep Clause Mod
|
|
2351
|
-
if (
|
|
2366
|
+
if (pkmn.options.mod) {
|
|
2352
2367
|
for (foe.pokemon) |p| {
|
|
2353
2368
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2354
2369
|
}
|
|
@@ -2454,7 +2469,7 @@ pub const Effects = struct {
|
|
|
2454
2469
|
if (side.active.volatiles.Binding) return;
|
|
2455
2470
|
side.active.volatiles.Binding = true;
|
|
2456
2471
|
// GLITCH: Hyper Beam automatic selection glitch if Recharging gets cleared on miss
|
|
2457
|
-
// (Pokémon Showdown
|
|
2472
|
+
// (Pokémon Showdown clears Recharging in onTryMove instead)
|
|
2458
2473
|
if (!showdown) {
|
|
2459
2474
|
foe.active.volatiles.Recharging = false;
|
|
2460
2475
|
} else if (foe.stored().hp == 0) return if (!rewrap) battle.rng.advance(1);
|
|
@@ -2951,9 +2966,10 @@ pub const Rolls = struct {
|
|
|
2951
2966
|
}
|
|
2952
2967
|
|
|
2953
2968
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2954
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2955
|
-
@intCast(val)
|
|
2956
|
-
else
|
|
2969
|
+
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
2970
|
+
if (val >= 1 and val <= 7) @as(u3, @intCast(val)) else null
|
|
2971
|
+
else
|
|
2972
|
+
null) orelse if (@hasDecl(@TypeOf(battle.rng), "sleepDuration"))
|
|
2957
2973
|
battle.rng.sleepDuration(player)
|
|
2958
2974
|
else if (showdown)
|
|
2959
2975
|
battle.rng.range(u3, 1, 8)
|
|
@@ -2970,9 +2986,10 @@ pub const Rolls = struct {
|
|
|
2970
2986
|
}
|
|
2971
2987
|
|
|
2972
2988
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
2973
|
-
const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
|
|
2974
|
-
val
|
|
2975
|
-
else
|
|
2989
|
+
const duration: u4 = (if (options.calc.overridden(player, .duration)) |val|
|
|
2990
|
+
if (val >= 1 and val <= 8) val else null
|
|
2991
|
+
else
|
|
2992
|
+
null) orelse if (@hasDecl(@TypeOf(battle.rng), "disableDuration"))
|
|
2976
2993
|
battle.rng.disableDuration(player)
|
|
2977
2994
|
else if (showdown)
|
|
2978
2995
|
battle.rng.range(u4, 1, 9)
|
|
@@ -2985,9 +3002,10 @@ pub const Rolls = struct {
|
|
|
2985
3002
|
}
|
|
2986
3003
|
|
|
2987
3004
|
fn confusionDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2988
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2989
|
-
@intCast(val)
|
|
2990
|
-
else
|
|
3005
|
+
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3006
|
+
if (val >= 2 and val <= 5) @as(u3, @intCast(val)) else null
|
|
3007
|
+
else
|
|
3008
|
+
null) orelse if (@hasDecl(@TypeOf(battle.rng), "confusionDuration"))
|
|
2991
3009
|
battle.rng.confusionDuration(player)
|
|
2992
3010
|
else if (showdown)
|
|
2993
3011
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3000,9 +3018,10 @@ pub const Rolls = struct {
|
|
|
3000
3018
|
}
|
|
3001
3019
|
|
|
3002
3020
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3003
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3004
|
-
@intCast(val)
|
|
3005
|
-
else
|
|
3021
|
+
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3022
|
+
if (val >= 2 and val <= 3) @as(u3, @intCast(val)) else null
|
|
3023
|
+
else
|
|
3024
|
+
null) orelse if (@hasDecl(@TypeOf(battle.rng), "attackingDuration"))
|
|
3006
3025
|
battle.rng.attackingDuration(player)
|
|
3007
3026
|
else if (showdown)
|
|
3008
3027
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3023,9 +3042,10 @@ pub const Rolls = struct {
|
|
|
3023
3042
|
options: anytype,
|
|
3024
3043
|
) !u3 {
|
|
3025
3044
|
const roll = if (effect == .Binding) .duration else .multi_hit;
|
|
3026
|
-
const n: u3 = if (options.calc.overridden(player, roll)) |val|
|
|
3027
|
-
@intCast(val)
|
|
3028
|
-
else
|
|
3045
|
+
const n: u3 = (if (options.calc.overridden(player, roll)) |val|
|
|
3046
|
+
if (val >= 2 and val <= 5) @as(u3, @intCast(val)) else null
|
|
3047
|
+
else
|
|
3048
|
+
null) orelse if (@hasDecl(@TypeOf(battle.rng), "distribution"))
|
|
3029
3049
|
battle.rng.distribution(player)
|
|
3030
3050
|
else if (showdown)
|
|
3031
3051
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3051,15 +3071,13 @@ pub const Rolls = struct {
|
|
|
3051
3071
|
options: anytype,
|
|
3052
3072
|
) !u4 {
|
|
3053
3073
|
// TODO: consider throwing error instead of rerolling?
|
|
3054
|
-
const
|
|
3074
|
+
const slot: u4 = (if (options.calc.overridden(player, .move_slot)) |val|
|
|
3055
3075
|
if (moves[val - 1].id != .None and (check_pp == 0 or moves[val - 1].pp > 0))
|
|
3056
3076
|
val
|
|
3057
3077
|
else
|
|
3058
3078
|
null
|
|
3059
3079
|
else
|
|
3060
|
-
null
|
|
3061
|
-
|
|
3062
|
-
const slot: u4 = overridden orelse if (@hasDecl(@TypeOf(battle.rng), "moveSlot"))
|
|
3080
|
+
null) orelse if (@hasDecl(@TypeOf(battle.rng), "moveSlot"))
|
|
3063
3081
|
battle.rng.moveSlot(player, moves, check_pp)
|
|
3064
3082
|
else slot: {
|
|
3065
3083
|
if (showdown) {
|
|
@@ -3217,7 +3235,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3217
3235
|
// Struggle (Pokémon Showdown would use 'move 1' here)
|
|
3218
3236
|
if (n == before) {
|
|
3219
3237
|
// GLITCH: Transform + Mirror Move / Metronome PP softlock
|
|
3220
|
-
if (!
|
|
3238
|
+
if (!pkmn.options.mod) {
|
|
3221
3239
|
while (slot <= 4) : (slot += 1) {
|
|
3222
3240
|
if (active.moves[slot - 1].pp != 0) return n;
|
|
3223
3241
|
}
|
package/src/lib/gen2/calc.zig
CHANGED
|
@@ -1,27 +1,19 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
4
|
-
|
|
1
|
+
const chance = @import("chance.zig");
|
|
5
2
|
const common = @import("../common/data.zig");
|
|
6
|
-
const util = @import("../common/util.zig");
|
|
7
|
-
|
|
8
3
|
const Optional = @import("../common/optional.zig").Optional;
|
|
4
|
+
const pkmn = @import("../pkmn.zig");
|
|
5
|
+
const std = @import("std");
|
|
6
|
+
const util = @import("../common/util.zig");
|
|
9
7
|
|
|
10
|
-
const
|
|
11
|
-
|
|
8
|
+
const Action = chance.Action;
|
|
9
|
+
const Actions = chance.Actions;
|
|
12
10
|
const assert = std.debug.assert;
|
|
13
|
-
|
|
11
|
+
const Criticals = chance.Criticals;
|
|
12
|
+
const Damages = chance.Damages;
|
|
14
13
|
const enabled = pkmn.options.calc;
|
|
15
|
-
|
|
14
|
+
const isPointerTo = util.isPointerTo;
|
|
16
15
|
const Player = common.Player;
|
|
17
|
-
|
|
18
16
|
const PointerType = util.PointerType;
|
|
19
|
-
const isPointerTo = util.isPointerTo;
|
|
20
|
-
|
|
21
|
-
const Actions = chance.Actions;
|
|
22
|
-
const Action = chance.Action;
|
|
23
|
-
const Criticals = chance.Criticals;
|
|
24
|
-
const Damages = chance.Damages;
|
|
25
17
|
|
|
26
18
|
const Int = if (@hasField(std.builtin.Type, "int")) .int else .Int;
|
|
27
19
|
const Enum = if (@hasField(std.builtin.Type, "enum")) .@"enum" else .Enum;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const data = @import("../../gen2/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Type = data.Type;
|
|
10
8
|
|
|
11
9
|
/// Representation of a held item in Generation II Pokémon.
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
|
-
const rng = @import("../../common/rng.zig");
|
|
6
3
|
const gen2 = @import("../../gen2/data.zig");
|
|
4
|
+
const rng = @import("../../common/rng.zig");
|
|
5
|
+
const std = @import("std");
|
|
7
6
|
|
|
8
7
|
const assert = std.debug.assert;
|
|
9
|
-
|
|
10
8
|
const percent = rng.Gen12.percent;
|
|
11
9
|
const Type = gen2.Type;
|
|
12
10
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen2 = @import("../../gen2/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
|
-
|
|
9
7
|
const Stats = gen2.Stats;
|
|
10
8
|
const Types = gen2.Types;
|
|
11
9
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const gen1 = @import("../../gen1/data.zig");
|
|
4
|
+
const std = @import("std");
|
|
6
5
|
|
|
7
6
|
const assert = std.debug.assert;
|
|
8
7
|
const Effectiveness = gen1.Effectiveness;
|
package/src/lib/gen2/data.zig
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
|
-
const pkmn = @import("../pkmn.zig");
|
|
5
|
-
|
|
6
|
-
const data = @import("../common/data.zig");
|
|
7
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
8
|
-
const protocol = @import("../common/protocol.zig");
|
|
9
|
-
const rng = @import("../common/rng.zig");
|
|
10
|
-
const util = @import("../common/util.zig");
|
|
11
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const calc = @import("calc.zig");
|
|
5
|
+
const chance = @import("chance.zig");
|
|
6
|
+
const data = @import("../common/data.zig");
|
|
7
|
+
const gen1 = @import("../gen1/data.zig");
|
|
12
8
|
const items = @import("data/items.zig");
|
|
9
|
+
const mechanics = @import("mechanics.zig");
|
|
13
10
|
const moves = @import("data/moves.zig");
|
|
11
|
+
const pkmn = @import("../pkmn.zig");
|
|
12
|
+
const protocol = @import("../common/protocol.zig");
|
|
13
|
+
const rng = @import("../common/rng.zig");
|
|
14
14
|
const species = @import("data/species.zig");
|
|
15
|
+
const std = @import("std");
|
|
15
16
|
const types = @import("data/types.zig");
|
|
16
|
-
|
|
17
|
-
const gen1 = @import("../gen1/data.zig");
|
|
18
|
-
|
|
19
|
-
const calc = @import("calc.zig");
|
|
20
|
-
const chance = @import("chance.zig");
|
|
21
|
-
const mechanics = @import("mechanics.zig");
|
|
17
|
+
const util = @import("../common/util.zig");
|
|
22
18
|
|
|
23
19
|
const assert = std.debug.assert;
|
|
24
20
|
const expectEqual = std.testing.expectEqual;
|
|
@@ -1,37 +1,32 @@
|
|
|
1
1
|
//! Code generated by `tools/generate` - manual edits will be overwritten.
|
|
2
2
|
|
|
3
|
-
const std = @import("std");
|
|
4
|
-
|
|
5
3
|
const common = @import("../common/data.zig");
|
|
6
|
-
|
|
7
4
|
const data = @import("data.zig");
|
|
8
5
|
const mechanics = @import("mechanics.zig");
|
|
6
|
+
const std = @import("std");
|
|
9
7
|
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const Effects = mechanics.Effects;
|
|
17
|
-
const State = mechanics.State;
|
|
18
|
-
|
|
19
|
-
const canMove = mechanics.canMove;
|
|
8
|
+
const adjustDamage = mechanics.adjustDamage;
|
|
9
|
+
const afterMove = mechanics.afterMove;
|
|
10
|
+
const applyDamage = mechanics.applyDamage;
|
|
11
|
+
const buildRage = mechanics.buildRage;
|
|
12
|
+
const calcDamage = mechanics.calcDamage;
|
|
20
13
|
const canCharge = mechanics.canCharge;
|
|
14
|
+
const canMove = mechanics.canMove;
|
|
15
|
+
const checkCriticalHit = mechanics.checkCriticalHit;
|
|
16
|
+
const checkHit = mechanics.checkHit;
|
|
21
17
|
const decrementPP = mechanics.decrementPP;
|
|
18
|
+
const destinyBond = mechanics.destinyBond;
|
|
22
19
|
const doMove = mechanics.doMove;
|
|
23
|
-
const checkHit = mechanics.checkHit;
|
|
24
|
-
const checkCriticalHit = mechanics.checkCriticalHit;
|
|
25
|
-
const calcDamage = mechanics.calcDamage;
|
|
26
|
-
const adjustDamage = mechanics.adjustDamage;
|
|
27
|
-
const randomizeDamage = mechanics.randomizeDamage;
|
|
28
|
-
const applyDamage = mechanics.applyDamage;
|
|
29
|
-
const reportOutcome = mechanics.reportOutcome;
|
|
30
20
|
const effectChance = mechanics.effectChance;
|
|
31
|
-
const
|
|
32
|
-
const
|
|
21
|
+
const Effectiveness = data.Effectiveness;
|
|
22
|
+
const Effects = mechanics.Effects;
|
|
33
23
|
const kingsRock = mechanics.kingsRock;
|
|
34
|
-
const
|
|
24
|
+
const Move = data.Move;
|
|
25
|
+
const Player = common.Player;
|
|
26
|
+
const randomizeDamage = mechanics.randomizeDamage;
|
|
27
|
+
const reportOutcome = mechanics.reportOutcome;
|
|
28
|
+
const Result = common.Result;
|
|
29
|
+
const State = mechanics.State;
|
|
35
30
|
|
|
36
31
|
const SECONDARY = true;
|
|
37
32
|
const KINGS = true;
|
package/src/lib/gen2/helpers.zig
CHANGED
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
1
|
const common = @import("../common/data.zig");
|
|
2
|
+
const data = @import("data.zig");
|
|
3
|
+
const gen1 = @import("../gen1/data.zig");
|
|
4
4
|
const options = @import("../common/options.zig");
|
|
5
5
|
const rng = @import("../common/rng.zig");
|
|
6
|
-
|
|
7
|
-
const gen1 = @import("../gen1/data.zig");
|
|
8
|
-
|
|
9
|
-
const data = @import("data.zig");
|
|
6
|
+
const std = @import("std");
|
|
10
7
|
|
|
11
8
|
const assert = std.debug.assert;
|
|
12
|
-
|
|
13
9
|
const Choice = common.Choice;
|
|
14
|
-
|
|
15
|
-
const showdown = options.showdown;
|
|
16
|
-
|
|
17
|
-
const PSRNG = rng.PSRNG;
|
|
18
|
-
|
|
19
10
|
const DVs = data.DVs;
|
|
20
11
|
const Item = data.Item;
|
|
21
12
|
const Move = data.Move;
|
|
22
13
|
const MoveSlot = data.MoveSlot;
|
|
14
|
+
const PSRNG = rng.PSRNG;
|
|
15
|
+
const showdown = options.showdown;
|
|
23
16
|
const Species = data.Species;
|
|
24
17
|
const Stats = data.Stats;
|
|
25
18
|
|