@pkmn/engine 0.1.0-dev.c544e10e → 0.1.0-dev.cbdbb719
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.js +6 -7
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +4 -4
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +3 -2
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.js +4 -4
- package/build/pkg/protocol.js.map +1 -1
- package/build/test/display.js +8 -3
- package/build/test/display.js.map +1 -1
- package/build/tools/debug.js +2 -3
- package/build/tools/debug.js.map +1 -1
- package/build.zig +208 -81
- package/package.json +32 -21
- package/src/bin/install-pkmn-engine +25 -17
- 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 +73 -73
- 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 +511 -131
- package/src/lib/gen1/chance.zig +374 -267
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +15 -14
- package/src/lib/gen1/helpers.zig +96 -536
- package/src/lib/gen1/mechanics.zig +285 -189
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +85 -492
- package/src/lib/gen2/data.zig +8 -7
- package/src/lib/gen2/generated.zig +1 -3
- package/src/lib/gen2/helpers.zig +41 -9
- package/src/lib/gen2/mechanics.zig +178 -121
- 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/test/display/pkmn.html.tmpl +5 -5
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
|
@@ -106,6 +106,7 @@ pub const State = struct {
|
|
|
106
106
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
107
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
108
|
move: Move, // wCurPlayerMove
|
|
109
|
+
hit: u3 = 0,
|
|
109
110
|
from: Move = .None,
|
|
110
111
|
mslot: u4, // wCurMoveNum
|
|
111
112
|
first: bool, // wEnemyGoesFirst
|
|
@@ -140,11 +141,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
140
141
|
const p1 = battle.side(.P1);
|
|
141
142
|
const p2 = battle.side(.P2);
|
|
142
143
|
|
|
143
|
-
|
|
144
|
+
const p1_slot = findFirstAlive(p1);
|
|
144
145
|
assert(!showdown or p1_slot == 1);
|
|
145
146
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
146
147
|
|
|
147
|
-
|
|
148
|
+
const p2_slot = findFirstAlive(p2);
|
|
148
149
|
assert(!showdown or p2_slot == 1);
|
|
149
150
|
if (p2_slot == 0) return Result.Win;
|
|
150
151
|
|
|
@@ -256,7 +257,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
257
|
} else {
|
|
257
258
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
259
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
260
|
|
|
261
261
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
262
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -403,7 +403,9 @@ fn executeMove(
|
|
|
403
403
|
|
|
404
404
|
assert(choice.type == .Move);
|
|
405
405
|
var mslot: u4 = @intCast(choice.data);
|
|
406
|
+
_ = &mslot;
|
|
406
407
|
var move = Move.Pound; // TODO
|
|
408
|
+
_ = &move;
|
|
407
409
|
|
|
408
410
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
409
411
|
|
|
@@ -440,18 +442,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
440
442
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
441
443
|
const before = stored.status;
|
|
442
444
|
// 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
445
|
stored.status -= 1;
|
|
447
|
-
// }
|
|
448
|
-
|
|
449
|
-
const duration = Status.duration(stored.status);
|
|
450
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
451
|
-
try options.chance.sleep(player, duration);
|
|
452
|
-
}
|
|
453
446
|
|
|
454
|
-
if (duration == 0) {
|
|
447
|
+
if (Status.duration(stored.status) == 0) {
|
|
455
448
|
try log.curestatus(.{ ident, before, .Message });
|
|
456
449
|
stored.status = 0; // clears EXT if present
|
|
457
450
|
resetCant(volatiles);
|
|
@@ -481,12 +474,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
481
474
|
}
|
|
482
475
|
|
|
483
476
|
if (volatiles.disable.duration > 0) {
|
|
484
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
485
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
486
|
-
// } else {
|
|
487
477
|
volatiles.disable.duration -= 1;
|
|
488
|
-
// }
|
|
489
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
490
478
|
|
|
491
479
|
if (volatiles.disable.duration == 0) {
|
|
492
480
|
volatiles.disable.move = 0;
|
|
@@ -496,13 +484,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
496
484
|
|
|
497
485
|
if (volatiles.Confusion) {
|
|
498
486
|
assert(volatiles.confusion > 0);
|
|
499
|
-
|
|
500
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
501
|
-
// if (!extend) volatiles.confusion = 0;
|
|
502
|
-
// } else {
|
|
503
487
|
volatiles.confusion -= 1;
|
|
504
|
-
// }
|
|
505
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
506
488
|
|
|
507
489
|
if (volatiles.confusion == 0) {
|
|
508
490
|
volatiles.Confusion = false;
|
|
@@ -565,7 +547,7 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
565
547
|
|
|
566
548
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
567
549
|
var side = battle.side(player);
|
|
568
|
-
|
|
550
|
+
const volatiles = &side.active.volatiles;
|
|
569
551
|
|
|
570
552
|
// FIXME: pass in target to avoid lookup?
|
|
571
553
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
@@ -663,7 +645,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
663
645
|
}
|
|
664
646
|
assert(stage <= 4);
|
|
665
647
|
|
|
666
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
648
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
667
649
|
}
|
|
668
650
|
|
|
669
651
|
pub fn calcDamage(
|
|
@@ -731,7 +713,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
731
713
|
|
|
732
714
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
733
715
|
if (state.damage <= 1) return;
|
|
734
|
-
const random = try Rolls.damage(battle, player, options);
|
|
716
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
735
717
|
options.calc.base(player.foe(), state.damage);
|
|
736
718
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
737
719
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -997,7 +979,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
997
979
|
|
|
998
980
|
// Weather
|
|
999
981
|
if (battle.field.weather != .None) {
|
|
1000
|
-
assert(battle.field.weather_duration > 0);
|
|
1001
982
|
battle.field.weather_duration -= 1;
|
|
1002
983
|
|
|
1003
984
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1056,7 +1037,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1056
1037
|
var volatiles = &side.active.volatiles;
|
|
1057
1038
|
|
|
1058
1039
|
if (volatiles.PerishSong) {
|
|
1059
|
-
assert(volatiles.perish_song > 0);
|
|
1060
1040
|
volatiles.perish_song -= 1;
|
|
1061
1041
|
|
|
1062
1042
|
assert(volatiles.perish_song < 3);
|
|
@@ -1086,7 +1066,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1086
1066
|
|
|
1087
1067
|
// Mystery Berry
|
|
1088
1068
|
inline for (players) |player| {
|
|
1089
|
-
|
|
1069
|
+
const stored = battle.side(player).stored();
|
|
1090
1070
|
|
|
1091
1071
|
if (stored.item == .MysteryBerry) {
|
|
1092
1072
|
// TODO stored and active moves, transform, etc
|
|
@@ -1193,7 +1173,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1193
1173
|
var volatiles = &side.active.volatiles;
|
|
1194
1174
|
|
|
1195
1175
|
if (volatiles.Encore) {
|
|
1196
|
-
assert(volatiles.encore > 0);
|
|
1197
1176
|
volatiles.encore -= 1;
|
|
1198
1177
|
|
|
1199
1178
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
@@ -1215,7 +1194,6 @@ fn updateSideCondition(
|
|
|
1215
1194
|
) !void {
|
|
1216
1195
|
var side = battle.side(player);
|
|
1217
1196
|
if (@field(side.conditions, key)) {
|
|
1218
|
-
assert(@field(side.conditions, value) > 0);
|
|
1219
1197
|
@field(side.conditions, value) -= 1;
|
|
1220
1198
|
|
|
1221
1199
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1306,14 +1284,32 @@ pub const Effects = struct {
|
|
|
1306
1284
|
}
|
|
1307
1285
|
|
|
1308
1286
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1287
|
+
assert(!state.miss);
|
|
1288
|
+
var side = battle.side(player);
|
|
1289
|
+
|
|
1290
|
+
const ident = battle.active(player);
|
|
1291
|
+
|
|
1292
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1293
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1294
|
+
return options.log.fail(.{ ident, .None });
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1298
|
+
if (side.stored().hp <= hp) {
|
|
1299
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1300
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1301
|
+
return options.log.fail(.{ ident, .None });
|
|
1302
|
+
} else {
|
|
1303
|
+
side.stored().hp -= hp;
|
|
1304
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1305
|
+
|
|
1306
|
+
const before = side.active.boosts.atk;
|
|
1307
|
+
var i: usize = 0;
|
|
1308
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1309
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1310
|
+
}
|
|
1311
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1312
|
+
}
|
|
1317
1313
|
}
|
|
1318
1314
|
|
|
1319
1315
|
pub const bide = struct {
|
|
@@ -1325,14 +1321,7 @@ pub const Effects = struct {
|
|
|
1325
1321
|
|
|
1326
1322
|
if (!volatiles.Bide) return;
|
|
1327
1323
|
|
|
1328
|
-
assert(volatiles.attacks > 0);
|
|
1329
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1330
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1331
|
-
// } else {
|
|
1332
1324
|
volatiles.attacks -= 1;
|
|
1333
|
-
// }
|
|
1334
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1335
|
-
|
|
1336
1325
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1337
1326
|
|
|
1338
1327
|
volatiles.Bide = false;
|
|
@@ -1478,8 +1467,8 @@ pub const Effects = struct {
|
|
|
1478
1467
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1479
1468
|
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1480
1469
|
} else {
|
|
1481
|
-
|
|
1482
|
-
|
|
1470
|
+
const stats = &side.active.stats;
|
|
1471
|
+
const boosts = &side.active.boosts;
|
|
1483
1472
|
|
|
1484
1473
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1485
1474
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
@@ -1599,8 +1588,16 @@ pub const Effects = struct {
|
|
|
1599
1588
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1600
1589
|
}
|
|
1601
1590
|
|
|
1602
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1603
|
-
|
|
1591
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1592
|
+
var side = battle.side(player);
|
|
1593
|
+
var stored = side.stored();
|
|
1594
|
+
|
|
1595
|
+
stored.hp = 0;
|
|
1596
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1597
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1598
|
+
if (!showdown) stored.status = 0;
|
|
1599
|
+
side.active.volatiles.LeechSeed = false;
|
|
1600
|
+
side.active.volatiles.DestinyBond = false;
|
|
1604
1601
|
}
|
|
1605
1602
|
|
|
1606
1603
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1726,8 +1723,26 @@ pub const Effects = struct {
|
|
|
1726
1723
|
foe.active.volatiles.frozen = true;
|
|
1727
1724
|
}
|
|
1728
1725
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1726
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1727
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1728
|
+
var side = battle.side(player);
|
|
1729
|
+
var volatiles = &side.active.volatiles;
|
|
1730
|
+
|
|
1731
|
+
if (state.miss) {
|
|
1732
|
+
volatiles.fury_cutter = 0;
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1737
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1738
|
+
volatiles.fury_cutter += 1;
|
|
1739
|
+
|
|
1740
|
+
if (showdown) {
|
|
1741
|
+
state.bp *= 2;
|
|
1742
|
+
assert(state.bp <= 160);
|
|
1743
|
+
} else {
|
|
1744
|
+
state.damage *|= 2;
|
|
1745
|
+
}
|
|
1731
1746
|
}
|
|
1732
1747
|
|
|
1733
1748
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1888,7 +1903,29 @@ pub const Effects = struct {
|
|
|
1888
1903
|
}
|
|
1889
1904
|
|
|
1890
1905
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1891
|
-
|
|
1906
|
+
var side = battle.side(player);
|
|
1907
|
+
|
|
1908
|
+
if (state.miss) {
|
|
1909
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1910
|
+
try options.log.lastmiss(.{});
|
|
1911
|
+
return options.log.miss(.{battle.active(player)});
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
const move = battle.foe(player).lastMove(false);
|
|
1915
|
+
if (move == .None or move == .Struggle) {
|
|
1916
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
var slot: u4 = 0;
|
|
1920
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1921
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1922
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1923
|
+
}
|
|
1924
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1925
|
+
|
|
1926
|
+
side.active.move(slot).id = move;
|
|
1927
|
+
side.active.move(slot).pp = 5;
|
|
1928
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1892
1929
|
}
|
|
1893
1930
|
|
|
1894
1931
|
pub fn mirrorMove(
|
|
@@ -2185,11 +2222,10 @@ pub const Effects = struct {
|
|
|
2185
2222
|
}
|
|
2186
2223
|
|
|
2187
2224
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2188
|
-
|
|
2189
|
-
var volatiles = &side.active.volatiles;
|
|
2225
|
+
const side = battle.side(player);
|
|
2190
2226
|
|
|
2191
|
-
assert(volatiles.Rage);
|
|
2192
|
-
state.damage *|= (volatiles.rage +| 1);
|
|
2227
|
+
assert(side.active.volatiles.Rage);
|
|
2228
|
+
state.damage *|= (side.active.volatiles.rage +| 1);
|
|
2193
2229
|
}
|
|
2194
2230
|
};
|
|
2195
2231
|
|
|
@@ -2397,17 +2433,37 @@ pub const Effects = struct {
|
|
|
2397
2433
|
}
|
|
2398
2434
|
|
|
2399
2435
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2400
|
-
|
|
2401
|
-
// var foe = battle.foe(player);
|
|
2436
|
+
var foe = battle.foe(player);
|
|
2402
2437
|
|
|
2403
|
-
|
|
2438
|
+
const last = foe.lastMove(false);
|
|
2439
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2440
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2441
|
+
}
|
|
2404
2442
|
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
//
|
|
2410
|
-
|
|
2443
|
+
var slot: u4 = 0;
|
|
2444
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2445
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2446
|
+
}
|
|
2447
|
+
// If the move was called via Metronome it might not exist
|
|
2448
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2449
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2453
|
+
const pp = @min(
|
|
2454
|
+
foe.active.move(slot).pp,
|
|
2455
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2456
|
+
);
|
|
2457
|
+
foe.active.move(slot).pp -= pp;
|
|
2458
|
+
if (!foe.active.volatiles.Transform) {
|
|
2459
|
+
var move_slot = foe.stored().move(slot);
|
|
2460
|
+
assert(move_slot.pp > 0);
|
|
2461
|
+
|
|
2462
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2463
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2411
2467
|
}
|
|
2412
2468
|
|
|
2413
2469
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2541,8 +2597,8 @@ pub const Effects = struct {
|
|
|
2541
2597
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2542
2598
|
var side = battle.side(player);
|
|
2543
2599
|
|
|
2544
|
-
|
|
2545
|
-
|
|
2600
|
+
const stats = &side.active.stats;
|
|
2601
|
+
const boosts = &side.active.boosts;
|
|
2546
2602
|
|
|
2547
2603
|
if (!state.proc) return;
|
|
2548
2604
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
@@ -2563,7 +2619,7 @@ pub const Effects = struct {
|
|
|
2563
2619
|
|
|
2564
2620
|
if (state.miss) return log.fail(.{ ident, .None });
|
|
2565
2621
|
|
|
2566
|
-
|
|
2622
|
+
const stats = &side.active.stats;
|
|
2567
2623
|
var boosts = &side.active.boosts;
|
|
2568
2624
|
|
|
2569
2625
|
const move = Move.get(state.move);
|
|
@@ -2634,7 +2690,7 @@ pub const Effects = struct {
|
|
|
2634
2690
|
|
|
2635
2691
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2636
2692
|
|
|
2637
|
-
|
|
2693
|
+
const stats = &foe.active.stats;
|
|
2638
2694
|
var boosts = &foe.active.boosts;
|
|
2639
2695
|
|
|
2640
2696
|
const move = Move.get(state.move);
|
|
@@ -2718,9 +2774,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2718
2774
|
}
|
|
2719
2775
|
|
|
2720
2776
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2721
|
-
|
|
2777
|
+
const stats = unmodifiedStats(battle, side);
|
|
2722
2778
|
inline for (STATS) |s| {
|
|
2723
|
-
|
|
2779
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2724
2780
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2725
2781
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2726
2782
|
}
|
|
@@ -2743,7 +2799,7 @@ fn boostStat(
|
|
|
2743
2799
|
n: u2,
|
|
2744
2800
|
) !void {
|
|
2745
2801
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2746
|
-
|
|
2802
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2747
2803
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2748
2804
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2749
2805
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2766,7 +2822,7 @@ fn unboostStat(
|
|
|
2766
2822
|
n: u2,
|
|
2767
2823
|
) !void {
|
|
2768
2824
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2769
|
-
|
|
2825
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2770
2826
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2771
2827
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2772
2828
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2794,7 +2850,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2794
2850
|
|
|
2795
2851
|
pub const Rolls = struct {
|
|
2796
2852
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2797
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2853
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2798
2854
|
player == .P1
|
|
2799
2855
|
else if (showdown)
|
|
2800
2856
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2806,7 +2862,7 @@ pub const Rolls = struct {
|
|
|
2806
2862
|
}
|
|
2807
2863
|
|
|
2808
2864
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2809
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2865
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2810
2866
|
val == .true
|
|
2811
2867
|
else if (showdown)
|
|
2812
2868
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2818,7 +2874,7 @@ pub const Rolls = struct {
|
|
|
2818
2874
|
}
|
|
2819
2875
|
|
|
2820
2876
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2821
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2877
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2822
2878
|
val == .true
|
|
2823
2879
|
else if (showdown)
|
|
2824
2880
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2829,21 +2885,23 @@ pub const Rolls = struct {
|
|
|
2829
2885
|
return ok;
|
|
2830
2886
|
}
|
|
2831
2887
|
|
|
2832
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2833
|
-
|
|
2888
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2889
|
+
_ = .{ player, options };
|
|
2890
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2834
2891
|
val == .true
|
|
2835
2892
|
else if (showdown)
|
|
2836
2893
|
battle.rng.chance(u8, rate, 256)
|
|
2837
2894
|
else
|
|
2838
2895
|
battle.rng.next() < rate;
|
|
2839
2896
|
|
|
2840
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2897
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2841
2898
|
return crit;
|
|
2842
2899
|
}
|
|
2843
2900
|
|
|
2844
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2845
|
-
|
|
2846
|
-
|
|
2901
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2902
|
+
_ = .{ player, options };
|
|
2903
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2904
|
+
@as(u8, val) + 216
|
|
2847
2905
|
else roll: {
|
|
2848
2906
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
2849
2907
|
while (true) {
|
|
@@ -2853,12 +2911,12 @@ pub const Rolls = struct {
|
|
|
2853
2911
|
};
|
|
2854
2912
|
|
|
2855
2913
|
assert(roll >= 217 and roll <= 255);
|
|
2856
|
-
try options.chance.damage(player, roll);
|
|
2914
|
+
try options.chance.damage(player, i, roll);
|
|
2857
2915
|
return roll;
|
|
2858
2916
|
}
|
|
2859
2917
|
|
|
2860
2918
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2861
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2919
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2862
2920
|
val == .true
|
|
2863
2921
|
else if (showdown)
|
|
2864
2922
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2870,7 +2928,7 @@ pub const Rolls = struct {
|
|
|
2870
2928
|
}
|
|
2871
2929
|
|
|
2872
2930
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2873
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2931
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2874
2932
|
val == .true
|
|
2875
2933
|
else if (showdown)
|
|
2876
2934
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2882,7 +2940,7 @@ pub const Rolls = struct {
|
|
|
2882
2940
|
}
|
|
2883
2941
|
|
|
2884
2942
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2885
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2943
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2886
2944
|
val == .true
|
|
2887
2945
|
else if (showdown)
|
|
2888
2946
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2894,7 +2952,7 @@ pub const Rolls = struct {
|
|
|
2894
2952
|
}
|
|
2895
2953
|
|
|
2896
2954
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2897
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2955
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2898
2956
|
val == .true
|
|
2899
2957
|
else if (showdown)
|
|
2900
2958
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2907,7 +2965,7 @@ pub const Rolls = struct {
|
|
|
2907
2965
|
|
|
2908
2966
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2909
2967
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2910
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2968
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2911
2969
|
val == .true
|
|
2912
2970
|
else if (showdown)
|
|
2913
2971
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2919,7 +2977,7 @@ pub const Rolls = struct {
|
|
|
2919
2977
|
}
|
|
2920
2978
|
|
|
2921
2979
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2922
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2980
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2923
2981
|
val == .true
|
|
2924
2982
|
else if (showdown)
|
|
2925
2983
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2931,7 +2989,7 @@ pub const Rolls = struct {
|
|
|
2931
2989
|
}
|
|
2932
2990
|
|
|
2933
2991
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2934
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2992
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2935
2993
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2936
2994
|
else if (showdown)
|
|
2937
2995
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2947,7 +3005,7 @@ pub const Rolls = struct {
|
|
|
2947
3005
|
}
|
|
2948
3006
|
|
|
2949
3007
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2950
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3008
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2951
3009
|
(val - 1) * 40
|
|
2952
3010
|
else if (showdown) power: {
|
|
2953
3011
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2969,7 +3027,7 @@ pub const Rolls = struct {
|
|
|
2969
3027
|
}
|
|
2970
3028
|
|
|
2971
3029
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2972
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3030
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2973
3031
|
val + 3
|
|
2974
3032
|
else if (showdown) num: {
|
|
2975
3033
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2997,7 +3055,7 @@ pub const Rolls = struct {
|
|
|
2997
3055
|
}
|
|
2998
3056
|
|
|
2999
3057
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
3000
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3058
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
3001
3059
|
val
|
|
3002
3060
|
else if (showdown)
|
|
3003
3061
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3013,13 +3071,12 @@ pub const Rolls = struct {
|
|
|
3013
3071
|
return hits;
|
|
3014
3072
|
}
|
|
3015
3073
|
|
|
3016
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3017
|
-
|
|
3074
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3075
|
+
assert(!showdown);
|
|
3076
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3018
3077
|
val + 1
|
|
3019
|
-
else if (showdown)
|
|
3020
|
-
battle.rng.range(u8, 2, 6)
|
|
3021
3078
|
else
|
|
3022
|
-
(battle.rng.next() & 3) + 2;
|
|
3079
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3023
3080
|
|
|
3024
3081
|
assert(pp >= 2 and pp <= 5);
|
|
3025
3082
|
try options.chance.spite(player, pp);
|
|
@@ -3033,7 +3090,7 @@ pub const Rolls = struct {
|
|
|
3033
3090
|
var i: u8 = 0;
|
|
3034
3091
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3035
3092
|
assert(mtype != .@"???");
|
|
3036
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3093
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3037
3094
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3038
3095
|
else if (showdown) ty: {
|
|
3039
3096
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3067,7 +3124,7 @@ pub const Rolls = struct {
|
|
|
3067
3124
|
assert(count <= 8);
|
|
3068
3125
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3069
3126
|
|
|
3070
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3127
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3071
3128
|
val == .true
|
|
3072
3129
|
else if (showdown)
|
|
3073
3130
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3089,7 +3146,7 @@ pub const Rolls = struct {
|
|
|
3089
3146
|
const moves = active.moves;
|
|
3090
3147
|
|
|
3091
3148
|
// TODO: consider throwing error instead of rerolling?
|
|
3092
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3149
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3093
3150
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3094
3151
|
else
|
|
3095
3152
|
null;
|
|
@@ -3126,7 +3183,7 @@ pub const Rolls = struct {
|
|
|
3126
3183
|
const moves = active.moves;
|
|
3127
3184
|
|
|
3128
3185
|
// TODO: consider throwing error instead of rerolling?
|
|
3129
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3186
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3130
3187
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3131
3188
|
else
|
|
3132
3189
|
null;
|
|
@@ -3170,7 +3227,7 @@ pub const Rolls = struct {
|
|
|
3170
3227
|
assert(n > 0 and n <= 6);
|
|
3171
3228
|
|
|
3172
3229
|
// TODO: consider throwing error instead of rerolling?
|
|
3173
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3230
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3174
3231
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3175
3232
|
break :val null;
|
|
3176
3233
|
} else null;
|
|
@@ -3196,7 +3253,7 @@ pub const Rolls = struct {
|
|
|
3196
3253
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3197
3254
|
|
|
3198
3255
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3199
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3256
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3200
3257
|
@intCast(val)
|
|
3201
3258
|
else if (showdown)
|
|
3202
3259
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3211,7 +3268,7 @@ pub const Rolls = struct {
|
|
|
3211
3268
|
}
|
|
3212
3269
|
|
|
3213
3270
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3214
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3271
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3215
3272
|
val
|
|
3216
3273
|
else if (showdown)
|
|
3217
3274
|
battle.rng.range(u8, 1, max)
|
|
@@ -3237,7 +3294,7 @@ pub const Rolls = struct {
|
|
|
3237
3294
|
}
|
|
3238
3295
|
|
|
3239
3296
|
// TODO: consider throwing error instead of rerolling?
|
|
3240
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3297
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3241
3298
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3242
3299
|
break :val val;
|
|
3243
3300
|
} else null;
|
|
@@ -3269,7 +3326,7 @@ pub const Rolls = struct {
|
|
|
3269
3326
|
}
|
|
3270
3327
|
|
|
3271
3328
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3272
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3329
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3273
3330
|
@intCast(val)
|
|
3274
3331
|
else if (showdown)
|
|
3275
3332
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3281,12 +3338,12 @@ pub const Rolls = struct {
|
|
|
3281
3338
|
};
|
|
3282
3339
|
|
|
3283
3340
|
assert(duration >= 1 and duration <= 6);
|
|
3284
|
-
options.chance.duration(
|
|
3341
|
+
options.chance.duration(player, duration);
|
|
3285
3342
|
return duration + 1;
|
|
3286
3343
|
}
|
|
3287
3344
|
|
|
3288
3345
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3289
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3346
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3290
3347
|
@intCast(val)
|
|
3291
3348
|
else if (showdown)
|
|
3292
3349
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3295,12 +3352,12 @@ pub const Rolls = struct {
|
|
|
3295
3352
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3296
3353
|
|
|
3297
3354
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3298
|
-
options.chance.duration(
|
|
3355
|
+
options.chance.duration(player, duration);
|
|
3299
3356
|
return duration;
|
|
3300
3357
|
}
|
|
3301
3358
|
|
|
3302
3359
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3303
|
-
const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
|
|
3360
|
+
const duration: u4 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3304
3361
|
@intCast(val)
|
|
3305
3362
|
else if (showdown)
|
|
3306
3363
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
@@ -3313,12 +3370,12 @@ pub const Rolls = struct {
|
|
|
3313
3370
|
};
|
|
3314
3371
|
|
|
3315
3372
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3316
|
-
options.chance.duration(
|
|
3373
|
+
options.chance.duration(player, duration);
|
|
3317
3374
|
return duration;
|
|
3318
3375
|
}
|
|
3319
3376
|
|
|
3320
3377
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3321
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3378
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3322
3379
|
@intCast(val)
|
|
3323
3380
|
else if (showdown)
|
|
3324
3381
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3326,12 +3383,12 @@ pub const Rolls = struct {
|
|
|
3326
3383
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3327
3384
|
|
|
3328
3385
|
assert(duration >= 2 and duration <= 3);
|
|
3329
|
-
options.chance.duration(
|
|
3386
|
+
options.chance.duration(player, duration);
|
|
3330
3387
|
return duration;
|
|
3331
3388
|
}
|
|
3332
3389
|
|
|
3333
3390
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3334
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3391
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3335
3392
|
@intCast(val)
|
|
3336
3393
|
else if (showdown)
|
|
3337
3394
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3340,13 +3397,13 @@ pub const Rolls = struct {
|
|
|
3340
3397
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3341
3398
|
|
|
3342
3399
|
assert(duration >= 2 and duration <= 5);
|
|
3343
|
-
options.chance.duration(
|
|
3400
|
+
options.chance.duration(player, duration);
|
|
3344
3401
|
return duration + 1;
|
|
3345
3402
|
}
|
|
3346
3403
|
|
|
3347
3404
|
// TODO: consider sharing implementation with bindingDuration
|
|
3348
3405
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3349
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3406
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3350
3407
|
@intCast(val)
|
|
3351
3408
|
else if (showdown)
|
|
3352
3409
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3354,7 +3411,7 @@ pub const Rolls = struct {
|
|
|
3354
3411
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3355
3412
|
|
|
3356
3413
|
assert(duration >= 2 and duration <= 5);
|
|
3357
|
-
options.chance.duration(
|
|
3414
|
+
options.chance.duration(player, duration);
|
|
3358
3415
|
return duration + 1;
|
|
3359
3416
|
}
|
|
3360
3417
|
};
|
|
@@ -3467,7 +3524,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3467
3524
|
.Move => {
|
|
3468
3525
|
const side = battle.side(player);
|
|
3469
3526
|
|
|
3470
|
-
|
|
3527
|
+
const active = &side.active;
|
|
3471
3528
|
const stored = side.stored();
|
|
3472
3529
|
|
|
3473
3530
|
// While players are not given any input options on the cartridge in these cases,
|