@pkmn/engine 0.1.0-dev.3a62fd35 → 0.1.0-dev.3ed876db
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 -3
- package/README.md +15 -11
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +44 -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 +97 -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 +21 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +22 -0
- package/build/pkg/addon.js +35 -58
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +25 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +23 -9
- package/build/pkg/index.js +17 -11
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +109 -107
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +36 -70
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +141 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +237 -83
- package/package.json +53 -26
- package/src/bin/install-pkmn-engine +17 -7
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +8 -8
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +13 -9
- package/src/lib/common/options.zig +6 -0
- package/src/lib/common/protocol.zig +55 -52
- package/src/lib/common/rational.zig +121 -29
- package/src/lib/common/rng.zig +11 -13
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +683 -156
- package/src/lib/gen1/chance.zig +415 -290
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +2 -4
- package/src/lib/gen1/data/types.zig +1 -2
- package/src/lib/gen1/data.zig +38 -41
- package/src/lib/gen1/helpers.zig +92 -548
- package/src/lib/gen1/mechanics.zig +354 -228
- package/src/lib/gen2/calc.zig +37 -27
- package/src/lib/gen2/chance.zig +90 -502
- 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 +26 -29
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +46 -25
- package/src/lib/gen2/mechanics.zig +180 -133
- package/src/lib/node.zig +2 -4
- package/src/lib/pkmn.zig +14 -16
- package/src/lib/wasm.zig +1 -2
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -1,50 +1,36 @@
|
|
|
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
|
-
const util = @import("../common/util.zig");
|
|
10
2
|
|
|
3
|
+
const common = @import("../common/data.zig");
|
|
11
4
|
const data = @import("data.zig");
|
|
12
5
|
const generated = @import("generated.zig");
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
18
|
-
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
19
|
-
|
|
20
|
-
const showdown = pkmn.options.showdown;
|
|
21
|
-
|
|
22
|
-
const Choice = common.Choice;
|
|
23
|
-
const ID = common.ID;
|
|
24
|
-
const Player = common.Player;
|
|
25
|
-
const Result = common.Result;
|
|
6
|
+
const pkmn = @import("../pkmn.zig");
|
|
7
|
+
const protocol = @import("../common/protocol.zig");
|
|
8
|
+
const rng = @import("../common/rng.zig");
|
|
9
|
+
const std = @import("std");
|
|
26
10
|
|
|
27
11
|
const Activate = protocol.Activate;
|
|
12
|
+
const ActivePokemon = data.ActivePokemon;
|
|
13
|
+
const assert = std.debug.assert;
|
|
28
14
|
const Boost = protocol.Boost;
|
|
15
|
+
const Boosts = data.Boosts;
|
|
16
|
+
const Choice = common.Choice;
|
|
29
17
|
const Damage = protocol.Damage;
|
|
18
|
+
const Effectiveness = data.Effectiveness;
|
|
30
19
|
const End = protocol.End;
|
|
20
|
+
const expectEqual = std.testing.expectEqual;
|
|
21
|
+
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
22
|
+
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
31
23
|
const Fail = protocol.Fail;
|
|
32
|
-
const Heal = protocol.Heal;
|
|
33
|
-
const Start = protocol.Start;
|
|
34
|
-
|
|
35
24
|
const Gen12 = rng.Gen12;
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const ActivePokemon = data.ActivePokemon;
|
|
40
|
-
const Boosts = data.Boosts;
|
|
41
|
-
const Effectiveness = data.Effectiveness;
|
|
25
|
+
const Heal = protocol.Heal;
|
|
26
|
+
const ID = common.ID;
|
|
42
27
|
const Item = data.Item;
|
|
43
28
|
const Move = data.Move;
|
|
44
|
-
const
|
|
45
|
-
const
|
|
29
|
+
const Player = common.Player;
|
|
30
|
+
const Result = common.Result;
|
|
31
|
+
const showdown = pkmn.options.showdown;
|
|
46
32
|
const Side = data.Side;
|
|
47
|
-
const
|
|
33
|
+
const Start = protocol.Start;
|
|
48
34
|
const Stats = data.Stats;
|
|
49
35
|
const Status = data.Status;
|
|
50
36
|
const TriAttack = data.TriAttack;
|
|
@@ -106,6 +92,7 @@ pub const State = struct {
|
|
|
106
92
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
93
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
94
|
move: Move, // wCurPlayerMove
|
|
95
|
+
hit: u3 = 0,
|
|
109
96
|
from: Move = .None,
|
|
110
97
|
mslot: u4, // wCurMoveNum
|
|
111
98
|
first: bool, // wEnemyGoesFirst
|
|
@@ -256,7 +243,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
243
|
} else {
|
|
257
244
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
245
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
246
|
|
|
261
247
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
248
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -442,18 +428,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
442
428
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
443
429
|
const before = stored.status;
|
|
444
430
|
// Even if the EXT bit is set this will still correctly modify the sleep duration
|
|
445
|
-
// if (options.calc.modify(player, .sleep)) |extend| {
|
|
446
|
-
// if (!extend) stored.status = 0;
|
|
447
|
-
// } else {
|
|
448
431
|
stored.status -= 1;
|
|
449
|
-
// }
|
|
450
|
-
|
|
451
|
-
const duration = Status.duration(stored.status);
|
|
452
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
453
|
-
try options.chance.sleep(player, duration);
|
|
454
|
-
}
|
|
455
432
|
|
|
456
|
-
if (duration == 0) {
|
|
433
|
+
if (Status.duration(stored.status) == 0) {
|
|
457
434
|
try log.curestatus(.{ ident, before, .Message });
|
|
458
435
|
stored.status = 0; // clears EXT if present
|
|
459
436
|
resetCant(volatiles);
|
|
@@ -483,12 +460,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
483
460
|
}
|
|
484
461
|
|
|
485
462
|
if (volatiles.disable.duration > 0) {
|
|
486
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
487
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
488
|
-
// } else {
|
|
489
463
|
volatiles.disable.duration -= 1;
|
|
490
|
-
// }
|
|
491
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
492
464
|
|
|
493
465
|
if (volatiles.disable.duration == 0) {
|
|
494
466
|
volatiles.disable.move = 0;
|
|
@@ -498,13 +470,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
498
470
|
|
|
499
471
|
if (volatiles.Confusion) {
|
|
500
472
|
assert(volatiles.confusion > 0);
|
|
501
|
-
|
|
502
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
503
|
-
// if (!extend) volatiles.confusion = 0;
|
|
504
|
-
// } else {
|
|
505
473
|
volatiles.confusion -= 1;
|
|
506
|
-
// }
|
|
507
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
508
474
|
|
|
509
475
|
if (volatiles.confusion == 0) {
|
|
510
476
|
volatiles.Confusion = false;
|
|
@@ -665,7 +631,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
665
631
|
}
|
|
666
632
|
assert(stage <= 4);
|
|
667
633
|
|
|
668
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
634
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
669
635
|
}
|
|
670
636
|
|
|
671
637
|
pub fn calcDamage(
|
|
@@ -733,7 +699,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
733
699
|
|
|
734
700
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
735
701
|
if (state.damage <= 1) return;
|
|
736
|
-
const random = try Rolls.damage(battle, player, options);
|
|
702
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
737
703
|
options.calc.base(player.foe(), state.damage);
|
|
738
704
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
739
705
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -1226,7 +1192,7 @@ fn updateSideCondition(
|
|
|
1226
1192
|
fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
|
|
1227
1193
|
battle.turn += 1;
|
|
1228
1194
|
|
|
1229
|
-
if (
|
|
1195
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1230
1196
|
try options.log.tie(.{});
|
|
1231
1197
|
return Result.Tie;
|
|
1232
1198
|
} else if (battle.turn >= 65535) {
|
|
@@ -1304,14 +1270,32 @@ pub const Effects = struct {
|
|
|
1304
1270
|
}
|
|
1305
1271
|
|
|
1306
1272
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1273
|
+
assert(!state.miss);
|
|
1274
|
+
var side = battle.side(player);
|
|
1275
|
+
|
|
1276
|
+
const ident = battle.active(player);
|
|
1277
|
+
|
|
1278
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1279
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1280
|
+
return options.log.fail(.{ ident, .None });
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1284
|
+
if (side.stored().hp <= hp) {
|
|
1285
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1286
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1287
|
+
return options.log.fail(.{ ident, .None });
|
|
1288
|
+
} else {
|
|
1289
|
+
side.stored().hp -= hp;
|
|
1290
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1291
|
+
|
|
1292
|
+
const before = side.active.boosts.atk;
|
|
1293
|
+
var i: usize = 0;
|
|
1294
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1295
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1296
|
+
}
|
|
1297
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1298
|
+
}
|
|
1315
1299
|
}
|
|
1316
1300
|
|
|
1317
1301
|
pub const bide = struct {
|
|
@@ -1323,13 +1307,7 @@ pub const Effects = struct {
|
|
|
1323
1307
|
|
|
1324
1308
|
if (!volatiles.Bide) return;
|
|
1325
1309
|
|
|
1326
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1327
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1328
|
-
// } else {
|
|
1329
1310
|
volatiles.attacks -= 1;
|
|
1330
|
-
// }
|
|
1331
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1332
|
-
|
|
1333
1311
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1334
1312
|
|
|
1335
1313
|
volatiles.Bide = false;
|
|
@@ -1596,8 +1574,16 @@ pub const Effects = struct {
|
|
|
1596
1574
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1597
1575
|
}
|
|
1598
1576
|
|
|
1599
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1600
|
-
|
|
1577
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1578
|
+
var side = battle.side(player);
|
|
1579
|
+
var stored = side.stored();
|
|
1580
|
+
|
|
1581
|
+
stored.hp = 0;
|
|
1582
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1583
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1584
|
+
if (!showdown) stored.status = 0;
|
|
1585
|
+
side.active.volatiles.LeechSeed = false;
|
|
1586
|
+
side.active.volatiles.DestinyBond = false;
|
|
1601
1587
|
}
|
|
1602
1588
|
|
|
1603
1589
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1712,7 +1698,7 @@ pub const Effects = struct {
|
|
|
1712
1698
|
if (!state.proc) return;
|
|
1713
1699
|
if (foe.conditions.Safeguard) return;
|
|
1714
1700
|
// Freeze Clause Mod
|
|
1715
|
-
if (
|
|
1701
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1716
1702
|
|
|
1717
1703
|
foe_stored.status = Status.init(.FRZ);
|
|
1718
1704
|
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
@@ -1723,8 +1709,26 @@ pub const Effects = struct {
|
|
|
1723
1709
|
foe.active.volatiles.frozen = true;
|
|
1724
1710
|
}
|
|
1725
1711
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1712
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1713
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1714
|
+
var side = battle.side(player);
|
|
1715
|
+
var volatiles = &side.active.volatiles;
|
|
1716
|
+
|
|
1717
|
+
if (state.miss) {
|
|
1718
|
+
volatiles.fury_cutter = 0;
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1723
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1724
|
+
volatiles.fury_cutter += 1;
|
|
1725
|
+
|
|
1726
|
+
if (showdown) {
|
|
1727
|
+
state.bp *= 2;
|
|
1728
|
+
assert(state.bp <= 160);
|
|
1729
|
+
} else {
|
|
1730
|
+
state.damage *|= 2;
|
|
1731
|
+
}
|
|
1728
1732
|
}
|
|
1729
1733
|
|
|
1730
1734
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1885,7 +1889,29 @@ pub const Effects = struct {
|
|
|
1885
1889
|
}
|
|
1886
1890
|
|
|
1887
1891
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1888
|
-
|
|
1892
|
+
var side = battle.side(player);
|
|
1893
|
+
|
|
1894
|
+
if (state.miss) {
|
|
1895
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1896
|
+
try options.log.lastmiss(.{});
|
|
1897
|
+
return options.log.miss(.{battle.active(player)});
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
const move = battle.foe(player).lastMove(false);
|
|
1901
|
+
if (move == .None or move == .Struggle) {
|
|
1902
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
var slot: u4 = 0;
|
|
1906
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1907
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1908
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1909
|
+
}
|
|
1910
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1911
|
+
|
|
1912
|
+
side.active.move(slot).id = move;
|
|
1913
|
+
side.active.move(slot).pp = 5;
|
|
1914
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1889
1915
|
}
|
|
1890
1916
|
|
|
1891
1917
|
pub fn mirrorMove(
|
|
@@ -2350,7 +2376,7 @@ pub const Effects = struct {
|
|
|
2350
2376
|
}
|
|
2351
2377
|
|
|
2352
2378
|
// Sleep Clause Mod
|
|
2353
|
-
if (
|
|
2379
|
+
if (pkmn.options.mod) {
|
|
2354
2380
|
for (foe.pokemon) |p| {
|
|
2355
2381
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2356
2382
|
}
|
|
@@ -2393,17 +2419,37 @@ pub const Effects = struct {
|
|
|
2393
2419
|
}
|
|
2394
2420
|
|
|
2395
2421
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2396
|
-
|
|
2397
|
-
// var foe = battle.foe(player);
|
|
2422
|
+
var foe = battle.foe(player);
|
|
2398
2423
|
|
|
2399
|
-
|
|
2424
|
+
const last = foe.lastMove(false);
|
|
2425
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2426
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2427
|
+
}
|
|
2400
2428
|
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
//
|
|
2406
|
-
|
|
2429
|
+
var slot: u4 = 0;
|
|
2430
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2431
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2432
|
+
}
|
|
2433
|
+
// If the move was called via Metronome it might not exist
|
|
2434
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2435
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2439
|
+
const pp = @min(
|
|
2440
|
+
foe.active.move(slot).pp,
|
|
2441
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2442
|
+
);
|
|
2443
|
+
foe.active.move(slot).pp -= pp;
|
|
2444
|
+
if (!foe.active.volatiles.Transform) {
|
|
2445
|
+
var move_slot = foe.stored().move(slot);
|
|
2446
|
+
assert(move_slot.pp > 0);
|
|
2447
|
+
|
|
2448
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2449
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2407
2453
|
}
|
|
2408
2454
|
|
|
2409
2455
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2790,7 +2836,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2790
2836
|
|
|
2791
2837
|
pub const Rolls = struct {
|
|
2792
2838
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2793
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2839
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2794
2840
|
player == .P1
|
|
2795
2841
|
else if (showdown)
|
|
2796
2842
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2802,7 +2848,7 @@ pub const Rolls = struct {
|
|
|
2802
2848
|
}
|
|
2803
2849
|
|
|
2804
2850
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2805
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2851
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2806
2852
|
val == .true
|
|
2807
2853
|
else if (showdown)
|
|
2808
2854
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2814,7 +2860,7 @@ pub const Rolls = struct {
|
|
|
2814
2860
|
}
|
|
2815
2861
|
|
|
2816
2862
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2817
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2863
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2818
2864
|
val == .true
|
|
2819
2865
|
else if (showdown)
|
|
2820
2866
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2825,20 +2871,22 @@ pub const Rolls = struct {
|
|
|
2825
2871
|
return ok;
|
|
2826
2872
|
}
|
|
2827
2873
|
|
|
2828
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2829
|
-
|
|
2874
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2875
|
+
_ = .{ player, options };
|
|
2876
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2830
2877
|
val == .true
|
|
2831
2878
|
else if (showdown)
|
|
2832
2879
|
battle.rng.chance(u8, rate, 256)
|
|
2833
2880
|
else
|
|
2834
2881
|
battle.rng.next() < rate;
|
|
2835
2882
|
|
|
2836
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2883
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2837
2884
|
return crit;
|
|
2838
2885
|
}
|
|
2839
2886
|
|
|
2840
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2841
|
-
|
|
2887
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2888
|
+
_ = .{ player, options };
|
|
2889
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2842
2890
|
val
|
|
2843
2891
|
else roll: {
|
|
2844
2892
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2849,12 +2897,12 @@ pub const Rolls = struct {
|
|
|
2849
2897
|
};
|
|
2850
2898
|
|
|
2851
2899
|
assert(roll >= 217 and roll <= 255);
|
|
2852
|
-
try options.chance.damage(player, roll);
|
|
2900
|
+
try options.chance.damage(player, i, roll);
|
|
2853
2901
|
return roll;
|
|
2854
2902
|
}
|
|
2855
2903
|
|
|
2856
2904
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2857
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2905
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2858
2906
|
val == .true
|
|
2859
2907
|
else if (showdown)
|
|
2860
2908
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2866,7 +2914,7 @@ pub const Rolls = struct {
|
|
|
2866
2914
|
}
|
|
2867
2915
|
|
|
2868
2916
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2869
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2917
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2870
2918
|
val == .true
|
|
2871
2919
|
else if (showdown)
|
|
2872
2920
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2878,7 +2926,7 @@ pub const Rolls = struct {
|
|
|
2878
2926
|
}
|
|
2879
2927
|
|
|
2880
2928
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2881
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2929
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2882
2930
|
val == .true
|
|
2883
2931
|
else if (showdown)
|
|
2884
2932
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2890,7 +2938,7 @@ pub const Rolls = struct {
|
|
|
2890
2938
|
}
|
|
2891
2939
|
|
|
2892
2940
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2893
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2941
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2894
2942
|
val == .true
|
|
2895
2943
|
else if (showdown)
|
|
2896
2944
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2903,7 +2951,7 @@ pub const Rolls = struct {
|
|
|
2903
2951
|
|
|
2904
2952
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2905
2953
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2906
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2954
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2907
2955
|
val == .true
|
|
2908
2956
|
else if (showdown)
|
|
2909
2957
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2915,7 +2963,7 @@ pub const Rolls = struct {
|
|
|
2915
2963
|
}
|
|
2916
2964
|
|
|
2917
2965
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2918
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2966
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2919
2967
|
val == .true
|
|
2920
2968
|
else if (showdown)
|
|
2921
2969
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2927,7 +2975,7 @@ pub const Rolls = struct {
|
|
|
2927
2975
|
}
|
|
2928
2976
|
|
|
2929
2977
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2930
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2978
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2931
2979
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2932
2980
|
else if (showdown)
|
|
2933
2981
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2943,7 +2991,7 @@ pub const Rolls = struct {
|
|
|
2943
2991
|
}
|
|
2944
2992
|
|
|
2945
2993
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2946
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
2994
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2947
2995
|
(val - 1) * 40
|
|
2948
2996
|
else if (showdown) power: {
|
|
2949
2997
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2965,7 +3013,7 @@ pub const Rolls = struct {
|
|
|
2965
3013
|
}
|
|
2966
3014
|
|
|
2967
3015
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2968
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3016
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2969
3017
|
val + 3
|
|
2970
3018
|
else if (showdown) num: {
|
|
2971
3019
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2993,7 +3041,7 @@ pub const Rolls = struct {
|
|
|
2993
3041
|
}
|
|
2994
3042
|
|
|
2995
3043
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
2996
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3044
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
2997
3045
|
val
|
|
2998
3046
|
else if (showdown)
|
|
2999
3047
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3009,13 +3057,12 @@ pub const Rolls = struct {
|
|
|
3009
3057
|
return hits;
|
|
3010
3058
|
}
|
|
3011
3059
|
|
|
3012
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3013
|
-
|
|
3060
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3061
|
+
assert(!showdown);
|
|
3062
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3014
3063
|
val + 1
|
|
3015
|
-
else if (showdown)
|
|
3016
|
-
battle.rng.range(u8, 2, 6)
|
|
3017
3064
|
else
|
|
3018
|
-
(battle.rng.next() & 3) + 2;
|
|
3065
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3019
3066
|
|
|
3020
3067
|
assert(pp >= 2 and pp <= 5);
|
|
3021
3068
|
try options.chance.spite(player, pp);
|
|
@@ -3029,7 +3076,7 @@ pub const Rolls = struct {
|
|
|
3029
3076
|
var i: u8 = 0;
|
|
3030
3077
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3031
3078
|
assert(mtype != .@"???");
|
|
3032
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3079
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3033
3080
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3034
3081
|
else if (showdown) ty: {
|
|
3035
3082
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3063,7 +3110,7 @@ pub const Rolls = struct {
|
|
|
3063
3110
|
assert(count <= 8);
|
|
3064
3111
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3065
3112
|
|
|
3066
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3113
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3067
3114
|
val == .true
|
|
3068
3115
|
else if (showdown)
|
|
3069
3116
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3085,7 +3132,7 @@ pub const Rolls = struct {
|
|
|
3085
3132
|
const moves = active.moves;
|
|
3086
3133
|
|
|
3087
3134
|
// TODO: consider throwing error instead of rerolling?
|
|
3088
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3135
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3089
3136
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3090
3137
|
else
|
|
3091
3138
|
null;
|
|
@@ -3122,7 +3169,7 @@ pub const Rolls = struct {
|
|
|
3122
3169
|
const moves = active.moves;
|
|
3123
3170
|
|
|
3124
3171
|
// TODO: consider throwing error instead of rerolling?
|
|
3125
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3172
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3126
3173
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3127
3174
|
else
|
|
3128
3175
|
null;
|
|
@@ -3166,7 +3213,7 @@ pub const Rolls = struct {
|
|
|
3166
3213
|
assert(n > 0 and n <= 6);
|
|
3167
3214
|
|
|
3168
3215
|
// TODO: consider throwing error instead of rerolling?
|
|
3169
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3216
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3170
3217
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3171
3218
|
break :val null;
|
|
3172
3219
|
} else null;
|
|
@@ -3192,7 +3239,7 @@ pub const Rolls = struct {
|
|
|
3192
3239
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3193
3240
|
|
|
3194
3241
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3195
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3242
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3196
3243
|
@intCast(val)
|
|
3197
3244
|
else if (showdown)
|
|
3198
3245
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3207,7 +3254,7 @@ pub const Rolls = struct {
|
|
|
3207
3254
|
}
|
|
3208
3255
|
|
|
3209
3256
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3210
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3257
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3211
3258
|
val
|
|
3212
3259
|
else if (showdown)
|
|
3213
3260
|
battle.rng.range(u8, 1, max)
|
|
@@ -3233,7 +3280,7 @@ pub const Rolls = struct {
|
|
|
3233
3280
|
}
|
|
3234
3281
|
|
|
3235
3282
|
// TODO: consider throwing error instead of rerolling?
|
|
3236
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3283
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3237
3284
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3238
3285
|
break :val val;
|
|
3239
3286
|
} else null;
|
|
@@ -3265,7 +3312,7 @@ pub const Rolls = struct {
|
|
|
3265
3312
|
}
|
|
3266
3313
|
|
|
3267
3314
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3268
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3315
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3269
3316
|
@intCast(val)
|
|
3270
3317
|
else if (showdown)
|
|
3271
3318
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3277,12 +3324,12 @@ pub const Rolls = struct {
|
|
|
3277
3324
|
};
|
|
3278
3325
|
|
|
3279
3326
|
assert(duration >= 1 and duration <= 6);
|
|
3280
|
-
options.chance.duration(
|
|
3327
|
+
options.chance.duration(player, duration);
|
|
3281
3328
|
return duration + 1;
|
|
3282
3329
|
}
|
|
3283
3330
|
|
|
3284
3331
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3285
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3332
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3286
3333
|
@intCast(val)
|
|
3287
3334
|
else if (showdown)
|
|
3288
3335
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3291,16 +3338,16 @@ pub const Rolls = struct {
|
|
|
3291
3338
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3292
3339
|
|
|
3293
3340
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3294
|
-
options.chance.duration(
|
|
3341
|
+
options.chance.duration(player, duration);
|
|
3295
3342
|
return duration;
|
|
3296
3343
|
}
|
|
3297
3344
|
|
|
3298
3345
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3299
|
-
const duration:
|
|
3346
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3300
3347
|
@intCast(val)
|
|
3301
3348
|
else if (showdown)
|
|
3302
3349
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3303
|
-
battle.rng.range(
|
|
3350
|
+
battle.rng.range(u3, 2, 6)
|
|
3304
3351
|
else duration: {
|
|
3305
3352
|
while (true) {
|
|
3306
3353
|
const r = battle.rng.next() & 7;
|
|
@@ -3309,12 +3356,12 @@ pub const Rolls = struct {
|
|
|
3309
3356
|
};
|
|
3310
3357
|
|
|
3311
3358
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3312
|
-
options.chance.duration(
|
|
3359
|
+
options.chance.duration(player, duration);
|
|
3313
3360
|
return duration;
|
|
3314
3361
|
}
|
|
3315
3362
|
|
|
3316
3363
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3317
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3364
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3318
3365
|
@intCast(val)
|
|
3319
3366
|
else if (showdown)
|
|
3320
3367
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3322,12 +3369,12 @@ pub const Rolls = struct {
|
|
|
3322
3369
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3323
3370
|
|
|
3324
3371
|
assert(duration >= 2 and duration <= 3);
|
|
3325
|
-
options.chance.duration(
|
|
3372
|
+
options.chance.duration(player, duration);
|
|
3326
3373
|
return duration;
|
|
3327
3374
|
}
|
|
3328
3375
|
|
|
3329
3376
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3330
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3377
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3331
3378
|
@intCast(val)
|
|
3332
3379
|
else if (showdown)
|
|
3333
3380
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3336,13 +3383,13 @@ pub const Rolls = struct {
|
|
|
3336
3383
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3337
3384
|
|
|
3338
3385
|
assert(duration >= 2 and duration <= 5);
|
|
3339
|
-
options.chance.duration(
|
|
3386
|
+
options.chance.duration(player, duration);
|
|
3340
3387
|
return duration + 1;
|
|
3341
3388
|
}
|
|
3342
3389
|
|
|
3343
3390
|
// TODO: consider sharing implementation with bindingDuration
|
|
3344
3391
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3345
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3392
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3346
3393
|
@intCast(val)
|
|
3347
3394
|
else if (showdown)
|
|
3348
3395
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3350,7 +3397,7 @@ pub const Rolls = struct {
|
|
|
3350
3397
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3351
3398
|
|
|
3352
3399
|
assert(duration >= 2 and duration <= 5);
|
|
3353
|
-
options.chance.duration(
|
|
3400
|
+
options.chance.duration(player, duration);
|
|
3354
3401
|
return duration + 1;
|
|
3355
3402
|
}
|
|
3356
3403
|
};
|