@pkmn/engine 0.1.0-dev.f4ba6ebd → 0.1.0-dev.f6545544
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 +3 -0
- package/build/pkg/addon/browser.js +47 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +110 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +25 -3
- package/build/pkg/addon.js +34 -65
- 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/data.d.ts +23 -1
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +39 -89
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +105 -103
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +52 -76
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +152 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +359 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +33 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +196 -84
- package/package.json +51 -135
- package/src/bin/install-pkmn-engine +3 -2
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +255 -0
- package/src/lib/bindings/node.zig +121 -0
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +1 -1
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/optional.zig +1 -1
- package/src/lib/common/options.zig +4 -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/common/util.zig +1 -1
- package/src/lib/gen1/calc.zig +208 -61
- package/src/lib/gen1/chance.zig +40 -22
- 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 +9 -13
- package/src/lib/gen1/helpers.zig +5 -21
- package/src/lib/gen1/mechanics.zig +107 -82
- package/src/lib/gen2/calc.zig +9 -17
- package/src/lib/gen2/chance.zig +0 -3
- 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 -16
- package/src/lib/gen2/mechanics.zig +41 -44
- package/src/lib/node.zig +3 -116
- package/src/lib/pkmn.zig +7 -2
- package/src/lib/wasm.zig +13 -9
- package/src/tools/display/dom.ts +95 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -405
- package/build/test/display.js.map +0 -1
- package/src/test/display/pkmn.html.tmpl +0 -368
package/src/lib/gen1/helpers.zig
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
1
|
const builtin = @import("builtin");
|
|
3
|
-
|
|
4
2
|
const common = @import("../common/data.zig");
|
|
3
|
+
const data = @import("data.zig");
|
|
5
4
|
const options = @import("../common/options.zig");
|
|
6
5
|
const protocol = @import("../common/protocol.zig");
|
|
7
6
|
const rng = @import("../common/rng.zig");
|
|
7
|
+
const std = @import("std");
|
|
8
8
|
const util = @import("../common/util.zig");
|
|
9
9
|
|
|
10
|
-
const data = @import("data.zig");
|
|
11
|
-
|
|
12
10
|
const assert = std.debug.assert;
|
|
13
|
-
|
|
14
11
|
const Choice = common.Choice;
|
|
15
|
-
const Player = common.Player;
|
|
16
|
-
|
|
17
|
-
const showdown = options.showdown;
|
|
18
|
-
const chance = options.chance;
|
|
19
|
-
|
|
20
|
-
const PSRNG = rng.PSRNG;
|
|
21
|
-
|
|
22
12
|
const DVs = data.DVs;
|
|
23
13
|
const Move = data.Move;
|
|
24
14
|
const MoveSlot = data.MoveSlot;
|
|
15
|
+
const PSRNG = rng.PSRNG;
|
|
16
|
+
const showdown = options.showdown;
|
|
25
17
|
const Species = data.Species;
|
|
26
18
|
const Stats = data.Stats;
|
|
27
|
-
const Status = data.Status;
|
|
28
19
|
|
|
29
20
|
const endian = builtin.cpu.arch.endian();
|
|
30
21
|
|
|
@@ -269,14 +260,7 @@ fn blocked(m: Move) bool {
|
|
|
269
260
|
fn durations(m: Move) bool {
|
|
270
261
|
// Any moves that have effects that result in duration tracking with -Dchance
|
|
271
262
|
return switch (Move.get(m).effect) {
|
|
272
|
-
.
|
|
273
|
-
.Bide,
|
|
274
|
-
.Sleep,
|
|
275
|
-
.Rage,
|
|
276
|
-
.Binding,
|
|
277
|
-
.Thrashing,
|
|
278
|
-
.ConfusionChance,
|
|
279
|
-
.Disable,
|
|
263
|
+
.Haze,
|
|
280
264
|
.Metronome,
|
|
281
265
|
=> true,
|
|
282
266
|
else => false,
|
|
@@ -1,39 +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
|
|
7
|
-
const
|
|
6
|
+
const data = @import("data.zig");
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
8
8
|
const protocol = @import("../common/protocol.zig");
|
|
9
9
|
const rng = @import("../common/rng.zig");
|
|
10
|
-
|
|
11
|
-
const data = @import("data.zig");
|
|
12
|
-
const chance = @import("chance.zig");
|
|
10
|
+
const std = @import("std");
|
|
13
11
|
|
|
14
12
|
const assert = std.debug.assert;
|
|
15
|
-
|
|
16
|
-
const expectEqual = std.testing.expectEqual;
|
|
17
|
-
|
|
18
|
-
const showdown = pkmn.options.showdown;
|
|
19
|
-
|
|
20
|
-
const Choice = common.Choice;
|
|
21
|
-
const ID = common.ID;
|
|
22
|
-
const Player = common.Player;
|
|
23
|
-
const Result = common.Result;
|
|
24
|
-
|
|
25
13
|
const Boost = protocol.Boost;
|
|
14
|
+
const Choice = common.Choice;
|
|
26
15
|
const Damage = protocol.Damage;
|
|
16
|
+
const Effectiveness = data.Effectiveness;
|
|
17
|
+
const expectEqual = std.testing.expectEqual;
|
|
27
18
|
const Fail = protocol.Fail;
|
|
28
|
-
const Heal = protocol.Heal;
|
|
29
|
-
|
|
30
19
|
const Gen12 = rng.Gen12;
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const Effectiveness = data.Effectiveness;
|
|
20
|
+
const Heal = protocol.Heal;
|
|
21
|
+
const ID = common.ID;
|
|
34
22
|
const Move = data.Move;
|
|
35
23
|
const MoveSlot = data.MoveSlot;
|
|
36
|
-
const
|
|
24
|
+
const Player = common.Player;
|
|
25
|
+
const Result = common.Result;
|
|
26
|
+
const showdown = pkmn.options.showdown;
|
|
37
27
|
const Side = data.Side;
|
|
38
28
|
const Species = data.Species;
|
|
39
29
|
const Stats = data.Stats;
|
|
@@ -176,7 +166,10 @@ fn selectMove(
|
|
|
176
166
|
if (foe_choice.type == .Switch) {
|
|
177
167
|
const last = side.active.move(battle.lastMove(player).index);
|
|
178
168
|
if (last.id == .Metronome) side.last_selected_move = last.id;
|
|
179
|
-
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
|
+
}
|
|
180
173
|
}
|
|
181
174
|
}
|
|
182
175
|
return null;
|
|
@@ -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);
|
|
@@ -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(mslot).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;
|
|
@@ -557,18 +569,17 @@ fn beforeMove(
|
|
|
557
569
|
// This can only happen if a Pokémon started the battle frozen/sleeping and was thawed/woken
|
|
558
570
|
// before the side had a selected a move - we simply need to assume this leads to a desync
|
|
559
571
|
if (side.last_selected_move == .None) {
|
|
560
|
-
assert(!
|
|
572
|
+
assert(!pkmn.options.mod);
|
|
561
573
|
return .err;
|
|
562
574
|
}
|
|
563
575
|
|
|
564
576
|
if (volatiles.Confusion) {
|
|
565
577
|
assert(volatiles.confusion > 0);
|
|
566
578
|
if (options.calc.overridden(player, .confusion)) |obs| switch (obs) {
|
|
567
|
-
.started => volatiles.confusion
|
|
579
|
+
.started, .ended => volatiles.confusion = 0,
|
|
568
580
|
.continuing => if (volatiles.confusion > 1) {
|
|
569
581
|
volatiles.confusion -= 1;
|
|
570
582
|
},
|
|
571
|
-
.ended => volatiles.confusion = 0,
|
|
572
583
|
.overwritten => {},
|
|
573
584
|
else => unreachable,
|
|
574
585
|
} else {
|
|
@@ -707,7 +718,11 @@ fn beforeMove(
|
|
|
707
718
|
// can be expected to recognize this pattern and pass the information only to the
|
|
708
719
|
// correct player)
|
|
709
720
|
try log.start(.{ battle.active(player), .ConfusionSilent });
|
|
710
|
-
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);
|
|
711
726
|
}
|
|
712
727
|
|
|
713
728
|
// This shouldn't actually set last_used_move, but Pokémon Showdown sets last
|
|
@@ -773,9 +788,14 @@ fn canMove(
|
|
|
773
788
|
|
|
774
789
|
if (move.effect == .Thrashing) {
|
|
775
790
|
Effects.thrashing(battle, player, options);
|
|
776
|
-
} else if (
|
|
777
|
-
// Pokémon Showdown handles this after hit/miss checks and damage calculation
|
|
778
|
-
|
|
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
|
+
}
|
|
779
799
|
}
|
|
780
800
|
|
|
781
801
|
return true;
|
|
@@ -1035,22 +1055,20 @@ fn doMove(
|
|
|
1035
1055
|
try Effects.poison(battle, player, Move.get(.PoisonSting), options);
|
|
1036
1056
|
}
|
|
1037
1057
|
try log.hitcount(.{ battle.active(player.foe()), hit });
|
|
1038
|
-
} else if (showdown) {
|
|
1058
|
+
} else if (showdown and move.effect == .Binding) {
|
|
1039
1059
|
// This should be handled much earlier but Pokémon Showdown does it here... ¯\_(ツ)_/¯
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
try buildRage(battle, player.foe(), options);
|
|
1051
|
-
}
|
|
1052
|
-
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);
|
|
1053
1070
|
}
|
|
1071
|
+
return null;
|
|
1054
1072
|
}
|
|
1055
1073
|
}
|
|
1056
1074
|
|
|
@@ -1131,7 +1149,7 @@ fn calcDamage(
|
|
|
1131
1149
|
if ((!showdown or !cfz) and (atk > 255 or def > 255)) {
|
|
1132
1150
|
atk = @max((atk / 4) & 255, 1);
|
|
1133
1151
|
// GLITCH: not adjusted to be a min of 1 on cartridge (can lead to division-by-zero freeze)
|
|
1134
|
-
def = @max((def / 4) & 255, if (
|
|
1152
|
+
def = @max((def / 4) & 255, if (pkmn.options.mod) 1 else 0);
|
|
1135
1153
|
}
|
|
1136
1154
|
|
|
1137
1155
|
const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
|
|
@@ -1152,7 +1170,7 @@ fn calcDamage(
|
|
|
1152
1170
|
// encountered so we need to make sure that in the event of a miss we commit the status of the
|
|
1153
1171
|
// crit roll. The alternative to doing this extra work is to *always* commit the crit roll but
|
|
1154
1172
|
// that results in counter intuitive probabilities in the common case
|
|
1155
|
-
if (pkmn.options.chance and !
|
|
1173
|
+
if (pkmn.options.chance and !pkmn.options.mod and move.effect != .Explode) {
|
|
1156
1174
|
atk = attack(side, !crit, special);
|
|
1157
1175
|
def = defense(target, !crit, special, false);
|
|
1158
1176
|
if ((atk > 255 or def > 255) and (def / 4) & 255 == 0) options.chance.glitch();
|
|
@@ -1237,13 +1255,13 @@ fn specialDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1237
1255
|
.Psywave => power: {
|
|
1238
1256
|
const max: u8 = @intCast(@as(u16, side.stored().level) * 3 / 2);
|
|
1239
1257
|
// GLITCH: Psywave infinite glitch loop
|
|
1240
|
-
if (!
|
|
1258
|
+
if (!pkmn.options.mod and max <= 1) return Result.Error;
|
|
1241
1259
|
break :power try Rolls.psywave(battle, player, max, options);
|
|
1242
1260
|
},
|
|
1243
1261
|
else => unreachable,
|
|
1244
1262
|
};
|
|
1245
1263
|
|
|
1246
|
-
if (battle.last_damage == 0) return if (
|
|
1264
|
+
if (battle.last_damage == 0) return if (pkmn.options.mod) null else Result.Error;
|
|
1247
1265
|
|
|
1248
1266
|
const sub = showdown and foe.active.volatiles.Substitute;
|
|
1249
1267
|
_ = try applyDamage(battle, player.foe(), player.foe(), .None, options);
|
|
@@ -1280,12 +1298,16 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1280
1298
|
|
|
1281
1299
|
if (!used or !selected) {
|
|
1282
1300
|
// GLITCH: Counter desync (covered by Desync Clause Mod on Pokémon Showdown)
|
|
1283
|
-
if (!
|
|
1301
|
+
if (!pkmn.options.mod) return Result.Error;
|
|
1284
1302
|
try options.log.fail(.{ battle.active(player), .None });
|
|
1285
1303
|
return null;
|
|
1286
1304
|
}
|
|
1287
1305
|
|
|
1288
|
-
|
|
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;
|
|
1289
1311
|
|
|
1290
1312
|
// Pokémon Showdown calls moveHit before Counter
|
|
1291
1313
|
if (!showdown and !try checkHit(battle, player, move, options)) return null;
|
|
@@ -1317,7 +1339,7 @@ fn applyDamage(
|
|
|
1317
1339
|
subbed.active.volatiles.Substitute = false;
|
|
1318
1340
|
// battle.last_damage is not updated with the amount of HP the Substitute had
|
|
1319
1341
|
try options.log.end(.{ battle.active(sub_player), .Substitute });
|
|
1320
|
-
options.calc.capped(sub_player);
|
|
1342
|
+
if (reason != .Confusion) options.calc.capped(sub_player);
|
|
1321
1343
|
return true;
|
|
1322
1344
|
} else {
|
|
1323
1345
|
// Safe to truncate since less than subbed.volatiles.substitute which is a u8
|
|
@@ -1527,7 +1549,7 @@ fn faint(battle: anytype, player: Player, done: bool, cap: bool, options: anytyp
|
|
|
1527
1549
|
assert(!foe_volatiles.MultiHit);
|
|
1528
1550
|
if (foe_volatiles.Bide) {
|
|
1529
1551
|
assert(!foe_volatiles.Thrashing and !foe_volatiles.Rage);
|
|
1530
|
-
foe_volatiles.state = if (
|
|
1552
|
+
foe_volatiles.state = if (pkmn.options.mod) 0 else foe_volatiles.state & 255;
|
|
1531
1553
|
if (foe_volatiles.state != 0) return Result.Error;
|
|
1532
1554
|
}
|
|
1533
1555
|
|
|
@@ -1618,12 +1640,12 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
|
|
|
1618
1640
|
|
|
1619
1641
|
battle.turn += 1;
|
|
1620
1642
|
|
|
1621
|
-
if (
|
|
1643
|
+
if (pkmn.options.mod and pkmn.options.ebc and checkEBC(battle)) {
|
|
1622
1644
|
try options.log.tie(.{});
|
|
1623
1645
|
return Result.Tie;
|
|
1624
1646
|
}
|
|
1625
1647
|
|
|
1626
|
-
if (
|
|
1648
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1627
1649
|
try options.log.tie(.{});
|
|
1628
1650
|
return Result.Tie;
|
|
1629
1651
|
} else if (battle.turn >= 65535) {
|
|
@@ -1872,6 +1894,7 @@ pub const Effects = struct {
|
|
|
1872
1894
|
|
|
1873
1895
|
foe.active.volatiles.confusion = Rolls.confusionDuration(battle, player, options);
|
|
1874
1896
|
options.chance.observe(.confusion, player.foe(), .started);
|
|
1897
|
+
options.calc.confusion(player.foe());
|
|
1875
1898
|
|
|
1876
1899
|
try options.log.start(.{ battle.active(player.foe()), .Confusion });
|
|
1877
1900
|
}
|
|
@@ -1917,12 +1940,11 @@ pub const Effects = struct {
|
|
|
1917
1940
|
|
|
1918
1941
|
// Technically this is still considered simply a "miss" on the cartridge,
|
|
1919
1942
|
// but diverging from Pokémon Showdown here would mostly just be pedantic
|
|
1920
|
-
if (n == 0) {
|
|
1943
|
+
if (n == 0 or (pkmn.options.mod and err)) {
|
|
1921
1944
|
try options.log.fail(.{ foe_ident, .None });
|
|
1922
1945
|
return null;
|
|
1923
1946
|
} else if (err) {
|
|
1924
1947
|
// GLITCH: Transform + Mirror Move / Metronome PP softlock
|
|
1925
|
-
assert(!showdown);
|
|
1926
1948
|
return Result.Error;
|
|
1927
1949
|
}
|
|
1928
1950
|
|
|
@@ -2000,7 +2022,7 @@ pub const Effects = struct {
|
|
|
2000
2022
|
if (foe.active.types.includes(move.type)) return;
|
|
2001
2023
|
if (!try Rolls.secondaryChance(battle, player, true, options)) return;
|
|
2002
2024
|
// Freeze Clause Mod
|
|
2003
|
-
if (
|
|
2025
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
2004
2026
|
|
|
2005
2027
|
foe_stored.status = Status.init(.FRZ);
|
|
2006
2028
|
// GLITCH: Hyper Beam recharging status is not cleared
|
|
@@ -2341,7 +2363,7 @@ pub const Effects = struct {
|
|
|
2341
2363
|
}
|
|
2342
2364
|
|
|
2343
2365
|
// Sleep Clause Mod
|
|
2344
|
-
if (
|
|
2366
|
+
if (pkmn.options.mod) {
|
|
2345
2367
|
for (foe.pokemon) |p| {
|
|
2346
2368
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2347
2369
|
}
|
|
@@ -2447,7 +2469,7 @@ pub const Effects = struct {
|
|
|
2447
2469
|
if (side.active.volatiles.Binding) return;
|
|
2448
2470
|
side.active.volatiles.Binding = true;
|
|
2449
2471
|
// GLITCH: Hyper Beam automatic selection glitch if Recharging gets cleared on miss
|
|
2450
|
-
// (Pokémon Showdown
|
|
2472
|
+
// (Pokémon Showdown clears Recharging in onTryMove instead)
|
|
2451
2473
|
if (!showdown) {
|
|
2452
2474
|
foe.active.volatiles.Recharging = false;
|
|
2453
2475
|
} else if (foe.stored().hp == 0) return if (!rewrap) battle.rng.advance(1);
|
|
@@ -2944,9 +2966,10 @@ pub const Rolls = struct {
|
|
|
2944
2966
|
}
|
|
2945
2967
|
|
|
2946
2968
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2947
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2948
|
-
@intCast(val)
|
|
2949
|
-
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"))
|
|
2950
2973
|
battle.rng.sleepDuration(player)
|
|
2951
2974
|
else if (showdown)
|
|
2952
2975
|
battle.rng.range(u3, 1, 8)
|
|
@@ -2963,9 +2986,10 @@ pub const Rolls = struct {
|
|
|
2963
2986
|
}
|
|
2964
2987
|
|
|
2965
2988
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
2966
|
-
const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
|
|
2967
|
-
val
|
|
2968
|
-
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"))
|
|
2969
2993
|
battle.rng.disableDuration(player)
|
|
2970
2994
|
else if (showdown)
|
|
2971
2995
|
battle.rng.range(u4, 1, 9)
|
|
@@ -2978,9 +3002,10 @@ pub const Rolls = struct {
|
|
|
2978
3002
|
}
|
|
2979
3003
|
|
|
2980
3004
|
fn confusionDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2981
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2982
|
-
@intCast(val)
|
|
2983
|
-
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"))
|
|
2984
3009
|
battle.rng.confusionDuration(player)
|
|
2985
3010
|
else if (showdown)
|
|
2986
3011
|
battle.rng.range(u3, 2, 6)
|
|
@@ -2993,9 +3018,10 @@ pub const Rolls = struct {
|
|
|
2993
3018
|
}
|
|
2994
3019
|
|
|
2995
3020
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2996
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2997
|
-
@intCast(val)
|
|
2998
|
-
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"))
|
|
2999
3025
|
battle.rng.attackingDuration(player)
|
|
3000
3026
|
else if (showdown)
|
|
3001
3027
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3016,9 +3042,10 @@ pub const Rolls = struct {
|
|
|
3016
3042
|
options: anytype,
|
|
3017
3043
|
) !u3 {
|
|
3018
3044
|
const roll = if (effect == .Binding) .duration else .multi_hit;
|
|
3019
|
-
const n: u3 = if (options.calc.overridden(player, roll)) |val|
|
|
3020
|
-
@intCast(val)
|
|
3021
|
-
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"))
|
|
3022
3049
|
battle.rng.distribution(player)
|
|
3023
3050
|
else if (showdown)
|
|
3024
3051
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3044,15 +3071,13 @@ pub const Rolls = struct {
|
|
|
3044
3071
|
options: anytype,
|
|
3045
3072
|
) !u4 {
|
|
3046
3073
|
// TODO: consider throwing error instead of rerolling?
|
|
3047
|
-
const
|
|
3074
|
+
const slot: u4 = (if (options.calc.overridden(player, .move_slot)) |val|
|
|
3048
3075
|
if (moves[val - 1].id != .None and (check_pp == 0 or moves[val - 1].pp > 0))
|
|
3049
3076
|
val
|
|
3050
3077
|
else
|
|
3051
3078
|
null
|
|
3052
3079
|
else
|
|
3053
|
-
null
|
|
3054
|
-
|
|
3055
|
-
const slot: u4 = overridden orelse if (@hasDecl(@TypeOf(battle.rng), "moveSlot"))
|
|
3080
|
+
null) orelse if (@hasDecl(@TypeOf(battle.rng), "moveSlot"))
|
|
3056
3081
|
battle.rng.moveSlot(player, moves, check_pp)
|
|
3057
3082
|
else slot: {
|
|
3058
3083
|
if (showdown) {
|
|
@@ -3210,7 +3235,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3210
3235
|
// Struggle (Pokémon Showdown would use 'move 1' here)
|
|
3211
3236
|
if (n == before) {
|
|
3212
3237
|
// GLITCH: Transform + Mirror Move / Metronome PP softlock
|
|
3213
|
-
if (!
|
|
3238
|
+
if (!pkmn.options.mod) {
|
|
3214
3239
|
while (slot <= 4) : (slot += 1) {
|
|
3215
3240
|
if (active.moves[slot - 1].pp != 0) return n;
|
|
3216
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;
|
package/src/lib/gen2/chance.zig
CHANGED
|
@@ -8,7 +8,6 @@ const expectEqual = std.testing.expectEqual;
|
|
|
8
8
|
|
|
9
9
|
const options = @import("../common/options.zig");
|
|
10
10
|
const rational = @import("../common/rational.zig");
|
|
11
|
-
const rng = @import("../common/rng.zig");
|
|
12
11
|
const util = @import("../common/util.zig");
|
|
13
12
|
|
|
14
13
|
const Array = @import("../common/array.zig").Array;
|
|
@@ -20,8 +19,6 @@ const data = @import("data.zig");
|
|
|
20
19
|
const enabled = options.chance;
|
|
21
20
|
const showdown = options.showdown;
|
|
22
21
|
|
|
23
|
-
const Gen12 = rng.Gen12;
|
|
24
|
-
|
|
25
22
|
const PointerType = util.PointerType;
|
|
26
23
|
const isPointerTo = util.isPointerTo;
|
|
27
24
|
|
|
@@ -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;
|