@pkmn/engine 0.1.0-dev.b05836cd → 0.1.0-dev.b9015a94
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 +41 -14
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +43 -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 +96 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon.d.ts +2 -0
- package/build/pkg/addon.js +35 -58
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +27 -5
- 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 +24 -10
- package/build/pkg/index.js +15 -10
- 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 +35 -69
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +128 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +132 -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 +203 -89
- package/package.json +48 -28
- package/src/bin/install-pkmn-engine +30 -20
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +2 -2
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +14 -10
- package/src/lib/common/options.zig +2 -0
- package/src/lib/common/protocol.zig +60 -60
- package/src/lib/common/rational.zig +121 -27
- package/src/lib/common/rng.zig +7 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +509 -131
- package/src/lib/gen1/chance.zig +374 -267
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +17 -16
- package/src/lib/gen1/helpers.zig +95 -538
- package/src/lib/gen1/mechanics.zig +284 -191
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +84 -496
- package/src/lib/gen2/data.zig +8 -7
- package/src/lib/gen2/generated.zig +1 -3
- package/src/lib/gen2/helpers.zig +41 -13
- package/src/lib/gen2/mechanics.zig +178 -127
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +32 -15
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +351 -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
|
@@ -6,7 +6,6 @@ const common = @import("../common/data.zig");
|
|
|
6
6
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
7
|
const protocol = @import("../common/protocol.zig");
|
|
8
8
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
9
|
|
|
11
10
|
const data = @import("data.zig");
|
|
12
11
|
const generated = @import("generated.zig");
|
|
@@ -34,17 +33,12 @@ const Start = protocol.Start;
|
|
|
34
33
|
|
|
35
34
|
const Gen12 = rng.Gen12;
|
|
36
35
|
|
|
37
|
-
const PointerType = util.PointerType;
|
|
38
|
-
|
|
39
36
|
const ActivePokemon = data.ActivePokemon;
|
|
40
37
|
const Boosts = data.Boosts;
|
|
41
38
|
const Effectiveness = data.Effectiveness;
|
|
42
39
|
const Item = data.Item;
|
|
43
40
|
const Move = data.Move;
|
|
44
|
-
const MoveSlot = data.MoveSlot;
|
|
45
|
-
const Pokemon = data.Pokemon;
|
|
46
41
|
const Side = data.Side;
|
|
47
|
-
const Species = data.Species;
|
|
48
42
|
const Stats = data.Stats;
|
|
49
43
|
const Status = data.Status;
|
|
50
44
|
const TriAttack = data.TriAttack;
|
|
@@ -106,6 +100,7 @@ pub const State = struct {
|
|
|
106
100
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
101
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
102
|
move: Move, // wCurPlayerMove
|
|
103
|
+
hit: u3 = 0,
|
|
109
104
|
from: Move = .None,
|
|
110
105
|
mslot: u4, // wCurMoveNum
|
|
111
106
|
first: bool, // wEnemyGoesFirst
|
|
@@ -140,11 +135,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
140
135
|
const p1 = battle.side(.P1);
|
|
141
136
|
const p2 = battle.side(.P2);
|
|
142
137
|
|
|
143
|
-
|
|
138
|
+
const p1_slot = findFirstAlive(p1);
|
|
144
139
|
assert(!showdown or p1_slot == 1);
|
|
145
140
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
146
141
|
|
|
147
|
-
|
|
142
|
+
const p2_slot = findFirstAlive(p2);
|
|
148
143
|
assert(!showdown or p2_slot == 1);
|
|
149
144
|
if (p2_slot == 0) return Result.Win;
|
|
150
145
|
|
|
@@ -256,7 +251,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
251
|
} else {
|
|
257
252
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
253
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
254
|
|
|
261
255
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
256
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -403,7 +397,9 @@ fn executeMove(
|
|
|
403
397
|
|
|
404
398
|
assert(choice.type == .Move);
|
|
405
399
|
var mslot: u4 = @intCast(choice.data);
|
|
400
|
+
_ = &mslot;
|
|
406
401
|
var move = Move.Pound; // TODO
|
|
402
|
+
_ = &move;
|
|
407
403
|
|
|
408
404
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
409
405
|
|
|
@@ -440,18 +436,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
440
436
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
441
437
|
const before = stored.status;
|
|
442
438
|
// Even if the EXT bit is set this will still correctly modify the sleep duration
|
|
443
|
-
// if (options.calc.modify(player, .sleep)) |extend| {
|
|
444
|
-
// if (!extend) stored.status = 0;
|
|
445
|
-
// } else {
|
|
446
439
|
stored.status -= 1;
|
|
447
|
-
// }
|
|
448
440
|
|
|
449
|
-
|
|
450
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
451
|
-
try options.chance.sleep(player, duration);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
if (duration == 0) {
|
|
441
|
+
if (Status.duration(stored.status) == 0) {
|
|
455
442
|
try log.curestatus(.{ ident, before, .Message });
|
|
456
443
|
stored.status = 0; // clears EXT if present
|
|
457
444
|
resetCant(volatiles);
|
|
@@ -481,12 +468,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
481
468
|
}
|
|
482
469
|
|
|
483
470
|
if (volatiles.disable.duration > 0) {
|
|
484
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
485
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
486
|
-
// } else {
|
|
487
471
|
volatiles.disable.duration -= 1;
|
|
488
|
-
// }
|
|
489
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
490
472
|
|
|
491
473
|
if (volatiles.disable.duration == 0) {
|
|
492
474
|
volatiles.disable.move = 0;
|
|
@@ -496,13 +478,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
496
478
|
|
|
497
479
|
if (volatiles.Confusion) {
|
|
498
480
|
assert(volatiles.confusion > 0);
|
|
499
|
-
|
|
500
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
501
|
-
// if (!extend) volatiles.confusion = 0;
|
|
502
|
-
// } else {
|
|
503
481
|
volatiles.confusion -= 1;
|
|
504
|
-
// }
|
|
505
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
506
482
|
|
|
507
483
|
if (volatiles.confusion == 0) {
|
|
508
484
|
volatiles.Confusion = false;
|
|
@@ -565,7 +541,7 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
565
541
|
|
|
566
542
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
567
543
|
var side = battle.side(player);
|
|
568
|
-
|
|
544
|
+
const volatiles = &side.active.volatiles;
|
|
569
545
|
|
|
570
546
|
// FIXME: pass in target to avoid lookup?
|
|
571
547
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
@@ -663,7 +639,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
663
639
|
}
|
|
664
640
|
assert(stage <= 4);
|
|
665
641
|
|
|
666
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
642
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
667
643
|
}
|
|
668
644
|
|
|
669
645
|
pub fn calcDamage(
|
|
@@ -731,7 +707,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
731
707
|
|
|
732
708
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
733
709
|
if (state.damage <= 1) return;
|
|
734
|
-
const random = try Rolls.damage(battle, player, options);
|
|
710
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
735
711
|
options.calc.base(player.foe(), state.damage);
|
|
736
712
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
737
713
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -997,7 +973,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
997
973
|
|
|
998
974
|
// Weather
|
|
999
975
|
if (battle.field.weather != .None) {
|
|
1000
|
-
assert(battle.field.weather_duration > 0);
|
|
1001
976
|
battle.field.weather_duration -= 1;
|
|
1002
977
|
|
|
1003
978
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1056,7 +1031,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1056
1031
|
var volatiles = &side.active.volatiles;
|
|
1057
1032
|
|
|
1058
1033
|
if (volatiles.PerishSong) {
|
|
1059
|
-
assert(volatiles.perish_song > 0);
|
|
1060
1034
|
volatiles.perish_song -= 1;
|
|
1061
1035
|
|
|
1062
1036
|
assert(volatiles.perish_song < 3);
|
|
@@ -1086,7 +1060,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1086
1060
|
|
|
1087
1061
|
// Mystery Berry
|
|
1088
1062
|
inline for (players) |player| {
|
|
1089
|
-
|
|
1063
|
+
const stored = battle.side(player).stored();
|
|
1090
1064
|
|
|
1091
1065
|
if (stored.item == .MysteryBerry) {
|
|
1092
1066
|
// TODO stored and active moves, transform, etc
|
|
@@ -1193,7 +1167,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1193
1167
|
var volatiles = &side.active.volatiles;
|
|
1194
1168
|
|
|
1195
1169
|
if (volatiles.Encore) {
|
|
1196
|
-
assert(volatiles.encore > 0);
|
|
1197
1170
|
volatiles.encore -= 1;
|
|
1198
1171
|
|
|
1199
1172
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
@@ -1215,7 +1188,6 @@ fn updateSideCondition(
|
|
|
1215
1188
|
) !void {
|
|
1216
1189
|
var side = battle.side(player);
|
|
1217
1190
|
if (@field(side.conditions, key)) {
|
|
1218
|
-
assert(@field(side.conditions, value) > 0);
|
|
1219
1191
|
@field(side.conditions, value) -= 1;
|
|
1220
1192
|
|
|
1221
1193
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1306,14 +1278,32 @@ pub const Effects = struct {
|
|
|
1306
1278
|
}
|
|
1307
1279
|
|
|
1308
1280
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1281
|
+
assert(!state.miss);
|
|
1282
|
+
var side = battle.side(player);
|
|
1283
|
+
|
|
1284
|
+
const ident = battle.active(player);
|
|
1285
|
+
|
|
1286
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1287
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1288
|
+
return options.log.fail(.{ ident, .None });
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1292
|
+
if (side.stored().hp <= hp) {
|
|
1293
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1294
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1295
|
+
return options.log.fail(.{ ident, .None });
|
|
1296
|
+
} else {
|
|
1297
|
+
side.stored().hp -= hp;
|
|
1298
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1299
|
+
|
|
1300
|
+
const before = side.active.boosts.atk;
|
|
1301
|
+
var i: usize = 0;
|
|
1302
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1303
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1304
|
+
}
|
|
1305
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1306
|
+
}
|
|
1317
1307
|
}
|
|
1318
1308
|
|
|
1319
1309
|
pub const bide = struct {
|
|
@@ -1325,14 +1315,7 @@ pub const Effects = struct {
|
|
|
1325
1315
|
|
|
1326
1316
|
if (!volatiles.Bide) return;
|
|
1327
1317
|
|
|
1328
|
-
assert(volatiles.attacks > 0);
|
|
1329
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1330
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1331
|
-
// } else {
|
|
1332
1318
|
volatiles.attacks -= 1;
|
|
1333
|
-
// }
|
|
1334
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1335
|
-
|
|
1336
1319
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1337
1320
|
|
|
1338
1321
|
volatiles.Bide = false;
|
|
@@ -1478,8 +1461,8 @@ pub const Effects = struct {
|
|
|
1478
1461
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1479
1462
|
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1480
1463
|
} else {
|
|
1481
|
-
|
|
1482
|
-
|
|
1464
|
+
const stats = &side.active.stats;
|
|
1465
|
+
const boosts = &side.active.boosts;
|
|
1483
1466
|
|
|
1484
1467
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1485
1468
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
@@ -1599,8 +1582,16 @@ pub const Effects = struct {
|
|
|
1599
1582
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1600
1583
|
}
|
|
1601
1584
|
|
|
1602
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1603
|
-
|
|
1585
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1586
|
+
var side = battle.side(player);
|
|
1587
|
+
var stored = side.stored();
|
|
1588
|
+
|
|
1589
|
+
stored.hp = 0;
|
|
1590
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1591
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1592
|
+
if (!showdown) stored.status = 0;
|
|
1593
|
+
side.active.volatiles.LeechSeed = false;
|
|
1594
|
+
side.active.volatiles.DestinyBond = false;
|
|
1604
1595
|
}
|
|
1605
1596
|
|
|
1606
1597
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1726,8 +1717,26 @@ pub const Effects = struct {
|
|
|
1726
1717
|
foe.active.volatiles.frozen = true;
|
|
1727
1718
|
}
|
|
1728
1719
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1720
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1721
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1722
|
+
var side = battle.side(player);
|
|
1723
|
+
var volatiles = &side.active.volatiles;
|
|
1724
|
+
|
|
1725
|
+
if (state.miss) {
|
|
1726
|
+
volatiles.fury_cutter = 0;
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1731
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1732
|
+
volatiles.fury_cutter += 1;
|
|
1733
|
+
|
|
1734
|
+
if (showdown) {
|
|
1735
|
+
state.bp *= 2;
|
|
1736
|
+
assert(state.bp <= 160);
|
|
1737
|
+
} else {
|
|
1738
|
+
state.damage *|= 2;
|
|
1739
|
+
}
|
|
1731
1740
|
}
|
|
1732
1741
|
|
|
1733
1742
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1888,7 +1897,29 @@ pub const Effects = struct {
|
|
|
1888
1897
|
}
|
|
1889
1898
|
|
|
1890
1899
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1891
|
-
|
|
1900
|
+
var side = battle.side(player);
|
|
1901
|
+
|
|
1902
|
+
if (state.miss) {
|
|
1903
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1904
|
+
try options.log.lastmiss(.{});
|
|
1905
|
+
return options.log.miss(.{battle.active(player)});
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
const move = battle.foe(player).lastMove(false);
|
|
1909
|
+
if (move == .None or move == .Struggle) {
|
|
1910
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
var slot: u4 = 0;
|
|
1914
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1915
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1916
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1917
|
+
}
|
|
1918
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1919
|
+
|
|
1920
|
+
side.active.move(slot).id = move;
|
|
1921
|
+
side.active.move(slot).pp = 5;
|
|
1922
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1892
1923
|
}
|
|
1893
1924
|
|
|
1894
1925
|
pub fn mirrorMove(
|
|
@@ -2185,11 +2216,10 @@ pub const Effects = struct {
|
|
|
2185
2216
|
}
|
|
2186
2217
|
|
|
2187
2218
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2188
|
-
|
|
2189
|
-
var volatiles = &side.active.volatiles;
|
|
2219
|
+
const side = battle.side(player);
|
|
2190
2220
|
|
|
2191
|
-
assert(volatiles.Rage);
|
|
2192
|
-
state.damage *|= (volatiles.rage +| 1);
|
|
2221
|
+
assert(side.active.volatiles.Rage);
|
|
2222
|
+
state.damage *|= (side.active.volatiles.rage +| 1);
|
|
2193
2223
|
}
|
|
2194
2224
|
};
|
|
2195
2225
|
|
|
@@ -2397,17 +2427,37 @@ pub const Effects = struct {
|
|
|
2397
2427
|
}
|
|
2398
2428
|
|
|
2399
2429
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2400
|
-
|
|
2401
|
-
// var foe = battle.foe(player);
|
|
2430
|
+
var foe = battle.foe(player);
|
|
2402
2431
|
|
|
2403
|
-
|
|
2432
|
+
const last = foe.lastMove(false);
|
|
2433
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2434
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2435
|
+
}
|
|
2404
2436
|
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
//
|
|
2410
|
-
|
|
2437
|
+
var slot: u4 = 0;
|
|
2438
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2439
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2440
|
+
}
|
|
2441
|
+
// If the move was called via Metronome it might not exist
|
|
2442
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2443
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2447
|
+
const pp = @min(
|
|
2448
|
+
foe.active.move(slot).pp,
|
|
2449
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2450
|
+
);
|
|
2451
|
+
foe.active.move(slot).pp -= pp;
|
|
2452
|
+
if (!foe.active.volatiles.Transform) {
|
|
2453
|
+
var move_slot = foe.stored().move(slot);
|
|
2454
|
+
assert(move_slot.pp > 0);
|
|
2455
|
+
|
|
2456
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2457
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2411
2461
|
}
|
|
2412
2462
|
|
|
2413
2463
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2541,8 +2591,8 @@ pub const Effects = struct {
|
|
|
2541
2591
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2542
2592
|
var side = battle.side(player);
|
|
2543
2593
|
|
|
2544
|
-
|
|
2545
|
-
|
|
2594
|
+
const stats = &side.active.stats;
|
|
2595
|
+
const boosts = &side.active.boosts;
|
|
2546
2596
|
|
|
2547
2597
|
if (!state.proc) return;
|
|
2548
2598
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
@@ -2563,7 +2613,7 @@ pub const Effects = struct {
|
|
|
2563
2613
|
|
|
2564
2614
|
if (state.miss) return log.fail(.{ ident, .None });
|
|
2565
2615
|
|
|
2566
|
-
|
|
2616
|
+
const stats = &side.active.stats;
|
|
2567
2617
|
var boosts = &side.active.boosts;
|
|
2568
2618
|
|
|
2569
2619
|
const move = Move.get(state.move);
|
|
@@ -2634,7 +2684,7 @@ pub const Effects = struct {
|
|
|
2634
2684
|
|
|
2635
2685
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2636
2686
|
|
|
2637
|
-
|
|
2687
|
+
const stats = &foe.active.stats;
|
|
2638
2688
|
var boosts = &foe.active.boosts;
|
|
2639
2689
|
|
|
2640
2690
|
const move = Move.get(state.move);
|
|
@@ -2718,9 +2768,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2718
2768
|
}
|
|
2719
2769
|
|
|
2720
2770
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2721
|
-
|
|
2771
|
+
const stats = unmodifiedStats(battle, side);
|
|
2722
2772
|
inline for (STATS) |s| {
|
|
2723
|
-
|
|
2773
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2724
2774
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2725
2775
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2726
2776
|
}
|
|
@@ -2743,7 +2793,7 @@ fn boostStat(
|
|
|
2743
2793
|
n: u2,
|
|
2744
2794
|
) !void {
|
|
2745
2795
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2746
|
-
|
|
2796
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2747
2797
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2748
2798
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2749
2799
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2766,7 +2816,7 @@ fn unboostStat(
|
|
|
2766
2816
|
n: u2,
|
|
2767
2817
|
) !void {
|
|
2768
2818
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2769
|
-
|
|
2819
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2770
2820
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2771
2821
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2772
2822
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2794,7 +2844,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2794
2844
|
|
|
2795
2845
|
pub const Rolls = struct {
|
|
2796
2846
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2797
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2847
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2798
2848
|
player == .P1
|
|
2799
2849
|
else if (showdown)
|
|
2800
2850
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2806,7 +2856,7 @@ pub const Rolls = struct {
|
|
|
2806
2856
|
}
|
|
2807
2857
|
|
|
2808
2858
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2809
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2859
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2810
2860
|
val == .true
|
|
2811
2861
|
else if (showdown)
|
|
2812
2862
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2818,7 +2868,7 @@ pub const Rolls = struct {
|
|
|
2818
2868
|
}
|
|
2819
2869
|
|
|
2820
2870
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2821
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2871
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2822
2872
|
val == .true
|
|
2823
2873
|
else if (showdown)
|
|
2824
2874
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2829,20 +2879,22 @@ pub const Rolls = struct {
|
|
|
2829
2879
|
return ok;
|
|
2830
2880
|
}
|
|
2831
2881
|
|
|
2832
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2833
|
-
|
|
2882
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2883
|
+
_ = .{ player, options };
|
|
2884
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2834
2885
|
val == .true
|
|
2835
2886
|
else if (showdown)
|
|
2836
2887
|
battle.rng.chance(u8, rate, 256)
|
|
2837
2888
|
else
|
|
2838
2889
|
battle.rng.next() < rate;
|
|
2839
2890
|
|
|
2840
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2891
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2841
2892
|
return crit;
|
|
2842
2893
|
}
|
|
2843
2894
|
|
|
2844
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2845
|
-
|
|
2895
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2896
|
+
_ = .{ player, options };
|
|
2897
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2846
2898
|
val
|
|
2847
2899
|
else roll: {
|
|
2848
2900
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2853,12 +2905,12 @@ pub const Rolls = struct {
|
|
|
2853
2905
|
};
|
|
2854
2906
|
|
|
2855
2907
|
assert(roll >= 217 and roll <= 255);
|
|
2856
|
-
try options.chance.damage(player, roll);
|
|
2908
|
+
try options.chance.damage(player, i, roll);
|
|
2857
2909
|
return roll;
|
|
2858
2910
|
}
|
|
2859
2911
|
|
|
2860
2912
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2861
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2913
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2862
2914
|
val == .true
|
|
2863
2915
|
else if (showdown)
|
|
2864
2916
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2870,7 +2922,7 @@ pub const Rolls = struct {
|
|
|
2870
2922
|
}
|
|
2871
2923
|
|
|
2872
2924
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2873
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2925
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2874
2926
|
val == .true
|
|
2875
2927
|
else if (showdown)
|
|
2876
2928
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2882,7 +2934,7 @@ pub const Rolls = struct {
|
|
|
2882
2934
|
}
|
|
2883
2935
|
|
|
2884
2936
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2885
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2937
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2886
2938
|
val == .true
|
|
2887
2939
|
else if (showdown)
|
|
2888
2940
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2894,7 +2946,7 @@ pub const Rolls = struct {
|
|
|
2894
2946
|
}
|
|
2895
2947
|
|
|
2896
2948
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2897
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2949
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2898
2950
|
val == .true
|
|
2899
2951
|
else if (showdown)
|
|
2900
2952
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2907,7 +2959,7 @@ pub const Rolls = struct {
|
|
|
2907
2959
|
|
|
2908
2960
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2909
2961
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2910
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2962
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2911
2963
|
val == .true
|
|
2912
2964
|
else if (showdown)
|
|
2913
2965
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2919,7 +2971,7 @@ pub const Rolls = struct {
|
|
|
2919
2971
|
}
|
|
2920
2972
|
|
|
2921
2973
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2922
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2974
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2923
2975
|
val == .true
|
|
2924
2976
|
else if (showdown)
|
|
2925
2977
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2931,7 +2983,7 @@ pub const Rolls = struct {
|
|
|
2931
2983
|
}
|
|
2932
2984
|
|
|
2933
2985
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2934
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2986
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2935
2987
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2936
2988
|
else if (showdown)
|
|
2937
2989
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2947,7 +2999,7 @@ pub const Rolls = struct {
|
|
|
2947
2999
|
}
|
|
2948
3000
|
|
|
2949
3001
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2950
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3002
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2951
3003
|
(val - 1) * 40
|
|
2952
3004
|
else if (showdown) power: {
|
|
2953
3005
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2969,7 +3021,7 @@ pub const Rolls = struct {
|
|
|
2969
3021
|
}
|
|
2970
3022
|
|
|
2971
3023
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2972
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3024
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2973
3025
|
val + 3
|
|
2974
3026
|
else if (showdown) num: {
|
|
2975
3027
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2997,7 +3049,7 @@ pub const Rolls = struct {
|
|
|
2997
3049
|
}
|
|
2998
3050
|
|
|
2999
3051
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
3000
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3052
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
3001
3053
|
val
|
|
3002
3054
|
else if (showdown)
|
|
3003
3055
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3013,13 +3065,12 @@ pub const Rolls = struct {
|
|
|
3013
3065
|
return hits;
|
|
3014
3066
|
}
|
|
3015
3067
|
|
|
3016
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3017
|
-
|
|
3068
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3069
|
+
assert(!showdown);
|
|
3070
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3018
3071
|
val + 1
|
|
3019
|
-
else if (showdown)
|
|
3020
|
-
battle.rng.range(u8, 2, 6)
|
|
3021
3072
|
else
|
|
3022
|
-
(battle.rng.next() & 3) + 2;
|
|
3073
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3023
3074
|
|
|
3024
3075
|
assert(pp >= 2 and pp <= 5);
|
|
3025
3076
|
try options.chance.spite(player, pp);
|
|
@@ -3033,7 +3084,7 @@ pub const Rolls = struct {
|
|
|
3033
3084
|
var i: u8 = 0;
|
|
3034
3085
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3035
3086
|
assert(mtype != .@"???");
|
|
3036
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3087
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3037
3088
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3038
3089
|
else if (showdown) ty: {
|
|
3039
3090
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3067,7 +3118,7 @@ pub const Rolls = struct {
|
|
|
3067
3118
|
assert(count <= 8);
|
|
3068
3119
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3069
3120
|
|
|
3070
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3121
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3071
3122
|
val == .true
|
|
3072
3123
|
else if (showdown)
|
|
3073
3124
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3089,7 +3140,7 @@ pub const Rolls = struct {
|
|
|
3089
3140
|
const moves = active.moves;
|
|
3090
3141
|
|
|
3091
3142
|
// TODO: consider throwing error instead of rerolling?
|
|
3092
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3143
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3093
3144
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3094
3145
|
else
|
|
3095
3146
|
null;
|
|
@@ -3126,7 +3177,7 @@ pub const Rolls = struct {
|
|
|
3126
3177
|
const moves = active.moves;
|
|
3127
3178
|
|
|
3128
3179
|
// TODO: consider throwing error instead of rerolling?
|
|
3129
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3180
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3130
3181
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3131
3182
|
else
|
|
3132
3183
|
null;
|
|
@@ -3170,7 +3221,7 @@ pub const Rolls = struct {
|
|
|
3170
3221
|
assert(n > 0 and n <= 6);
|
|
3171
3222
|
|
|
3172
3223
|
// TODO: consider throwing error instead of rerolling?
|
|
3173
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3224
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3174
3225
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3175
3226
|
break :val null;
|
|
3176
3227
|
} else null;
|
|
@@ -3196,7 +3247,7 @@ pub const Rolls = struct {
|
|
|
3196
3247
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3197
3248
|
|
|
3198
3249
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3199
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3250
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3200
3251
|
@intCast(val)
|
|
3201
3252
|
else if (showdown)
|
|
3202
3253
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3211,7 +3262,7 @@ pub const Rolls = struct {
|
|
|
3211
3262
|
}
|
|
3212
3263
|
|
|
3213
3264
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3214
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3265
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3215
3266
|
val
|
|
3216
3267
|
else if (showdown)
|
|
3217
3268
|
battle.rng.range(u8, 1, max)
|
|
@@ -3237,7 +3288,7 @@ pub const Rolls = struct {
|
|
|
3237
3288
|
}
|
|
3238
3289
|
|
|
3239
3290
|
// TODO: consider throwing error instead of rerolling?
|
|
3240
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3291
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3241
3292
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3242
3293
|
break :val val;
|
|
3243
3294
|
} else null;
|
|
@@ -3269,7 +3320,7 @@ pub const Rolls = struct {
|
|
|
3269
3320
|
}
|
|
3270
3321
|
|
|
3271
3322
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3272
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3323
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3273
3324
|
@intCast(val)
|
|
3274
3325
|
else if (showdown)
|
|
3275
3326
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3281,12 +3332,12 @@ pub const Rolls = struct {
|
|
|
3281
3332
|
};
|
|
3282
3333
|
|
|
3283
3334
|
assert(duration >= 1 and duration <= 6);
|
|
3284
|
-
options.chance.duration(
|
|
3335
|
+
options.chance.duration(player, duration);
|
|
3285
3336
|
return duration + 1;
|
|
3286
3337
|
}
|
|
3287
3338
|
|
|
3288
3339
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3289
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3340
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3290
3341
|
@intCast(val)
|
|
3291
3342
|
else if (showdown)
|
|
3292
3343
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3295,16 +3346,16 @@ pub const Rolls = struct {
|
|
|
3295
3346
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3296
3347
|
|
|
3297
3348
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3298
|
-
options.chance.duration(
|
|
3349
|
+
options.chance.duration(player, duration);
|
|
3299
3350
|
return duration;
|
|
3300
3351
|
}
|
|
3301
3352
|
|
|
3302
3353
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3303
|
-
const duration:
|
|
3354
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3304
3355
|
@intCast(val)
|
|
3305
3356
|
else if (showdown)
|
|
3306
3357
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3307
|
-
battle.rng.range(
|
|
3358
|
+
battle.rng.range(u3, 2, 6)
|
|
3308
3359
|
else duration: {
|
|
3309
3360
|
while (true) {
|
|
3310
3361
|
const r = battle.rng.next() & 7;
|
|
@@ -3313,12 +3364,12 @@ pub const Rolls = struct {
|
|
|
3313
3364
|
};
|
|
3314
3365
|
|
|
3315
3366
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3316
|
-
options.chance.duration(
|
|
3367
|
+
options.chance.duration(player, duration);
|
|
3317
3368
|
return duration;
|
|
3318
3369
|
}
|
|
3319
3370
|
|
|
3320
3371
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3321
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3372
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3322
3373
|
@intCast(val)
|
|
3323
3374
|
else if (showdown)
|
|
3324
3375
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3326,12 +3377,12 @@ pub const Rolls = struct {
|
|
|
3326
3377
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3327
3378
|
|
|
3328
3379
|
assert(duration >= 2 and duration <= 3);
|
|
3329
|
-
options.chance.duration(
|
|
3380
|
+
options.chance.duration(player, duration);
|
|
3330
3381
|
return duration;
|
|
3331
3382
|
}
|
|
3332
3383
|
|
|
3333
3384
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3334
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3385
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3335
3386
|
@intCast(val)
|
|
3336
3387
|
else if (showdown)
|
|
3337
3388
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3340,13 +3391,13 @@ pub const Rolls = struct {
|
|
|
3340
3391
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3341
3392
|
|
|
3342
3393
|
assert(duration >= 2 and duration <= 5);
|
|
3343
|
-
options.chance.duration(
|
|
3394
|
+
options.chance.duration(player, duration);
|
|
3344
3395
|
return duration + 1;
|
|
3345
3396
|
}
|
|
3346
3397
|
|
|
3347
3398
|
// TODO: consider sharing implementation with bindingDuration
|
|
3348
3399
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3349
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3400
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3350
3401
|
@intCast(val)
|
|
3351
3402
|
else if (showdown)
|
|
3352
3403
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3354,7 +3405,7 @@ pub const Rolls = struct {
|
|
|
3354
3405
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3355
3406
|
|
|
3356
3407
|
assert(duration >= 2 and duration <= 5);
|
|
3357
|
-
options.chance.duration(
|
|
3408
|
+
options.chance.duration(player, duration);
|
|
3358
3409
|
return duration + 1;
|
|
3359
3410
|
}
|
|
3360
3411
|
};
|
|
@@ -3467,7 +3518,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3467
3518
|
.Move => {
|
|
3468
3519
|
const side = battle.side(player);
|
|
3469
3520
|
|
|
3470
|
-
|
|
3521
|
+
const active = &side.active;
|
|
3471
3522
|
const stored = side.stored();
|
|
3472
3523
|
|
|
3473
3524
|
// While players are not given any input options on the cartridge in these cases,
|