@pkmn/engine 0.1.0-dev.0cabf16e → 0.1.0-dev.0f9e9648
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 +1 -1
- package/README.md +47 -4
- package/build/pkg/addon/browser.d.ts +1 -1
- package/build/pkg/addon/browser.js +6 -2
- package/build/pkg/addon/browser.js.map +1 -1
- package/build/pkg/addon/node.js +22 -8
- package/build/pkg/addon/node.js.map +1 -1
- 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 +24 -4
- 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 +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +37 -100
- package/build/pkg/index.js +23 -75
- package/build/pkg/index.js.map +1 -1
- package/build/tools/debug.js +18 -9
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +18 -7
- package/build/tools/display/debug.jsx.map +1 -1
- package/build/tools/display/index.js +35 -15
- package/build/tools/display/index.js.map +1 -1
- package/build/tools/display/ui.jsx +17 -7
- package/build/tools/display/ui.jsx.map +1 -1
- package/build/tools/display/util.js +16 -1
- package/build/tools/display/util.js.map +1 -1
- package/build.zig +196 -84
- package/package.json +29 -127
- package/src/bin/install-pkmn-engine +3 -2
- 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/optional.zig +1 -1
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +4 -8
- package/src/lib/common/rational.zig +222 -147
- package/src/lib/common/rng.zig +1 -3
- package/src/lib/common/util.zig +17 -2
- package/src/lib/gen1/calc.zig +87 -97
- package/src/lib/gen1/chance.zig +34 -18
- 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 +4 -11
- package/src/lib/gen1/mechanics.zig +66 -50
- package/src/lib/gen2/calc.zig +10 -18
- 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 +3 -116
- package/src/lib/pkmn.zig +7 -2
- package/src/lib/wasm.zig +13 -9
- package/src/tools/display/dom.ts +5 -8
- package/src/tools/display/ui.css +0 -5
|
@@ -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;
|
|
@@ -424,7 +420,7 @@ fn executeMove(
|
|
|
424
420
|
mslot = @intCast(battle.lastMove(player).index);
|
|
425
421
|
const stored = side.stored();
|
|
426
422
|
// GLITCH: Struggle bypass PP underflow via Hyper Beam / Trapping-switch auto selection
|
|
427
|
-
auto = isForced(&side.active) or
|
|
423
|
+
auto = pkmn.options.mod or isForced(&side.active) or
|
|
428
424
|
side.active.volatiles.Binding or side.active.volatiles.Bide or
|
|
429
425
|
side.last_selected_move == .HyperBeam or
|
|
430
426
|
Status.is(stored.status, .FRZ) or Status.is(stored.status, .SLP);
|
|
@@ -457,6 +453,25 @@ fn executeMove(
|
|
|
457
453
|
.err => return @as(?Result, Result.Error),
|
|
458
454
|
}
|
|
459
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
|
+
|
|
460
475
|
const can = skip_can or
|
|
461
476
|
try canMove(battle, player, mslot, auto, skip_pp, .None, residual, options);
|
|
462
477
|
if (!can) return null;
|
|
@@ -554,7 +569,7 @@ fn beforeMove(
|
|
|
554
569
|
// This can only happen if a Pokémon started the battle frozen/sleeping and was thawed/woken
|
|
555
570
|
// before the side had a selected a move - we simply need to assume this leads to a desync
|
|
556
571
|
if (side.last_selected_move == .None) {
|
|
557
|
-
assert(!
|
|
572
|
+
assert(!pkmn.options.mod);
|
|
558
573
|
return .err;
|
|
559
574
|
}
|
|
560
575
|
|
|
@@ -603,17 +618,10 @@ fn beforeMove(
|
|
|
603
618
|
return .err;
|
|
604
619
|
}
|
|
605
620
|
}
|
|
606
|
-
// Pokémon Showdown incorrectly changes the "target" of the confusion self-hit based
|
|
607
|
-
// on the targeting behavior of the confused Pokémon's selected move which results
|
|
608
|
-
// in the wrong behavior with respect to the Substitute + Confusion glitch
|
|
609
|
-
const target = if (showdown and Move.get(side.last_selected_move).target == .Self)
|
|
610
|
-
player
|
|
611
|
-
else
|
|
612
|
-
player.foe();
|
|
613
621
|
|
|
614
622
|
const uncapped = battle.last_damage;
|
|
615
623
|
// Skipping adjustDamage / randomizeDamage / checkHit
|
|
616
|
-
_ = try applyDamage(battle, player,
|
|
624
|
+
_ = try applyDamage(battle, player, player.foe(), .Confusion, options);
|
|
617
625
|
// Pokémon Showdown thinks that confusion damage is uncapped ¯\_(ツ)_/¯
|
|
618
626
|
if (showdown) battle.last_damage = uncapped;
|
|
619
627
|
|
|
@@ -773,9 +781,14 @@ fn canMove(
|
|
|
773
781
|
|
|
774
782
|
if (move.effect == .Thrashing) {
|
|
775
783
|
Effects.thrashing(battle, player, options);
|
|
776
|
-
} else if (
|
|
777
|
-
// Pokémon Showdown handles this after hit/miss checks and damage calculation
|
|
778
|
-
|
|
784
|
+
} else if (move.effect == .Binding) {
|
|
785
|
+
// Pokémon Showdown handles this after hit/miss checks and damage calculation, though clears
|
|
786
|
+
// Recharging in onTryMove (required for the Hyper Beam automatic selection glitch)
|
|
787
|
+
if (showdown) {
|
|
788
|
+
battle.foe(player).active.volatiles.Recharging = false;
|
|
789
|
+
} else {
|
|
790
|
+
try Effects.binding(battle, player, false, options);
|
|
791
|
+
}
|
|
779
792
|
}
|
|
780
793
|
|
|
781
794
|
return true;
|
|
@@ -1129,7 +1142,7 @@ fn calcDamage(
|
|
|
1129
1142
|
if ((!showdown or !cfz) and (atk > 255 or def > 255)) {
|
|
1130
1143
|
atk = @max((atk / 4) & 255, 1);
|
|
1131
1144
|
// GLITCH: not adjusted to be a min of 1 on cartridge (can lead to division-by-zero freeze)
|
|
1132
|
-
def = @max((def / 4) & 255, if (
|
|
1145
|
+
def = @max((def / 4) & 255, if (pkmn.options.mod) 1 else 0);
|
|
1133
1146
|
}
|
|
1134
1147
|
|
|
1135
1148
|
const lvl: u32 = side.stored().level * @as(u2, if (crit) 2 else 1);
|
|
@@ -1150,7 +1163,7 @@ fn calcDamage(
|
|
|
1150
1163
|
// encountered so we need to make sure that in the event of a miss we commit the status of the
|
|
1151
1164
|
// crit roll. The alternative to doing this extra work is to *always* commit the crit roll but
|
|
1152
1165
|
// that results in counter intuitive probabilities in the common case
|
|
1153
|
-
if (pkmn.options.chance and !
|
|
1166
|
+
if (pkmn.options.chance and !pkmn.options.mod and move.effect != .Explode) {
|
|
1154
1167
|
atk = attack(side, !crit, special);
|
|
1155
1168
|
def = defense(target, !crit, special, false);
|
|
1156
1169
|
if ((atk > 255 or def > 255) and (def / 4) & 255 == 0) options.chance.glitch();
|
|
@@ -1235,13 +1248,13 @@ fn specialDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1235
1248
|
.Psywave => power: {
|
|
1236
1249
|
const max: u8 = @intCast(@as(u16, side.stored().level) * 3 / 2);
|
|
1237
1250
|
// GLITCH: Psywave infinite glitch loop
|
|
1238
|
-
if (!
|
|
1251
|
+
if (!pkmn.options.mod and max <= 1) return Result.Error;
|
|
1239
1252
|
break :power try Rolls.psywave(battle, player, max, options);
|
|
1240
1253
|
},
|
|
1241
1254
|
else => unreachable,
|
|
1242
1255
|
};
|
|
1243
1256
|
|
|
1244
|
-
if (battle.last_damage == 0) return if (
|
|
1257
|
+
if (battle.last_damage == 0) return if (pkmn.options.mod) null else Result.Error;
|
|
1245
1258
|
|
|
1246
1259
|
const sub = showdown and foe.active.volatiles.Substitute;
|
|
1247
1260
|
_ = try applyDamage(battle, player.foe(), player.foe(), .None, options);
|
|
@@ -1278,12 +1291,16 @@ fn counterDamage(battle: anytype, player: Player, move: Move.Data, options: anyt
|
|
|
1278
1291
|
|
|
1279
1292
|
if (!used or !selected) {
|
|
1280
1293
|
// GLITCH: Counter desync (covered by Desync Clause Mod on Pokémon Showdown)
|
|
1281
|
-
if (!
|
|
1294
|
+
if (!pkmn.options.mod) return Result.Error;
|
|
1282
1295
|
try options.log.fail(.{ battle.active(player), .None });
|
|
1283
1296
|
return null;
|
|
1284
1297
|
}
|
|
1285
1298
|
|
|
1286
|
-
|
|
1299
|
+
// NOTE: llvm/llvm-project#58557
|
|
1300
|
+
battle.last_damage = if (comptime builtin.target.cpu.arch.isWasm())
|
|
1301
|
+
@max(battle.last_damage *% 2, std.math.maxInt(u16))
|
|
1302
|
+
else
|
|
1303
|
+
battle.last_damage *| 2;
|
|
1287
1304
|
|
|
1288
1305
|
// Pokémon Showdown calls moveHit before Counter
|
|
1289
1306
|
if (!showdown and !try checkHit(battle, player, move, options)) return null;
|
|
@@ -1525,7 +1542,7 @@ fn faint(battle: anytype, player: Player, done: bool, cap: bool, options: anytyp
|
|
|
1525
1542
|
assert(!foe_volatiles.MultiHit);
|
|
1526
1543
|
if (foe_volatiles.Bide) {
|
|
1527
1544
|
assert(!foe_volatiles.Thrashing and !foe_volatiles.Rage);
|
|
1528
|
-
foe_volatiles.state = if (
|
|
1545
|
+
foe_volatiles.state = if (pkmn.options.mod) 0 else foe_volatiles.state & 255;
|
|
1529
1546
|
if (foe_volatiles.state != 0) return Result.Error;
|
|
1530
1547
|
}
|
|
1531
1548
|
|
|
@@ -1616,12 +1633,12 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
|
|
|
1616
1633
|
|
|
1617
1634
|
battle.turn += 1;
|
|
1618
1635
|
|
|
1619
|
-
if (
|
|
1636
|
+
if (pkmn.options.mod and pkmn.options.ebc and checkEBC(battle)) {
|
|
1620
1637
|
try options.log.tie(.{});
|
|
1621
1638
|
return Result.Tie;
|
|
1622
1639
|
}
|
|
1623
1640
|
|
|
1624
|
-
if (
|
|
1641
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1625
1642
|
try options.log.tie(.{});
|
|
1626
1643
|
return Result.Tie;
|
|
1627
1644
|
} else if (battle.turn >= 65535) {
|
|
@@ -1916,12 +1933,11 @@ pub const Effects = struct {
|
|
|
1916
1933
|
|
|
1917
1934
|
// Technically this is still considered simply a "miss" on the cartridge,
|
|
1918
1935
|
// but diverging from Pokémon Showdown here would mostly just be pedantic
|
|
1919
|
-
if (n == 0) {
|
|
1936
|
+
if (n == 0 or (pkmn.options.mod and err)) {
|
|
1920
1937
|
try options.log.fail(.{ foe_ident, .None });
|
|
1921
1938
|
return null;
|
|
1922
1939
|
} else if (err) {
|
|
1923
1940
|
// GLITCH: Transform + Mirror Move / Metronome PP softlock
|
|
1924
|
-
assert(!showdown);
|
|
1925
1941
|
return Result.Error;
|
|
1926
1942
|
}
|
|
1927
1943
|
|
|
@@ -1999,7 +2015,7 @@ pub const Effects = struct {
|
|
|
1999
2015
|
if (foe.active.types.includes(move.type)) return;
|
|
2000
2016
|
if (!try Rolls.secondaryChance(battle, player, true, options)) return;
|
|
2001
2017
|
// Freeze Clause Mod
|
|
2002
|
-
if (
|
|
2018
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
2003
2019
|
|
|
2004
2020
|
foe_stored.status = Status.init(.FRZ);
|
|
2005
2021
|
// GLITCH: Hyper Beam recharging status is not cleared
|
|
@@ -2340,7 +2356,7 @@ pub const Effects = struct {
|
|
|
2340
2356
|
}
|
|
2341
2357
|
|
|
2342
2358
|
// Sleep Clause Mod
|
|
2343
|
-
if (
|
|
2359
|
+
if (pkmn.options.mod) {
|
|
2344
2360
|
for (foe.pokemon) |p| {
|
|
2345
2361
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2346
2362
|
}
|
|
@@ -2446,7 +2462,7 @@ pub const Effects = struct {
|
|
|
2446
2462
|
if (side.active.volatiles.Binding) return;
|
|
2447
2463
|
side.active.volatiles.Binding = true;
|
|
2448
2464
|
// GLITCH: Hyper Beam automatic selection glitch if Recharging gets cleared on miss
|
|
2449
|
-
// (Pokémon Showdown
|
|
2465
|
+
// (Pokémon Showdown clears Recharging in onTryMove instead)
|
|
2450
2466
|
if (!showdown) {
|
|
2451
2467
|
foe.active.volatiles.Recharging = false;
|
|
2452
2468
|
} else if (foe.stored().hp == 0) return if (!rewrap) battle.rng.advance(1);
|
|
@@ -3212,7 +3228,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3212
3228
|
// Struggle (Pokémon Showdown would use 'move 1' here)
|
|
3213
3229
|
if (n == before) {
|
|
3214
3230
|
// GLITCH: Transform + Mirror Move / Metronome PP softlock
|
|
3215
|
-
if (!
|
|
3231
|
+
if (!pkmn.options.mod) {
|
|
3216
3232
|
while (slot <= 4) : (slot += 1) {
|
|
3217
3233
|
if (active.moves[slot - 1].pp != 0) return n;
|
|
3218
3234
|
}
|
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;
|
|
@@ -156,6 +148,6 @@ fn ReturnType(comptime field: Action.Field) type {
|
|
|
156
148
|
return ?(switch (field) {
|
|
157
149
|
.damages => u8,
|
|
158
150
|
.critical_hits => Optional(bool),
|
|
159
|
-
else =>
|
|
151
|
+
else => util.FieldType(Action, field),
|
|
160
152
|
});
|
|
161
153
|
}
|
|
@@ -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
|
|
|
@@ -1,44 +1,37 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
4
|
-
|
|
5
|
-
const common = @import("../common/data.zig");
|
|
6
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
|
-
const protocol = @import("../common/protocol.zig");
|
|
8
|
-
const rng = @import("../common/rng.zig");
|
|
9
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
10
5
|
const data = @import("data.zig");
|
|
11
6
|
const generated = @import("generated.zig");
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
17
|
-
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
18
|
-
|
|
19
|
-
const showdown = pkmn.options.showdown;
|
|
20
|
-
|
|
21
|
-
const Choice = common.Choice;
|
|
22
|
-
const ID = common.ID;
|
|
23
|
-
const Player = common.Player;
|
|
24
|
-
const Result = common.Result;
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
9
|
+
const rng = @import("../common/rng.zig");
|
|
10
|
+
const std = @import("std");
|
|
25
11
|
|
|
26
12
|
const Activate = protocol.Activate;
|
|
13
|
+
const ActivePokemon = data.ActivePokemon;
|
|
14
|
+
const assert = std.debug.assert;
|
|
27
15
|
const Boost = protocol.Boost;
|
|
16
|
+
const Boosts = data.Boosts;
|
|
17
|
+
const Choice = common.Choice;
|
|
28
18
|
const Damage = protocol.Damage;
|
|
19
|
+
const Effectiveness = data.Effectiveness;
|
|
29
20
|
const End = protocol.End;
|
|
21
|
+
const expectEqual = std.testing.expectEqual;
|
|
22
|
+
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
23
|
+
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
30
24
|
const Fail = protocol.Fail;
|
|
31
|
-
const Heal = protocol.Heal;
|
|
32
|
-
const Start = protocol.Start;
|
|
33
|
-
|
|
34
25
|
const Gen12 = rng.Gen12;
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
const Boosts = data.Boosts;
|
|
38
|
-
const Effectiveness = data.Effectiveness;
|
|
26
|
+
const Heal = protocol.Heal;
|
|
27
|
+
const ID = common.ID;
|
|
39
28
|
const Item = data.Item;
|
|
40
29
|
const Move = data.Move;
|
|
30
|
+
const Player = common.Player;
|
|
31
|
+
const Result = common.Result;
|
|
32
|
+
const showdown = pkmn.options.showdown;
|
|
41
33
|
const Side = data.Side;
|
|
34
|
+
const Start = protocol.Start;
|
|
42
35
|
const Stats = data.Stats;
|
|
43
36
|
const Status = data.Status;
|
|
44
37
|
const TriAttack = data.TriAttack;
|
|
@@ -1200,7 +1193,7 @@ fn updateSideCondition(
|
|
|
1200
1193
|
fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
|
|
1201
1194
|
battle.turn += 1;
|
|
1202
1195
|
|
|
1203
|
-
if (
|
|
1196
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1204
1197
|
try options.log.tie(.{});
|
|
1205
1198
|
return Result.Tie;
|
|
1206
1199
|
} else if (battle.turn >= 65535) {
|
|
@@ -1434,7 +1427,8 @@ pub const Effects = struct {
|
|
|
1434
1427
|
if (move.type.special() != (last == .MirrorCoat)) return;
|
|
1435
1428
|
if (state.damage == 0) return;
|
|
1436
1429
|
|
|
1437
|
-
|
|
1430
|
+
// NOTE: llvm/llvm-project#58557
|
|
1431
|
+
state.damage = smul(state.damage, 2);
|
|
1438
1432
|
state.miss = false;
|
|
1439
1433
|
}
|
|
1440
1434
|
|
|
@@ -1529,15 +1523,18 @@ pub const Effects = struct {
|
|
|
1529
1523
|
|
|
1530
1524
|
pub const double = struct {
|
|
1531
1525
|
pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1532
|
-
if (battle.foe(player).active.volatiles.Flying)
|
|
1526
|
+
if (!battle.foe(player).active.volatiles.Flying) return;
|
|
1527
|
+
state.damage = smul(state.damage, 2);
|
|
1533
1528
|
}
|
|
1534
1529
|
|
|
1535
1530
|
pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1536
|
-
if (battle.foe(player).active.volatiles.Underground)
|
|
1531
|
+
if (!battle.foe(player).active.volatiles.Underground) return;
|
|
1532
|
+
state.damage = smul(state.damage, 2);
|
|
1537
1533
|
}
|
|
1538
1534
|
|
|
1539
1535
|
pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1540
|
-
if (battle.foe(player).active.volatiles.minimized)
|
|
1536
|
+
if (!battle.foe(player).active.volatiles.minimized) return;
|
|
1537
|
+
state.damage = smul(state.damage, 2);
|
|
1541
1538
|
}
|
|
1542
1539
|
};
|
|
1543
1540
|
|
|
@@ -1706,7 +1703,7 @@ pub const Effects = struct {
|
|
|
1706
1703
|
if (!state.proc) return;
|
|
1707
1704
|
if (foe.conditions.Safeguard) return;
|
|
1708
1705
|
// Freeze Clause Mod
|
|
1709
|
-
if (
|
|
1706
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1710
1707
|
|
|
1711
1708
|
foe_stored.status = Status.init(.FRZ);
|
|
1712
1709
|
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
@@ -1735,7 +1732,7 @@ pub const Effects = struct {
|
|
|
1735
1732
|
state.bp *= 2;
|
|
1736
1733
|
assert(state.bp <= 160);
|
|
1737
1734
|
} else {
|
|
1738
|
-
state.damage
|
|
1735
|
+
state.damage = smul(state.damage, 2);
|
|
1739
1736
|
}
|
|
1740
1737
|
}
|
|
1741
1738
|
|
|
@@ -2206,7 +2203,7 @@ pub const Effects = struct {
|
|
|
2206
2203
|
|
|
2207
2204
|
pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2208
2205
|
// TODO: remove switching variable and just look at Choice saved in Battle struct?
|
|
2209
|
-
if (battle.foe(player).active.volatiles.switching) state.damage
|
|
2206
|
+
if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
|
|
2210
2207
|
}
|
|
2211
2208
|
|
|
2212
2209
|
pub const rage = struct {
|
|
@@ -2219,7 +2216,7 @@ pub const Effects = struct {
|
|
|
2219
2216
|
const side = battle.side(player);
|
|
2220
2217
|
|
|
2221
2218
|
assert(side.active.volatiles.Rage);
|
|
2222
|
-
state.damage
|
|
2219
|
+
state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
|
|
2223
2220
|
}
|
|
2224
2221
|
};
|
|
2225
2222
|
|
|
@@ -2314,7 +2311,7 @@ pub const Effects = struct {
|
|
|
2314
2311
|
side.active.volatiles.attacks += 1;
|
|
2315
2312
|
side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
|
|
2316
2313
|
|
|
2317
|
-
if (side.active.volatiles.DefenseCurl) state.damage
|
|
2314
|
+
if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
|
|
2318
2315
|
}
|
|
2319
2316
|
};
|
|
2320
2317
|
|
|
@@ -2384,7 +2381,7 @@ pub const Effects = struct {
|
|
|
2384
2381
|
}
|
|
2385
2382
|
|
|
2386
2383
|
// Sleep Clause Mod
|
|
2387
|
-
if (
|
|
2384
|
+
if (pkmn.options.mod) {
|
|
2388
2385
|
for (foe.pokemon) |p| {
|
|
2389
2386
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2390
2387
|
}
|
|
@@ -2842,6 +2839,12 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2842
2839
|
return t != .@"???" and !active.types.includes(t);
|
|
2843
2840
|
}
|
|
2844
2841
|
|
|
2842
|
+
// NOTE: llvm/llvm-project#58557
|
|
2843
|
+
fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
|
2844
|
+
if (!comptime builtin.target.cpu.arch.isWasm()) return a *| b;
|
|
2845
|
+
return @max(a *% b, std.math.maxInt(@TypeOf(a)));
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2845
2848
|
pub const Rolls = struct {
|
|
2846
2849
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2847
2850
|
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|