@pkmn/engine 0.1.0-dev.6c5fdce2 → 0.1.0-dev.6d566713
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 -5
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +1 -1
- 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 +203 -89
- package/package.json +32 -22
- package/src/bin/install-pkmn-engine +30 -20
- 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 +57 -57
- package/src/lib/common/rational.zig +114 -24
- package/src/lib/common/rng.zig +7 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +506 -128
- 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 +268 -174
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +84 -496
- package/src/lib/gen2/data.zig +7 -6
- package/src/lib/gen2/generated.zig +0 -2
- package/src/lib/gen2/helpers.zig +41 -13
- package/src/lib/gen2/mechanics.zig +158 -108
- package/src/lib/{binding/node.zig → node.zig} +3 -6
- 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
|
@@ -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
|
|
@@ -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);
|
|
@@ -442,18 +436,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
442
436
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
443
437
|
const before = stored.status;
|
|
444
438
|
// 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
439
|
stored.status -= 1;
|
|
449
|
-
// }
|
|
450
440
|
|
|
451
|
-
|
|
452
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
453
|
-
try options.chance.sleep(player, duration);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
if (duration == 0) {
|
|
441
|
+
if (Status.duration(stored.status) == 0) {
|
|
457
442
|
try log.curestatus(.{ ident, before, .Message });
|
|
458
443
|
stored.status = 0; // clears EXT if present
|
|
459
444
|
resetCant(volatiles);
|
|
@@ -483,12 +468,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
483
468
|
}
|
|
484
469
|
|
|
485
470
|
if (volatiles.disable.duration > 0) {
|
|
486
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
487
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
488
|
-
// } else {
|
|
489
471
|
volatiles.disable.duration -= 1;
|
|
490
|
-
// }
|
|
491
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
492
472
|
|
|
493
473
|
if (volatiles.disable.duration == 0) {
|
|
494
474
|
volatiles.disable.move = 0;
|
|
@@ -498,13 +478,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
498
478
|
|
|
499
479
|
if (volatiles.Confusion) {
|
|
500
480
|
assert(volatiles.confusion > 0);
|
|
501
|
-
|
|
502
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
503
|
-
// if (!extend) volatiles.confusion = 0;
|
|
504
|
-
// } else {
|
|
505
481
|
volatiles.confusion -= 1;
|
|
506
|
-
// }
|
|
507
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
508
482
|
|
|
509
483
|
if (volatiles.confusion == 0) {
|
|
510
484
|
volatiles.Confusion = false;
|
|
@@ -665,7 +639,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
665
639
|
}
|
|
666
640
|
assert(stage <= 4);
|
|
667
641
|
|
|
668
|
-
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);
|
|
669
643
|
}
|
|
670
644
|
|
|
671
645
|
pub fn calcDamage(
|
|
@@ -733,7 +707,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
733
707
|
|
|
734
708
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
735
709
|
if (state.damage <= 1) return;
|
|
736
|
-
const random = try Rolls.damage(battle, player, options);
|
|
710
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
737
711
|
options.calc.base(player.foe(), state.damage);
|
|
738
712
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
739
713
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -999,7 +973,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
999
973
|
|
|
1000
974
|
// Weather
|
|
1001
975
|
if (battle.field.weather != .None) {
|
|
1002
|
-
assert(battle.field.weather_duration > 0);
|
|
1003
976
|
battle.field.weather_duration -= 1;
|
|
1004
977
|
|
|
1005
978
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1058,7 +1031,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1058
1031
|
var volatiles = &side.active.volatiles;
|
|
1059
1032
|
|
|
1060
1033
|
if (volatiles.PerishSong) {
|
|
1061
|
-
assert(volatiles.perish_song > 0);
|
|
1062
1034
|
volatiles.perish_song -= 1;
|
|
1063
1035
|
|
|
1064
1036
|
assert(volatiles.perish_song < 3);
|
|
@@ -1195,7 +1167,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1195
1167
|
var volatiles = &side.active.volatiles;
|
|
1196
1168
|
|
|
1197
1169
|
if (volatiles.Encore) {
|
|
1198
|
-
assert(volatiles.encore > 0);
|
|
1199
1170
|
volatiles.encore -= 1;
|
|
1200
1171
|
|
|
1201
1172
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
@@ -1217,7 +1188,6 @@ fn updateSideCondition(
|
|
|
1217
1188
|
) !void {
|
|
1218
1189
|
var side = battle.side(player);
|
|
1219
1190
|
if (@field(side.conditions, key)) {
|
|
1220
|
-
assert(@field(side.conditions, value) > 0);
|
|
1221
1191
|
@field(side.conditions, value) -= 1;
|
|
1222
1192
|
|
|
1223
1193
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1308,14 +1278,32 @@ pub const Effects = struct {
|
|
|
1308
1278
|
}
|
|
1309
1279
|
|
|
1310
1280
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
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
|
+
}
|
|
1319
1307
|
}
|
|
1320
1308
|
|
|
1321
1309
|
pub const bide = struct {
|
|
@@ -1327,14 +1315,7 @@ pub const Effects = struct {
|
|
|
1327
1315
|
|
|
1328
1316
|
if (!volatiles.Bide) return;
|
|
1329
1317
|
|
|
1330
|
-
assert(volatiles.attacks > 0);
|
|
1331
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1332
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1333
|
-
// } else {
|
|
1334
1318
|
volatiles.attacks -= 1;
|
|
1335
|
-
// }
|
|
1336
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1337
|
-
|
|
1338
1319
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1339
1320
|
|
|
1340
1321
|
volatiles.Bide = false;
|
|
@@ -1601,8 +1582,16 @@ pub const Effects = struct {
|
|
|
1601
1582
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1602
1583
|
}
|
|
1603
1584
|
|
|
1604
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1605
|
-
|
|
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;
|
|
1606
1595
|
}
|
|
1607
1596
|
|
|
1608
1597
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1728,8 +1717,26 @@ pub const Effects = struct {
|
|
|
1728
1717
|
foe.active.volatiles.frozen = true;
|
|
1729
1718
|
}
|
|
1730
1719
|
|
|
1731
|
-
|
|
1732
|
-
|
|
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
|
+
}
|
|
1733
1740
|
}
|
|
1734
1741
|
|
|
1735
1742
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1890,7 +1897,29 @@ pub const Effects = struct {
|
|
|
1890
1897
|
}
|
|
1891
1898
|
|
|
1892
1899
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1893
|
-
|
|
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 });
|
|
1894
1923
|
}
|
|
1895
1924
|
|
|
1896
1925
|
pub fn mirrorMove(
|
|
@@ -2398,17 +2427,37 @@ pub const Effects = struct {
|
|
|
2398
2427
|
}
|
|
2399
2428
|
|
|
2400
2429
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2401
|
-
|
|
2402
|
-
// var foe = battle.foe(player);
|
|
2430
|
+
var foe = battle.foe(player);
|
|
2403
2431
|
|
|
2404
|
-
|
|
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
|
+
}
|
|
2405
2436
|
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
//
|
|
2411
|
-
|
|
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 });
|
|
2412
2461
|
}
|
|
2413
2462
|
|
|
2414
2463
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2795,7 +2844,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2795
2844
|
|
|
2796
2845
|
pub const Rolls = struct {
|
|
2797
2846
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2798
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2847
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2799
2848
|
player == .P1
|
|
2800
2849
|
else if (showdown)
|
|
2801
2850
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2807,7 +2856,7 @@ pub const Rolls = struct {
|
|
|
2807
2856
|
}
|
|
2808
2857
|
|
|
2809
2858
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2810
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2859
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2811
2860
|
val == .true
|
|
2812
2861
|
else if (showdown)
|
|
2813
2862
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2819,7 +2868,7 @@ pub const Rolls = struct {
|
|
|
2819
2868
|
}
|
|
2820
2869
|
|
|
2821
2870
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2822
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2871
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2823
2872
|
val == .true
|
|
2824
2873
|
else if (showdown)
|
|
2825
2874
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2830,20 +2879,22 @@ pub const Rolls = struct {
|
|
|
2830
2879
|
return ok;
|
|
2831
2880
|
}
|
|
2832
2881
|
|
|
2833
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2834
|
-
|
|
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|
|
|
2835
2885
|
val == .true
|
|
2836
2886
|
else if (showdown)
|
|
2837
2887
|
battle.rng.chance(u8, rate, 256)
|
|
2838
2888
|
else
|
|
2839
2889
|
battle.rng.next() < rate;
|
|
2840
2890
|
|
|
2841
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2891
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2842
2892
|
return crit;
|
|
2843
2893
|
}
|
|
2844
2894
|
|
|
2845
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2846
|
-
|
|
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|
|
|
2847
2898
|
val
|
|
2848
2899
|
else roll: {
|
|
2849
2900
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2854,12 +2905,12 @@ pub const Rolls = struct {
|
|
|
2854
2905
|
};
|
|
2855
2906
|
|
|
2856
2907
|
assert(roll >= 217 and roll <= 255);
|
|
2857
|
-
try options.chance.damage(player, roll);
|
|
2908
|
+
try options.chance.damage(player, i, roll);
|
|
2858
2909
|
return roll;
|
|
2859
2910
|
}
|
|
2860
2911
|
|
|
2861
2912
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2862
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2913
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2863
2914
|
val == .true
|
|
2864
2915
|
else if (showdown)
|
|
2865
2916
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2871,7 +2922,7 @@ pub const Rolls = struct {
|
|
|
2871
2922
|
}
|
|
2872
2923
|
|
|
2873
2924
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2874
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2925
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2875
2926
|
val == .true
|
|
2876
2927
|
else if (showdown)
|
|
2877
2928
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2883,7 +2934,7 @@ pub const Rolls = struct {
|
|
|
2883
2934
|
}
|
|
2884
2935
|
|
|
2885
2936
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2886
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2937
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2887
2938
|
val == .true
|
|
2888
2939
|
else if (showdown)
|
|
2889
2940
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2895,7 +2946,7 @@ pub const Rolls = struct {
|
|
|
2895
2946
|
}
|
|
2896
2947
|
|
|
2897
2948
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2898
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2949
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2899
2950
|
val == .true
|
|
2900
2951
|
else if (showdown)
|
|
2901
2952
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2908,7 +2959,7 @@ pub const Rolls = struct {
|
|
|
2908
2959
|
|
|
2909
2960
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2910
2961
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2911
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2962
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2912
2963
|
val == .true
|
|
2913
2964
|
else if (showdown)
|
|
2914
2965
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2920,7 +2971,7 @@ pub const Rolls = struct {
|
|
|
2920
2971
|
}
|
|
2921
2972
|
|
|
2922
2973
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2923
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2974
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2924
2975
|
val == .true
|
|
2925
2976
|
else if (showdown)
|
|
2926
2977
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2932,7 +2983,7 @@ pub const Rolls = struct {
|
|
|
2932
2983
|
}
|
|
2933
2984
|
|
|
2934
2985
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2935
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2986
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2936
2987
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2937
2988
|
else if (showdown)
|
|
2938
2989
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2948,7 +2999,7 @@ pub const Rolls = struct {
|
|
|
2948
2999
|
}
|
|
2949
3000
|
|
|
2950
3001
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2951
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3002
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2952
3003
|
(val - 1) * 40
|
|
2953
3004
|
else if (showdown) power: {
|
|
2954
3005
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2970,7 +3021,7 @@ pub const Rolls = struct {
|
|
|
2970
3021
|
}
|
|
2971
3022
|
|
|
2972
3023
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2973
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3024
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2974
3025
|
val + 3
|
|
2975
3026
|
else if (showdown) num: {
|
|
2976
3027
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2998,7 +3049,7 @@ pub const Rolls = struct {
|
|
|
2998
3049
|
}
|
|
2999
3050
|
|
|
3000
3051
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
3001
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3052
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
3002
3053
|
val
|
|
3003
3054
|
else if (showdown)
|
|
3004
3055
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3014,13 +3065,12 @@ pub const Rolls = struct {
|
|
|
3014
3065
|
return hits;
|
|
3015
3066
|
}
|
|
3016
3067
|
|
|
3017
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3018
|
-
|
|
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|
|
|
3019
3071
|
val + 1
|
|
3020
|
-
else if (showdown)
|
|
3021
|
-
battle.rng.range(u8, 2, 6)
|
|
3022
3072
|
else
|
|
3023
|
-
(battle.rng.next() & 3) + 2;
|
|
3073
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3024
3074
|
|
|
3025
3075
|
assert(pp >= 2 and pp <= 5);
|
|
3026
3076
|
try options.chance.spite(player, pp);
|
|
@@ -3034,7 +3084,7 @@ pub const Rolls = struct {
|
|
|
3034
3084
|
var i: u8 = 0;
|
|
3035
3085
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3036
3086
|
assert(mtype != .@"???");
|
|
3037
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3087
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3038
3088
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3039
3089
|
else if (showdown) ty: {
|
|
3040
3090
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3068,7 +3118,7 @@ pub const Rolls = struct {
|
|
|
3068
3118
|
assert(count <= 8);
|
|
3069
3119
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3070
3120
|
|
|
3071
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3121
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3072
3122
|
val == .true
|
|
3073
3123
|
else if (showdown)
|
|
3074
3124
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3090,7 +3140,7 @@ pub const Rolls = struct {
|
|
|
3090
3140
|
const moves = active.moves;
|
|
3091
3141
|
|
|
3092
3142
|
// TODO: consider throwing error instead of rerolling?
|
|
3093
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3143
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3094
3144
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3095
3145
|
else
|
|
3096
3146
|
null;
|
|
@@ -3127,7 +3177,7 @@ pub const Rolls = struct {
|
|
|
3127
3177
|
const moves = active.moves;
|
|
3128
3178
|
|
|
3129
3179
|
// TODO: consider throwing error instead of rerolling?
|
|
3130
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3180
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3131
3181
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3132
3182
|
else
|
|
3133
3183
|
null;
|
|
@@ -3171,7 +3221,7 @@ pub const Rolls = struct {
|
|
|
3171
3221
|
assert(n > 0 and n <= 6);
|
|
3172
3222
|
|
|
3173
3223
|
// TODO: consider throwing error instead of rerolling?
|
|
3174
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3224
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3175
3225
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3176
3226
|
break :val null;
|
|
3177
3227
|
} else null;
|
|
@@ -3197,7 +3247,7 @@ pub const Rolls = struct {
|
|
|
3197
3247
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3198
3248
|
|
|
3199
3249
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3200
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3250
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3201
3251
|
@intCast(val)
|
|
3202
3252
|
else if (showdown)
|
|
3203
3253
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3212,7 +3262,7 @@ pub const Rolls = struct {
|
|
|
3212
3262
|
}
|
|
3213
3263
|
|
|
3214
3264
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3215
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3265
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3216
3266
|
val
|
|
3217
3267
|
else if (showdown)
|
|
3218
3268
|
battle.rng.range(u8, 1, max)
|
|
@@ -3238,7 +3288,7 @@ pub const Rolls = struct {
|
|
|
3238
3288
|
}
|
|
3239
3289
|
|
|
3240
3290
|
// TODO: consider throwing error instead of rerolling?
|
|
3241
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3291
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3242
3292
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3243
3293
|
break :val val;
|
|
3244
3294
|
} else null;
|
|
@@ -3270,7 +3320,7 @@ pub const Rolls = struct {
|
|
|
3270
3320
|
}
|
|
3271
3321
|
|
|
3272
3322
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3273
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3323
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3274
3324
|
@intCast(val)
|
|
3275
3325
|
else if (showdown)
|
|
3276
3326
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3282,12 +3332,12 @@ pub const Rolls = struct {
|
|
|
3282
3332
|
};
|
|
3283
3333
|
|
|
3284
3334
|
assert(duration >= 1 and duration <= 6);
|
|
3285
|
-
options.chance.duration(
|
|
3335
|
+
options.chance.duration(player, duration);
|
|
3286
3336
|
return duration + 1;
|
|
3287
3337
|
}
|
|
3288
3338
|
|
|
3289
3339
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3290
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3340
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3291
3341
|
@intCast(val)
|
|
3292
3342
|
else if (showdown)
|
|
3293
3343
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3296,16 +3346,16 @@ pub const Rolls = struct {
|
|
|
3296
3346
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3297
3347
|
|
|
3298
3348
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3299
|
-
options.chance.duration(
|
|
3349
|
+
options.chance.duration(player, duration);
|
|
3300
3350
|
return duration;
|
|
3301
3351
|
}
|
|
3302
3352
|
|
|
3303
3353
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3304
|
-
const duration:
|
|
3354
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3305
3355
|
@intCast(val)
|
|
3306
3356
|
else if (showdown)
|
|
3307
3357
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3308
|
-
battle.rng.range(
|
|
3358
|
+
battle.rng.range(u3, 2, 6)
|
|
3309
3359
|
else duration: {
|
|
3310
3360
|
while (true) {
|
|
3311
3361
|
const r = battle.rng.next() & 7;
|
|
@@ -3314,12 +3364,12 @@ pub const Rolls = struct {
|
|
|
3314
3364
|
};
|
|
3315
3365
|
|
|
3316
3366
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3317
|
-
options.chance.duration(
|
|
3367
|
+
options.chance.duration(player, duration);
|
|
3318
3368
|
return duration;
|
|
3319
3369
|
}
|
|
3320
3370
|
|
|
3321
3371
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3322
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3372
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3323
3373
|
@intCast(val)
|
|
3324
3374
|
else if (showdown)
|
|
3325
3375
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3327,12 +3377,12 @@ pub const Rolls = struct {
|
|
|
3327
3377
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3328
3378
|
|
|
3329
3379
|
assert(duration >= 2 and duration <= 3);
|
|
3330
|
-
options.chance.duration(
|
|
3380
|
+
options.chance.duration(player, duration);
|
|
3331
3381
|
return duration;
|
|
3332
3382
|
}
|
|
3333
3383
|
|
|
3334
3384
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3335
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3385
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3336
3386
|
@intCast(val)
|
|
3337
3387
|
else if (showdown)
|
|
3338
3388
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3341,13 +3391,13 @@ pub const Rolls = struct {
|
|
|
3341
3391
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3342
3392
|
|
|
3343
3393
|
assert(duration >= 2 and duration <= 5);
|
|
3344
|
-
options.chance.duration(
|
|
3394
|
+
options.chance.duration(player, duration);
|
|
3345
3395
|
return duration + 1;
|
|
3346
3396
|
}
|
|
3347
3397
|
|
|
3348
3398
|
// TODO: consider sharing implementation with bindingDuration
|
|
3349
3399
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3350
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3400
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3351
3401
|
@intCast(val)
|
|
3352
3402
|
else if (showdown)
|
|
3353
3403
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3355,7 +3405,7 @@ pub const Rolls = struct {
|
|
|
3355
3405
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3356
3406
|
|
|
3357
3407
|
assert(duration >= 2 and duration <= 5);
|
|
3358
|
-
options.chance.duration(
|
|
3408
|
+
options.chance.duration(player, duration);
|
|
3359
3409
|
return duration + 1;
|
|
3360
3410
|
}
|
|
3361
3411
|
};
|