@pkmn/engine 0.1.0-dev.bb8e286d → 0.1.0-dev.bce6e8a5
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 +46 -18
- 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 +39 -71
- 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 +27 -6
- 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 +40 -90
- 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 +109 -107
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +53 -78
- 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 +330 -104
- package/package.json +59 -133
- package/src/bin/install-pkmn-engine +32 -21
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +255 -0
- package/src/lib/{binding → bindings}/node.zig +2 -11
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +14 -10
- package/src/lib/common/options.zig +6 -0
- package/src/lib/common/protocol.zig +60 -64
- package/src/lib/common/rational.zig +121 -29
- package/src/lib/common/rng.zig +7 -9
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +683 -156
- package/src/lib/gen1/chance.zig +405 -280
- 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 +24 -27
- package/src/lib/gen1/helpers.zig +92 -548
- package/src/lib/gen1/mechanics.zig +365 -241
- package/src/lib/gen2/calc.zig +37 -27
- package/src/lib/gen2/chance.zig +84 -496
- 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 +18 -21
- package/src/lib/gen2/generated.zig +18 -23
- package/src/lib/gen2/helpers.zig +46 -25
- package/src/lib/gen2/mechanics.zig +198 -140
- package/src/lib/node.zig +18 -0
- package/src/lib/pkmn.zig +37 -15
- package/src/lib/wasm.zig +17 -0
- package/src/tools/display/dom.ts +93 -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/binding/wasm.zig +0 -12
- 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,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
|
-
const util = @import("../common/util.zig");
|
|
10
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
11
5
|
const data = @import("data.zig");
|
|
12
6
|
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;
|
|
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");
|
|
26
11
|
|
|
27
12
|
const Activate = protocol.Activate;
|
|
13
|
+
const ActivePokemon = data.ActivePokemon;
|
|
14
|
+
const assert = std.debug.assert;
|
|
28
15
|
const Boost = protocol.Boost;
|
|
16
|
+
const Boosts = data.Boosts;
|
|
17
|
+
const Choice = common.Choice;
|
|
29
18
|
const Damage = protocol.Damage;
|
|
19
|
+
const Effectiveness = data.Effectiveness;
|
|
30
20
|
const End = protocol.End;
|
|
21
|
+
const expectEqual = std.testing.expectEqual;
|
|
22
|
+
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
23
|
+
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
31
24
|
const Fail = protocol.Fail;
|
|
32
|
-
const Heal = protocol.Heal;
|
|
33
|
-
const Start = protocol.Start;
|
|
34
|
-
|
|
35
25
|
const Gen12 = rng.Gen12;
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const ActivePokemon = data.ActivePokemon;
|
|
40
|
-
const Boosts = data.Boosts;
|
|
41
|
-
const Effectiveness = data.Effectiveness;
|
|
26
|
+
const Heal = protocol.Heal;
|
|
27
|
+
const ID = common.ID;
|
|
42
28
|
const Item = data.Item;
|
|
43
29
|
const Move = data.Move;
|
|
44
|
-
const
|
|
45
|
-
const
|
|
30
|
+
const Player = common.Player;
|
|
31
|
+
const Result = common.Result;
|
|
32
|
+
const showdown = pkmn.options.showdown;
|
|
46
33
|
const Side = data.Side;
|
|
47
|
-
const
|
|
34
|
+
const Start = protocol.Start;
|
|
48
35
|
const Stats = data.Stats;
|
|
49
36
|
const Status = data.Status;
|
|
50
37
|
const TriAttack = data.TriAttack;
|
|
@@ -106,6 +93,7 @@ pub const State = struct {
|
|
|
106
93
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
94
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
95
|
move: Move, // wCurPlayerMove
|
|
96
|
+
hit: u3 = 0,
|
|
109
97
|
from: Move = .None,
|
|
110
98
|
mslot: u4, // wCurMoveNum
|
|
111
99
|
first: bool, // wEnemyGoesFirst
|
|
@@ -256,7 +244,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
244
|
} else {
|
|
257
245
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
246
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
247
|
|
|
261
248
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
249
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -442,18 +429,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
442
429
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
443
430
|
const before = stored.status;
|
|
444
431
|
// 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
432
|
stored.status -= 1;
|
|
449
|
-
// }
|
|
450
433
|
|
|
451
|
-
|
|
452
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
453
|
-
try options.chance.sleep(player, duration);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
if (duration == 0) {
|
|
434
|
+
if (Status.duration(stored.status) == 0) {
|
|
457
435
|
try log.curestatus(.{ ident, before, .Message });
|
|
458
436
|
stored.status = 0; // clears EXT if present
|
|
459
437
|
resetCant(volatiles);
|
|
@@ -483,12 +461,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
483
461
|
}
|
|
484
462
|
|
|
485
463
|
if (volatiles.disable.duration > 0) {
|
|
486
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
487
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
488
|
-
// } else {
|
|
489
464
|
volatiles.disable.duration -= 1;
|
|
490
|
-
// }
|
|
491
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
492
465
|
|
|
493
466
|
if (volatiles.disable.duration == 0) {
|
|
494
467
|
volatiles.disable.move = 0;
|
|
@@ -498,13 +471,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
498
471
|
|
|
499
472
|
if (volatiles.Confusion) {
|
|
500
473
|
assert(volatiles.confusion > 0);
|
|
501
|
-
|
|
502
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
503
|
-
// if (!extend) volatiles.confusion = 0;
|
|
504
|
-
// } else {
|
|
505
474
|
volatiles.confusion -= 1;
|
|
506
|
-
// }
|
|
507
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
508
475
|
|
|
509
476
|
if (volatiles.confusion == 0) {
|
|
510
477
|
volatiles.Confusion = false;
|
|
@@ -665,7 +632,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
665
632
|
}
|
|
666
633
|
assert(stage <= 4);
|
|
667
634
|
|
|
668
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
635
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
669
636
|
}
|
|
670
637
|
|
|
671
638
|
pub fn calcDamage(
|
|
@@ -733,7 +700,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
733
700
|
|
|
734
701
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
735
702
|
if (state.damage <= 1) return;
|
|
736
|
-
const random = try Rolls.damage(battle, player, options);
|
|
703
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
737
704
|
options.calc.base(player.foe(), state.damage);
|
|
738
705
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
739
706
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -1226,7 +1193,7 @@ fn updateSideCondition(
|
|
|
1226
1193
|
fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
|
|
1227
1194
|
battle.turn += 1;
|
|
1228
1195
|
|
|
1229
|
-
if (
|
|
1196
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1230
1197
|
try options.log.tie(.{});
|
|
1231
1198
|
return Result.Tie;
|
|
1232
1199
|
} else if (battle.turn >= 65535) {
|
|
@@ -1304,14 +1271,32 @@ pub const Effects = struct {
|
|
|
1304
1271
|
}
|
|
1305
1272
|
|
|
1306
1273
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1274
|
+
assert(!state.miss);
|
|
1275
|
+
var side = battle.side(player);
|
|
1276
|
+
|
|
1277
|
+
const ident = battle.active(player);
|
|
1278
|
+
|
|
1279
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1280
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1281
|
+
return options.log.fail(.{ ident, .None });
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1285
|
+
if (side.stored().hp <= hp) {
|
|
1286
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1287
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1288
|
+
return options.log.fail(.{ ident, .None });
|
|
1289
|
+
} else {
|
|
1290
|
+
side.stored().hp -= hp;
|
|
1291
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1292
|
+
|
|
1293
|
+
const before = side.active.boosts.atk;
|
|
1294
|
+
var i: usize = 0;
|
|
1295
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1296
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1297
|
+
}
|
|
1298
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1299
|
+
}
|
|
1315
1300
|
}
|
|
1316
1301
|
|
|
1317
1302
|
pub const bide = struct {
|
|
@@ -1323,13 +1308,7 @@ pub const Effects = struct {
|
|
|
1323
1308
|
|
|
1324
1309
|
if (!volatiles.Bide) return;
|
|
1325
1310
|
|
|
1326
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1327
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1328
|
-
// } else {
|
|
1329
1311
|
volatiles.attacks -= 1;
|
|
1330
|
-
// }
|
|
1331
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1332
|
-
|
|
1333
1312
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1334
1313
|
|
|
1335
1314
|
volatiles.Bide = false;
|
|
@@ -1448,7 +1427,8 @@ pub const Effects = struct {
|
|
|
1448
1427
|
if (move.type.special() != (last == .MirrorCoat)) return;
|
|
1449
1428
|
if (state.damage == 0) return;
|
|
1450
1429
|
|
|
1451
|
-
|
|
1430
|
+
// NOTE: llvm/llvm-project#58557
|
|
1431
|
+
state.damage = smul(state.damage, 2);
|
|
1452
1432
|
state.miss = false;
|
|
1453
1433
|
}
|
|
1454
1434
|
|
|
@@ -1543,15 +1523,18 @@ pub const Effects = struct {
|
|
|
1543
1523
|
|
|
1544
1524
|
pub const double = struct {
|
|
1545
1525
|
pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1546
|
-
if (battle.foe(player).active.volatiles.Flying)
|
|
1526
|
+
if (!battle.foe(player).active.volatiles.Flying) return;
|
|
1527
|
+
state.damage = smul(state.damage, 2);
|
|
1547
1528
|
}
|
|
1548
1529
|
|
|
1549
1530
|
pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1550
|
-
if (battle.foe(player).active.volatiles.Underground)
|
|
1531
|
+
if (!battle.foe(player).active.volatiles.Underground) return;
|
|
1532
|
+
state.damage = smul(state.damage, 2);
|
|
1551
1533
|
}
|
|
1552
1534
|
|
|
1553
1535
|
pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1554
|
-
if (battle.foe(player).active.volatiles.minimized)
|
|
1536
|
+
if (!battle.foe(player).active.volatiles.minimized) return;
|
|
1537
|
+
state.damage = smul(state.damage, 2);
|
|
1555
1538
|
}
|
|
1556
1539
|
};
|
|
1557
1540
|
|
|
@@ -1596,8 +1579,16 @@ pub const Effects = struct {
|
|
|
1596
1579
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1597
1580
|
}
|
|
1598
1581
|
|
|
1599
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1600
|
-
|
|
1582
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1583
|
+
var side = battle.side(player);
|
|
1584
|
+
var stored = side.stored();
|
|
1585
|
+
|
|
1586
|
+
stored.hp = 0;
|
|
1587
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1588
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1589
|
+
if (!showdown) stored.status = 0;
|
|
1590
|
+
side.active.volatiles.LeechSeed = false;
|
|
1591
|
+
side.active.volatiles.DestinyBond = false;
|
|
1601
1592
|
}
|
|
1602
1593
|
|
|
1603
1594
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1712,7 +1703,7 @@ pub const Effects = struct {
|
|
|
1712
1703
|
if (!state.proc) return;
|
|
1713
1704
|
if (foe.conditions.Safeguard) return;
|
|
1714
1705
|
// Freeze Clause Mod
|
|
1715
|
-
if (
|
|
1706
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1716
1707
|
|
|
1717
1708
|
foe_stored.status = Status.init(.FRZ);
|
|
1718
1709
|
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
@@ -1723,8 +1714,26 @@ pub const Effects = struct {
|
|
|
1723
1714
|
foe.active.volatiles.frozen = true;
|
|
1724
1715
|
}
|
|
1725
1716
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1717
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1718
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1719
|
+
var side = battle.side(player);
|
|
1720
|
+
var volatiles = &side.active.volatiles;
|
|
1721
|
+
|
|
1722
|
+
if (state.miss) {
|
|
1723
|
+
volatiles.fury_cutter = 0;
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1728
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1729
|
+
volatiles.fury_cutter += 1;
|
|
1730
|
+
|
|
1731
|
+
if (showdown) {
|
|
1732
|
+
state.bp *= 2;
|
|
1733
|
+
assert(state.bp <= 160);
|
|
1734
|
+
} else {
|
|
1735
|
+
state.damage = smul(state.damage, 2);
|
|
1736
|
+
}
|
|
1728
1737
|
}
|
|
1729
1738
|
|
|
1730
1739
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1885,7 +1894,29 @@ pub const Effects = struct {
|
|
|
1885
1894
|
}
|
|
1886
1895
|
|
|
1887
1896
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1888
|
-
|
|
1897
|
+
var side = battle.side(player);
|
|
1898
|
+
|
|
1899
|
+
if (state.miss) {
|
|
1900
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1901
|
+
try options.log.lastmiss(.{});
|
|
1902
|
+
return options.log.miss(.{battle.active(player)});
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
const move = battle.foe(player).lastMove(false);
|
|
1906
|
+
if (move == .None or move == .Struggle) {
|
|
1907
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
var slot: u4 = 0;
|
|
1911
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1912
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1913
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1914
|
+
}
|
|
1915
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1916
|
+
|
|
1917
|
+
side.active.move(slot).id = move;
|
|
1918
|
+
side.active.move(slot).pp = 5;
|
|
1919
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1889
1920
|
}
|
|
1890
1921
|
|
|
1891
1922
|
pub fn mirrorMove(
|
|
@@ -2172,7 +2203,7 @@ pub const Effects = struct {
|
|
|
2172
2203
|
|
|
2173
2204
|
pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2174
2205
|
// TODO: remove switching variable and just look at Choice saved in Battle struct?
|
|
2175
|
-
if (battle.foe(player).active.volatiles.switching) state.damage
|
|
2206
|
+
if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
|
|
2176
2207
|
}
|
|
2177
2208
|
|
|
2178
2209
|
pub const rage = struct {
|
|
@@ -2185,7 +2216,7 @@ pub const Effects = struct {
|
|
|
2185
2216
|
const side = battle.side(player);
|
|
2186
2217
|
|
|
2187
2218
|
assert(side.active.volatiles.Rage);
|
|
2188
|
-
state.damage
|
|
2219
|
+
state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
|
|
2189
2220
|
}
|
|
2190
2221
|
};
|
|
2191
2222
|
|
|
@@ -2280,7 +2311,7 @@ pub const Effects = struct {
|
|
|
2280
2311
|
side.active.volatiles.attacks += 1;
|
|
2281
2312
|
side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
|
|
2282
2313
|
|
|
2283
|
-
if (side.active.volatiles.DefenseCurl) state.damage
|
|
2314
|
+
if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
|
|
2284
2315
|
}
|
|
2285
2316
|
};
|
|
2286
2317
|
|
|
@@ -2350,7 +2381,7 @@ pub const Effects = struct {
|
|
|
2350
2381
|
}
|
|
2351
2382
|
|
|
2352
2383
|
// Sleep Clause Mod
|
|
2353
|
-
if (
|
|
2384
|
+
if (pkmn.options.mod) {
|
|
2354
2385
|
for (foe.pokemon) |p| {
|
|
2355
2386
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2356
2387
|
}
|
|
@@ -2393,17 +2424,37 @@ pub const Effects = struct {
|
|
|
2393
2424
|
}
|
|
2394
2425
|
|
|
2395
2426
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2396
|
-
|
|
2397
|
-
|
|
2427
|
+
var foe = battle.foe(player);
|
|
2428
|
+
|
|
2429
|
+
const last = foe.lastMove(false);
|
|
2430
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2431
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2432
|
+
}
|
|
2398
2433
|
|
|
2399
|
-
|
|
2434
|
+
var slot: u4 = 0;
|
|
2435
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2436
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2437
|
+
}
|
|
2438
|
+
// If the move was called via Metronome it might not exist
|
|
2439
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2440
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2441
|
+
}
|
|
2400
2442
|
|
|
2401
|
-
//
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2443
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2444
|
+
const pp = @min(
|
|
2445
|
+
foe.active.move(slot).pp,
|
|
2446
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2447
|
+
);
|
|
2448
|
+
foe.active.move(slot).pp -= pp;
|
|
2449
|
+
if (!foe.active.volatiles.Transform) {
|
|
2450
|
+
var move_slot = foe.stored().move(slot);
|
|
2451
|
+
assert(move_slot.pp > 0);
|
|
2452
|
+
|
|
2453
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2454
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2407
2458
|
}
|
|
2408
2459
|
|
|
2409
2460
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2788,9 +2839,15 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2788
2839
|
return t != .@"???" and !active.types.includes(t);
|
|
2789
2840
|
}
|
|
2790
2841
|
|
|
2842
|
+
// NOTE: llvm/llvm-project#58557
|
|
2843
|
+
fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
|
2844
|
+
if (!comptime builtin.target.isWasm()) return a *| b;
|
|
2845
|
+
return @max(a *% b, std.math.maxInt(@TypeOf(a)));
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2791
2848
|
pub const Rolls = struct {
|
|
2792
2849
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2793
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2850
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2794
2851
|
player == .P1
|
|
2795
2852
|
else if (showdown)
|
|
2796
2853
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2802,7 +2859,7 @@ pub const Rolls = struct {
|
|
|
2802
2859
|
}
|
|
2803
2860
|
|
|
2804
2861
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2805
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2862
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2806
2863
|
val == .true
|
|
2807
2864
|
else if (showdown)
|
|
2808
2865
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2814,7 +2871,7 @@ pub const Rolls = struct {
|
|
|
2814
2871
|
}
|
|
2815
2872
|
|
|
2816
2873
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2817
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2874
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2818
2875
|
val == .true
|
|
2819
2876
|
else if (showdown)
|
|
2820
2877
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2825,20 +2882,22 @@ pub const Rolls = struct {
|
|
|
2825
2882
|
return ok;
|
|
2826
2883
|
}
|
|
2827
2884
|
|
|
2828
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2829
|
-
|
|
2885
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2886
|
+
_ = .{ player, options };
|
|
2887
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2830
2888
|
val == .true
|
|
2831
2889
|
else if (showdown)
|
|
2832
2890
|
battle.rng.chance(u8, rate, 256)
|
|
2833
2891
|
else
|
|
2834
2892
|
battle.rng.next() < rate;
|
|
2835
2893
|
|
|
2836
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2894
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2837
2895
|
return crit;
|
|
2838
2896
|
}
|
|
2839
2897
|
|
|
2840
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2841
|
-
|
|
2898
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2899
|
+
_ = .{ player, options };
|
|
2900
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2842
2901
|
val
|
|
2843
2902
|
else roll: {
|
|
2844
2903
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2849,12 +2908,12 @@ pub const Rolls = struct {
|
|
|
2849
2908
|
};
|
|
2850
2909
|
|
|
2851
2910
|
assert(roll >= 217 and roll <= 255);
|
|
2852
|
-
try options.chance.damage(player, roll);
|
|
2911
|
+
try options.chance.damage(player, i, roll);
|
|
2853
2912
|
return roll;
|
|
2854
2913
|
}
|
|
2855
2914
|
|
|
2856
2915
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2857
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2916
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2858
2917
|
val == .true
|
|
2859
2918
|
else if (showdown)
|
|
2860
2919
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2866,7 +2925,7 @@ pub const Rolls = struct {
|
|
|
2866
2925
|
}
|
|
2867
2926
|
|
|
2868
2927
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2869
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2928
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2870
2929
|
val == .true
|
|
2871
2930
|
else if (showdown)
|
|
2872
2931
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2878,7 +2937,7 @@ pub const Rolls = struct {
|
|
|
2878
2937
|
}
|
|
2879
2938
|
|
|
2880
2939
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2881
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2940
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2882
2941
|
val == .true
|
|
2883
2942
|
else if (showdown)
|
|
2884
2943
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2890,7 +2949,7 @@ pub const Rolls = struct {
|
|
|
2890
2949
|
}
|
|
2891
2950
|
|
|
2892
2951
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2893
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2952
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2894
2953
|
val == .true
|
|
2895
2954
|
else if (showdown)
|
|
2896
2955
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2903,7 +2962,7 @@ pub const Rolls = struct {
|
|
|
2903
2962
|
|
|
2904
2963
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2905
2964
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2906
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2965
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2907
2966
|
val == .true
|
|
2908
2967
|
else if (showdown)
|
|
2909
2968
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2915,7 +2974,7 @@ pub const Rolls = struct {
|
|
|
2915
2974
|
}
|
|
2916
2975
|
|
|
2917
2976
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2918
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2977
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2919
2978
|
val == .true
|
|
2920
2979
|
else if (showdown)
|
|
2921
2980
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2927,7 +2986,7 @@ pub const Rolls = struct {
|
|
|
2927
2986
|
}
|
|
2928
2987
|
|
|
2929
2988
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2930
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2989
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2931
2990
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2932
2991
|
else if (showdown)
|
|
2933
2992
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2943,7 +3002,7 @@ pub const Rolls = struct {
|
|
|
2943
3002
|
}
|
|
2944
3003
|
|
|
2945
3004
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2946
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3005
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2947
3006
|
(val - 1) * 40
|
|
2948
3007
|
else if (showdown) power: {
|
|
2949
3008
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2965,7 +3024,7 @@ pub const Rolls = struct {
|
|
|
2965
3024
|
}
|
|
2966
3025
|
|
|
2967
3026
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2968
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3027
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2969
3028
|
val + 3
|
|
2970
3029
|
else if (showdown) num: {
|
|
2971
3030
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2993,7 +3052,7 @@ pub const Rolls = struct {
|
|
|
2993
3052
|
}
|
|
2994
3053
|
|
|
2995
3054
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
2996
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3055
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
2997
3056
|
val
|
|
2998
3057
|
else if (showdown)
|
|
2999
3058
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3009,13 +3068,12 @@ pub const Rolls = struct {
|
|
|
3009
3068
|
return hits;
|
|
3010
3069
|
}
|
|
3011
3070
|
|
|
3012
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3013
|
-
|
|
3071
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3072
|
+
assert(!showdown);
|
|
3073
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3014
3074
|
val + 1
|
|
3015
|
-
else if (showdown)
|
|
3016
|
-
battle.rng.range(u8, 2, 6)
|
|
3017
3075
|
else
|
|
3018
|
-
(battle.rng.next() & 3) + 2;
|
|
3076
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3019
3077
|
|
|
3020
3078
|
assert(pp >= 2 and pp <= 5);
|
|
3021
3079
|
try options.chance.spite(player, pp);
|
|
@@ -3029,7 +3087,7 @@ pub const Rolls = struct {
|
|
|
3029
3087
|
var i: u8 = 0;
|
|
3030
3088
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3031
3089
|
assert(mtype != .@"???");
|
|
3032
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3090
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3033
3091
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3034
3092
|
else if (showdown) ty: {
|
|
3035
3093
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3063,7 +3121,7 @@ pub const Rolls = struct {
|
|
|
3063
3121
|
assert(count <= 8);
|
|
3064
3122
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3065
3123
|
|
|
3066
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3124
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3067
3125
|
val == .true
|
|
3068
3126
|
else if (showdown)
|
|
3069
3127
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3085,7 +3143,7 @@ pub const Rolls = struct {
|
|
|
3085
3143
|
const moves = active.moves;
|
|
3086
3144
|
|
|
3087
3145
|
// TODO: consider throwing error instead of rerolling?
|
|
3088
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3146
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3089
3147
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3090
3148
|
else
|
|
3091
3149
|
null;
|
|
@@ -3122,7 +3180,7 @@ pub const Rolls = struct {
|
|
|
3122
3180
|
const moves = active.moves;
|
|
3123
3181
|
|
|
3124
3182
|
// TODO: consider throwing error instead of rerolling?
|
|
3125
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3183
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3126
3184
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3127
3185
|
else
|
|
3128
3186
|
null;
|
|
@@ -3166,7 +3224,7 @@ pub const Rolls = struct {
|
|
|
3166
3224
|
assert(n > 0 and n <= 6);
|
|
3167
3225
|
|
|
3168
3226
|
// TODO: consider throwing error instead of rerolling?
|
|
3169
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3227
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3170
3228
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3171
3229
|
break :val null;
|
|
3172
3230
|
} else null;
|
|
@@ -3192,7 +3250,7 @@ pub const Rolls = struct {
|
|
|
3192
3250
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3193
3251
|
|
|
3194
3252
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3195
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3253
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3196
3254
|
@intCast(val)
|
|
3197
3255
|
else if (showdown)
|
|
3198
3256
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3207,7 +3265,7 @@ pub const Rolls = struct {
|
|
|
3207
3265
|
}
|
|
3208
3266
|
|
|
3209
3267
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3210
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3268
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3211
3269
|
val
|
|
3212
3270
|
else if (showdown)
|
|
3213
3271
|
battle.rng.range(u8, 1, max)
|
|
@@ -3233,7 +3291,7 @@ pub const Rolls = struct {
|
|
|
3233
3291
|
}
|
|
3234
3292
|
|
|
3235
3293
|
// TODO: consider throwing error instead of rerolling?
|
|
3236
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3294
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3237
3295
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3238
3296
|
break :val val;
|
|
3239
3297
|
} else null;
|
|
@@ -3265,7 +3323,7 @@ pub const Rolls = struct {
|
|
|
3265
3323
|
}
|
|
3266
3324
|
|
|
3267
3325
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3268
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3326
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3269
3327
|
@intCast(val)
|
|
3270
3328
|
else if (showdown)
|
|
3271
3329
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3277,12 +3335,12 @@ pub const Rolls = struct {
|
|
|
3277
3335
|
};
|
|
3278
3336
|
|
|
3279
3337
|
assert(duration >= 1 and duration <= 6);
|
|
3280
|
-
options.chance.duration(
|
|
3338
|
+
options.chance.duration(player, duration);
|
|
3281
3339
|
return duration + 1;
|
|
3282
3340
|
}
|
|
3283
3341
|
|
|
3284
3342
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3285
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3343
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3286
3344
|
@intCast(val)
|
|
3287
3345
|
else if (showdown)
|
|
3288
3346
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3291,16 +3349,16 @@ pub const Rolls = struct {
|
|
|
3291
3349
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3292
3350
|
|
|
3293
3351
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3294
|
-
options.chance.duration(
|
|
3352
|
+
options.chance.duration(player, duration);
|
|
3295
3353
|
return duration;
|
|
3296
3354
|
}
|
|
3297
3355
|
|
|
3298
3356
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3299
|
-
const duration:
|
|
3357
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3300
3358
|
@intCast(val)
|
|
3301
3359
|
else if (showdown)
|
|
3302
3360
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3303
|
-
battle.rng.range(
|
|
3361
|
+
battle.rng.range(u3, 2, 6)
|
|
3304
3362
|
else duration: {
|
|
3305
3363
|
while (true) {
|
|
3306
3364
|
const r = battle.rng.next() & 7;
|
|
@@ -3309,12 +3367,12 @@ pub const Rolls = struct {
|
|
|
3309
3367
|
};
|
|
3310
3368
|
|
|
3311
3369
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3312
|
-
options.chance.duration(
|
|
3370
|
+
options.chance.duration(player, duration);
|
|
3313
3371
|
return duration;
|
|
3314
3372
|
}
|
|
3315
3373
|
|
|
3316
3374
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3317
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3375
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3318
3376
|
@intCast(val)
|
|
3319
3377
|
else if (showdown)
|
|
3320
3378
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3322,12 +3380,12 @@ pub const Rolls = struct {
|
|
|
3322
3380
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3323
3381
|
|
|
3324
3382
|
assert(duration >= 2 and duration <= 3);
|
|
3325
|
-
options.chance.duration(
|
|
3383
|
+
options.chance.duration(player, duration);
|
|
3326
3384
|
return duration;
|
|
3327
3385
|
}
|
|
3328
3386
|
|
|
3329
3387
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3330
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3388
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3331
3389
|
@intCast(val)
|
|
3332
3390
|
else if (showdown)
|
|
3333
3391
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3336,13 +3394,13 @@ pub const Rolls = struct {
|
|
|
3336
3394
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3337
3395
|
|
|
3338
3396
|
assert(duration >= 2 and duration <= 5);
|
|
3339
|
-
options.chance.duration(
|
|
3397
|
+
options.chance.duration(player, duration);
|
|
3340
3398
|
return duration + 1;
|
|
3341
3399
|
}
|
|
3342
3400
|
|
|
3343
3401
|
// TODO: consider sharing implementation with bindingDuration
|
|
3344
3402
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3345
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3403
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3346
3404
|
@intCast(val)
|
|
3347
3405
|
else if (showdown)
|
|
3348
3406
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3350,7 +3408,7 @@ pub const Rolls = struct {
|
|
|
3350
3408
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3351
3409
|
|
|
3352
3410
|
assert(duration >= 2 and duration <= 5);
|
|
3353
|
-
options.chance.duration(
|
|
3411
|
+
options.chance.duration(player, duration);
|
|
3354
3412
|
return duration + 1;
|
|
3355
3413
|
}
|
|
3356
3414
|
};
|