@pkmn/engine 0.1.0-dev.c544e10e → 0.1.0-dev.cb3978dd
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 +44 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +97 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +21 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +22 -0
- package/build/pkg/addon.js +35 -58
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +10 -13
- package/build/pkg/gen1.js +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +55 -13
- 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 +36 -70
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +141 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +253 -89
- package/package.json +58 -30
- package/src/bin/install-pkmn-engine +29 -19
- 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 +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +14 -10
- package/src/lib/common/options.zig +6 -0
- package/src/lib/common/protocol.zig +75 -79
- package/src/lib/common/rational.zig +128 -32
- package/src/lib/common/rng.zig +7 -9
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +686 -159
- package/src/lib/gen1/chance.zig +405 -280
- package/src/lib/gen1/data/moves.zig +2 -4
- package/src/lib/gen1/data/species.zig +2 -4
- package/src/lib/gen1/data/types.zig +1 -2
- package/src/lib/gen1/data.zig +24 -27
- package/src/lib/gen1/helpers.zig +92 -548
- package/src/lib/gen1/mechanics.zig +373 -257
- package/src/lib/gen2/calc.zig +37 -27
- package/src/lib/gen2/chance.zig +84 -496
- package/src/lib/gen2/data/items.zig +1 -3
- package/src/lib/gen2/data/moves.zig +2 -4
- package/src/lib/gen2/data/species.zig +1 -3
- package/src/lib/gen2/data/types.zig +1 -2
- package/src/lib/gen2/data.zig +19 -22
- package/src/lib/gen2/generated.zig +19 -26
- package/src/lib/gen2/helpers.zig +46 -25
- package/src/lib/gen2/mechanics.zig +200 -157
- package/src/lib/{binding/node.zig → node.zig} +8 -13
- package/src/lib/pkmn.zig +36 -15
- package/src/lib/{binding/wasm.zig → wasm.zig} +1 -1
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -1,50 +1,36 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
4
|
-
|
|
5
|
-
const common = @import("../common/data.zig");
|
|
6
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
|
-
const protocol = @import("../common/protocol.zig");
|
|
8
|
-
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
2
|
|
|
3
|
+
const common = @import("../common/data.zig");
|
|
11
4
|
const data = @import("data.zig");
|
|
12
5
|
const generated = @import("generated.zig");
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
18
|
-
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
19
|
-
|
|
20
|
-
const showdown = pkmn.options.showdown;
|
|
21
|
-
|
|
22
|
-
const Choice = common.Choice;
|
|
23
|
-
const ID = common.ID;
|
|
24
|
-
const Player = common.Player;
|
|
25
|
-
const Result = common.Result;
|
|
6
|
+
const pkmn = @import("../pkmn.zig");
|
|
7
|
+
const protocol = @import("../common/protocol.zig");
|
|
8
|
+
const rng = @import("../common/rng.zig");
|
|
9
|
+
const std = @import("std");
|
|
26
10
|
|
|
27
11
|
const Activate = protocol.Activate;
|
|
12
|
+
const ActivePokemon = data.ActivePokemon;
|
|
13
|
+
const assert = std.debug.assert;
|
|
28
14
|
const Boost = protocol.Boost;
|
|
15
|
+
const Boosts = data.Boosts;
|
|
16
|
+
const Choice = common.Choice;
|
|
29
17
|
const Damage = protocol.Damage;
|
|
18
|
+
const Effectiveness = data.Effectiveness;
|
|
30
19
|
const End = protocol.End;
|
|
20
|
+
const expectEqual = std.testing.expectEqual;
|
|
21
|
+
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
22
|
+
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
31
23
|
const Fail = protocol.Fail;
|
|
32
|
-
const Heal = protocol.Heal;
|
|
33
|
-
const Start = protocol.Start;
|
|
34
|
-
|
|
35
24
|
const Gen12 = rng.Gen12;
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const ActivePokemon = data.ActivePokemon;
|
|
40
|
-
const Boosts = data.Boosts;
|
|
41
|
-
const Effectiveness = data.Effectiveness;
|
|
25
|
+
const Heal = protocol.Heal;
|
|
26
|
+
const ID = common.ID;
|
|
42
27
|
const Item = data.Item;
|
|
43
28
|
const Move = data.Move;
|
|
44
|
-
const
|
|
45
|
-
const
|
|
29
|
+
const Player = common.Player;
|
|
30
|
+
const Result = common.Result;
|
|
31
|
+
const showdown = pkmn.options.showdown;
|
|
46
32
|
const Side = data.Side;
|
|
47
|
-
const
|
|
33
|
+
const Start = protocol.Start;
|
|
48
34
|
const Stats = data.Stats;
|
|
49
35
|
const Status = data.Status;
|
|
50
36
|
const TriAttack = data.TriAttack;
|
|
@@ -106,6 +92,7 @@ pub const State = struct {
|
|
|
106
92
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
93
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
94
|
move: Move, // wCurPlayerMove
|
|
95
|
+
hit: u3 = 0,
|
|
109
96
|
from: Move = .None,
|
|
110
97
|
mslot: u4, // wCurMoveNum
|
|
111
98
|
first: bool, // wEnemyGoesFirst
|
|
@@ -140,11 +127,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
140
127
|
const p1 = battle.side(.P1);
|
|
141
128
|
const p2 = battle.side(.P2);
|
|
142
129
|
|
|
143
|
-
|
|
130
|
+
const p1_slot = findFirstAlive(p1);
|
|
144
131
|
assert(!showdown or p1_slot == 1);
|
|
145
132
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
146
133
|
|
|
147
|
-
|
|
134
|
+
const p2_slot = findFirstAlive(p2);
|
|
148
135
|
assert(!showdown or p2_slot == 1);
|
|
149
136
|
if (p2_slot == 0) return Result.Win;
|
|
150
137
|
|
|
@@ -256,7 +243,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
243
|
} else {
|
|
257
244
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
245
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
246
|
|
|
261
247
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
248
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -403,7 +389,9 @@ fn executeMove(
|
|
|
403
389
|
|
|
404
390
|
assert(choice.type == .Move);
|
|
405
391
|
var mslot: u4 = @intCast(choice.data);
|
|
392
|
+
_ = &mslot;
|
|
406
393
|
var move = Move.Pound; // TODO
|
|
394
|
+
_ = &move;
|
|
407
395
|
|
|
408
396
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
409
397
|
|
|
@@ -440,18 +428,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
440
428
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
441
429
|
const before = stored.status;
|
|
442
430
|
// 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
431
|
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
432
|
|
|
454
|
-
if (duration == 0) {
|
|
433
|
+
if (Status.duration(stored.status) == 0) {
|
|
455
434
|
try log.curestatus(.{ ident, before, .Message });
|
|
456
435
|
stored.status = 0; // clears EXT if present
|
|
457
436
|
resetCant(volatiles);
|
|
@@ -481,12 +460,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
481
460
|
}
|
|
482
461
|
|
|
483
462
|
if (volatiles.disable.duration > 0) {
|
|
484
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
485
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
486
|
-
// } else {
|
|
487
463
|
volatiles.disable.duration -= 1;
|
|
488
|
-
// }
|
|
489
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
490
464
|
|
|
491
465
|
if (volatiles.disable.duration == 0) {
|
|
492
466
|
volatiles.disable.move = 0;
|
|
@@ -496,13 +470,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
496
470
|
|
|
497
471
|
if (volatiles.Confusion) {
|
|
498
472
|
assert(volatiles.confusion > 0);
|
|
499
|
-
|
|
500
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
501
|
-
// if (!extend) volatiles.confusion = 0;
|
|
502
|
-
// } else {
|
|
503
473
|
volatiles.confusion -= 1;
|
|
504
|
-
// }
|
|
505
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
506
474
|
|
|
507
475
|
if (volatiles.confusion == 0) {
|
|
508
476
|
volatiles.Confusion = false;
|
|
@@ -565,7 +533,7 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
565
533
|
|
|
566
534
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
567
535
|
var side = battle.side(player);
|
|
568
|
-
|
|
536
|
+
const volatiles = &side.active.volatiles;
|
|
569
537
|
|
|
570
538
|
// FIXME: pass in target to avoid lookup?
|
|
571
539
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
@@ -663,7 +631,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
663
631
|
}
|
|
664
632
|
assert(stage <= 4);
|
|
665
633
|
|
|
666
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
634
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
667
635
|
}
|
|
668
636
|
|
|
669
637
|
pub fn calcDamage(
|
|
@@ -731,7 +699,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
731
699
|
|
|
732
700
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
733
701
|
if (state.damage <= 1) return;
|
|
734
|
-
const random = try Rolls.damage(battle, player, options);
|
|
702
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
735
703
|
options.calc.base(player.foe(), state.damage);
|
|
736
704
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
737
705
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -997,7 +965,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
997
965
|
|
|
998
966
|
// Weather
|
|
999
967
|
if (battle.field.weather != .None) {
|
|
1000
|
-
assert(battle.field.weather_duration > 0);
|
|
1001
968
|
battle.field.weather_duration -= 1;
|
|
1002
969
|
|
|
1003
970
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1056,7 +1023,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1056
1023
|
var volatiles = &side.active.volatiles;
|
|
1057
1024
|
|
|
1058
1025
|
if (volatiles.PerishSong) {
|
|
1059
|
-
assert(volatiles.perish_song > 0);
|
|
1060
1026
|
volatiles.perish_song -= 1;
|
|
1061
1027
|
|
|
1062
1028
|
assert(volatiles.perish_song < 3);
|
|
@@ -1086,7 +1052,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1086
1052
|
|
|
1087
1053
|
// Mystery Berry
|
|
1088
1054
|
inline for (players) |player| {
|
|
1089
|
-
|
|
1055
|
+
const stored = battle.side(player).stored();
|
|
1090
1056
|
|
|
1091
1057
|
if (stored.item == .MysteryBerry) {
|
|
1092
1058
|
// TODO stored and active moves, transform, etc
|
|
@@ -1193,7 +1159,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1193
1159
|
var volatiles = &side.active.volatiles;
|
|
1194
1160
|
|
|
1195
1161
|
if (volatiles.Encore) {
|
|
1196
|
-
assert(volatiles.encore > 0);
|
|
1197
1162
|
volatiles.encore -= 1;
|
|
1198
1163
|
|
|
1199
1164
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
@@ -1215,7 +1180,6 @@ fn updateSideCondition(
|
|
|
1215
1180
|
) !void {
|
|
1216
1181
|
var side = battle.side(player);
|
|
1217
1182
|
if (@field(side.conditions, key)) {
|
|
1218
|
-
assert(@field(side.conditions, value) > 0);
|
|
1219
1183
|
@field(side.conditions, value) -= 1;
|
|
1220
1184
|
|
|
1221
1185
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1228,7 +1192,7 @@ fn updateSideCondition(
|
|
|
1228
1192
|
fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
|
|
1229
1193
|
battle.turn += 1;
|
|
1230
1194
|
|
|
1231
|
-
if (
|
|
1195
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1232
1196
|
try options.log.tie(.{});
|
|
1233
1197
|
return Result.Tie;
|
|
1234
1198
|
} else if (battle.turn >= 65535) {
|
|
@@ -1306,14 +1270,32 @@ pub const Effects = struct {
|
|
|
1306
1270
|
}
|
|
1307
1271
|
|
|
1308
1272
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1273
|
+
assert(!state.miss);
|
|
1274
|
+
var side = battle.side(player);
|
|
1275
|
+
|
|
1276
|
+
const ident = battle.active(player);
|
|
1277
|
+
|
|
1278
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1279
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1280
|
+
return options.log.fail(.{ ident, .None });
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1284
|
+
if (side.stored().hp <= hp) {
|
|
1285
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1286
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1287
|
+
return options.log.fail(.{ ident, .None });
|
|
1288
|
+
} else {
|
|
1289
|
+
side.stored().hp -= hp;
|
|
1290
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1291
|
+
|
|
1292
|
+
const before = side.active.boosts.atk;
|
|
1293
|
+
var i: usize = 0;
|
|
1294
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1295
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1296
|
+
}
|
|
1297
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1298
|
+
}
|
|
1317
1299
|
}
|
|
1318
1300
|
|
|
1319
1301
|
pub const bide = struct {
|
|
@@ -1325,14 +1307,7 @@ pub const Effects = struct {
|
|
|
1325
1307
|
|
|
1326
1308
|
if (!volatiles.Bide) return;
|
|
1327
1309
|
|
|
1328
|
-
assert(volatiles.attacks > 0);
|
|
1329
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1330
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1331
|
-
// } else {
|
|
1332
1310
|
volatiles.attacks -= 1;
|
|
1333
|
-
// }
|
|
1334
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1335
|
-
|
|
1336
1311
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1337
1312
|
|
|
1338
1313
|
volatiles.Bide = false;
|
|
@@ -1478,8 +1453,8 @@ pub const Effects = struct {
|
|
|
1478
1453
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1479
1454
|
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1480
1455
|
} else {
|
|
1481
|
-
|
|
1482
|
-
|
|
1456
|
+
const stats = &side.active.stats;
|
|
1457
|
+
const boosts = &side.active.boosts;
|
|
1483
1458
|
|
|
1484
1459
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1485
1460
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
@@ -1599,8 +1574,16 @@ pub const Effects = struct {
|
|
|
1599
1574
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1600
1575
|
}
|
|
1601
1576
|
|
|
1602
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1603
|
-
|
|
1577
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1578
|
+
var side = battle.side(player);
|
|
1579
|
+
var stored = side.stored();
|
|
1580
|
+
|
|
1581
|
+
stored.hp = 0;
|
|
1582
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1583
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1584
|
+
if (!showdown) stored.status = 0;
|
|
1585
|
+
side.active.volatiles.LeechSeed = false;
|
|
1586
|
+
side.active.volatiles.DestinyBond = false;
|
|
1604
1587
|
}
|
|
1605
1588
|
|
|
1606
1589
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1715,7 +1698,7 @@ pub const Effects = struct {
|
|
|
1715
1698
|
if (!state.proc) return;
|
|
1716
1699
|
if (foe.conditions.Safeguard) return;
|
|
1717
1700
|
// Freeze Clause Mod
|
|
1718
|
-
if (
|
|
1701
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1719
1702
|
|
|
1720
1703
|
foe_stored.status = Status.init(.FRZ);
|
|
1721
1704
|
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
@@ -1726,8 +1709,26 @@ pub const Effects = struct {
|
|
|
1726
1709
|
foe.active.volatiles.frozen = true;
|
|
1727
1710
|
}
|
|
1728
1711
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1712
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1713
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1714
|
+
var side = battle.side(player);
|
|
1715
|
+
var volatiles = &side.active.volatiles;
|
|
1716
|
+
|
|
1717
|
+
if (state.miss) {
|
|
1718
|
+
volatiles.fury_cutter = 0;
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1723
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1724
|
+
volatiles.fury_cutter += 1;
|
|
1725
|
+
|
|
1726
|
+
if (showdown) {
|
|
1727
|
+
state.bp *= 2;
|
|
1728
|
+
assert(state.bp <= 160);
|
|
1729
|
+
} else {
|
|
1730
|
+
state.damage *|= 2;
|
|
1731
|
+
}
|
|
1731
1732
|
}
|
|
1732
1733
|
|
|
1733
1734
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1888,7 +1889,29 @@ pub const Effects = struct {
|
|
|
1888
1889
|
}
|
|
1889
1890
|
|
|
1890
1891
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1891
|
-
|
|
1892
|
+
var side = battle.side(player);
|
|
1893
|
+
|
|
1894
|
+
if (state.miss) {
|
|
1895
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1896
|
+
try options.log.lastmiss(.{});
|
|
1897
|
+
return options.log.miss(.{battle.active(player)});
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
const move = battle.foe(player).lastMove(false);
|
|
1901
|
+
if (move == .None or move == .Struggle) {
|
|
1902
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
var slot: u4 = 0;
|
|
1906
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1907
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1908
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1909
|
+
}
|
|
1910
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1911
|
+
|
|
1912
|
+
side.active.move(slot).id = move;
|
|
1913
|
+
side.active.move(slot).pp = 5;
|
|
1914
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1892
1915
|
}
|
|
1893
1916
|
|
|
1894
1917
|
pub fn mirrorMove(
|
|
@@ -2185,11 +2208,10 @@ pub const Effects = struct {
|
|
|
2185
2208
|
}
|
|
2186
2209
|
|
|
2187
2210
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2188
|
-
|
|
2189
|
-
var volatiles = &side.active.volatiles;
|
|
2211
|
+
const side = battle.side(player);
|
|
2190
2212
|
|
|
2191
|
-
assert(volatiles.Rage);
|
|
2192
|
-
state.damage *|= (volatiles.rage +| 1);
|
|
2213
|
+
assert(side.active.volatiles.Rage);
|
|
2214
|
+
state.damage *|= (side.active.volatiles.rage +| 1);
|
|
2193
2215
|
}
|
|
2194
2216
|
};
|
|
2195
2217
|
|
|
@@ -2354,7 +2376,7 @@ pub const Effects = struct {
|
|
|
2354
2376
|
}
|
|
2355
2377
|
|
|
2356
2378
|
// Sleep Clause Mod
|
|
2357
|
-
if (
|
|
2379
|
+
if (pkmn.options.mod) {
|
|
2358
2380
|
for (foe.pokemon) |p| {
|
|
2359
2381
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2360
2382
|
}
|
|
@@ -2397,17 +2419,37 @@ pub const Effects = struct {
|
|
|
2397
2419
|
}
|
|
2398
2420
|
|
|
2399
2421
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2400
|
-
|
|
2401
|
-
// var foe = battle.foe(player);
|
|
2422
|
+
var foe = battle.foe(player);
|
|
2402
2423
|
|
|
2403
|
-
|
|
2424
|
+
const last = foe.lastMove(false);
|
|
2425
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2426
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2427
|
+
}
|
|
2404
2428
|
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
//
|
|
2410
|
-
|
|
2429
|
+
var slot: u4 = 0;
|
|
2430
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2431
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2432
|
+
}
|
|
2433
|
+
// If the move was called via Metronome it might not exist
|
|
2434
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2435
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2439
|
+
const pp = @min(
|
|
2440
|
+
foe.active.move(slot).pp,
|
|
2441
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2442
|
+
);
|
|
2443
|
+
foe.active.move(slot).pp -= pp;
|
|
2444
|
+
if (!foe.active.volatiles.Transform) {
|
|
2445
|
+
var move_slot = foe.stored().move(slot);
|
|
2446
|
+
assert(move_slot.pp > 0);
|
|
2447
|
+
|
|
2448
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2449
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2411
2453
|
}
|
|
2412
2454
|
|
|
2413
2455
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2541,8 +2583,8 @@ pub const Effects = struct {
|
|
|
2541
2583
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2542
2584
|
var side = battle.side(player);
|
|
2543
2585
|
|
|
2544
|
-
|
|
2545
|
-
|
|
2586
|
+
const stats = &side.active.stats;
|
|
2587
|
+
const boosts = &side.active.boosts;
|
|
2546
2588
|
|
|
2547
2589
|
if (!state.proc) return;
|
|
2548
2590
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
@@ -2563,7 +2605,7 @@ pub const Effects = struct {
|
|
|
2563
2605
|
|
|
2564
2606
|
if (state.miss) return log.fail(.{ ident, .None });
|
|
2565
2607
|
|
|
2566
|
-
|
|
2608
|
+
const stats = &side.active.stats;
|
|
2567
2609
|
var boosts = &side.active.boosts;
|
|
2568
2610
|
|
|
2569
2611
|
const move = Move.get(state.move);
|
|
@@ -2634,7 +2676,7 @@ pub const Effects = struct {
|
|
|
2634
2676
|
|
|
2635
2677
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2636
2678
|
|
|
2637
|
-
|
|
2679
|
+
const stats = &foe.active.stats;
|
|
2638
2680
|
var boosts = &foe.active.boosts;
|
|
2639
2681
|
|
|
2640
2682
|
const move = Move.get(state.move);
|
|
@@ -2718,9 +2760,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2718
2760
|
}
|
|
2719
2761
|
|
|
2720
2762
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2721
|
-
|
|
2763
|
+
const stats = unmodifiedStats(battle, side);
|
|
2722
2764
|
inline for (STATS) |s| {
|
|
2723
|
-
|
|
2765
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2724
2766
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2725
2767
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2726
2768
|
}
|
|
@@ -2743,7 +2785,7 @@ fn boostStat(
|
|
|
2743
2785
|
n: u2,
|
|
2744
2786
|
) !void {
|
|
2745
2787
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2746
|
-
|
|
2788
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2747
2789
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2748
2790
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2749
2791
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2766,7 +2808,7 @@ fn unboostStat(
|
|
|
2766
2808
|
n: u2,
|
|
2767
2809
|
) !void {
|
|
2768
2810
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2769
|
-
|
|
2811
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2770
2812
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2771
2813
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2772
2814
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2794,7 +2836,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2794
2836
|
|
|
2795
2837
|
pub const Rolls = struct {
|
|
2796
2838
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2797
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2839
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2798
2840
|
player == .P1
|
|
2799
2841
|
else if (showdown)
|
|
2800
2842
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2806,7 +2848,7 @@ pub const Rolls = struct {
|
|
|
2806
2848
|
}
|
|
2807
2849
|
|
|
2808
2850
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2809
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2851
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2810
2852
|
val == .true
|
|
2811
2853
|
else if (showdown)
|
|
2812
2854
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2818,7 +2860,7 @@ pub const Rolls = struct {
|
|
|
2818
2860
|
}
|
|
2819
2861
|
|
|
2820
2862
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2821
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2863
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2822
2864
|
val == .true
|
|
2823
2865
|
else if (showdown)
|
|
2824
2866
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2829,20 +2871,22 @@ pub const Rolls = struct {
|
|
|
2829
2871
|
return ok;
|
|
2830
2872
|
}
|
|
2831
2873
|
|
|
2832
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2833
|
-
|
|
2874
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2875
|
+
_ = .{ player, options };
|
|
2876
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2834
2877
|
val == .true
|
|
2835
2878
|
else if (showdown)
|
|
2836
2879
|
battle.rng.chance(u8, rate, 256)
|
|
2837
2880
|
else
|
|
2838
2881
|
battle.rng.next() < rate;
|
|
2839
2882
|
|
|
2840
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2883
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2841
2884
|
return crit;
|
|
2842
2885
|
}
|
|
2843
2886
|
|
|
2844
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2845
|
-
|
|
2887
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2888
|
+
_ = .{ player, options };
|
|
2889
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2846
2890
|
val
|
|
2847
2891
|
else roll: {
|
|
2848
2892
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2853,12 +2897,12 @@ pub const Rolls = struct {
|
|
|
2853
2897
|
};
|
|
2854
2898
|
|
|
2855
2899
|
assert(roll >= 217 and roll <= 255);
|
|
2856
|
-
try options.chance.damage(player, roll);
|
|
2900
|
+
try options.chance.damage(player, i, roll);
|
|
2857
2901
|
return roll;
|
|
2858
2902
|
}
|
|
2859
2903
|
|
|
2860
2904
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2861
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2905
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2862
2906
|
val == .true
|
|
2863
2907
|
else if (showdown)
|
|
2864
2908
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2870,7 +2914,7 @@ pub const Rolls = struct {
|
|
|
2870
2914
|
}
|
|
2871
2915
|
|
|
2872
2916
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2873
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2917
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2874
2918
|
val == .true
|
|
2875
2919
|
else if (showdown)
|
|
2876
2920
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2882,7 +2926,7 @@ pub const Rolls = struct {
|
|
|
2882
2926
|
}
|
|
2883
2927
|
|
|
2884
2928
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2885
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2929
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2886
2930
|
val == .true
|
|
2887
2931
|
else if (showdown)
|
|
2888
2932
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2894,7 +2938,7 @@ pub const Rolls = struct {
|
|
|
2894
2938
|
}
|
|
2895
2939
|
|
|
2896
2940
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2897
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2941
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2898
2942
|
val == .true
|
|
2899
2943
|
else if (showdown)
|
|
2900
2944
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2907,7 +2951,7 @@ pub const Rolls = struct {
|
|
|
2907
2951
|
|
|
2908
2952
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2909
2953
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2910
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2954
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2911
2955
|
val == .true
|
|
2912
2956
|
else if (showdown)
|
|
2913
2957
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2919,7 +2963,7 @@ pub const Rolls = struct {
|
|
|
2919
2963
|
}
|
|
2920
2964
|
|
|
2921
2965
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2922
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2966
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2923
2967
|
val == .true
|
|
2924
2968
|
else if (showdown)
|
|
2925
2969
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2931,7 +2975,7 @@ pub const Rolls = struct {
|
|
|
2931
2975
|
}
|
|
2932
2976
|
|
|
2933
2977
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2934
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2978
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2935
2979
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2936
2980
|
else if (showdown)
|
|
2937
2981
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2947,7 +2991,7 @@ pub const Rolls = struct {
|
|
|
2947
2991
|
}
|
|
2948
2992
|
|
|
2949
2993
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2950
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
2994
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2951
2995
|
(val - 1) * 40
|
|
2952
2996
|
else if (showdown) power: {
|
|
2953
2997
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2969,7 +3013,7 @@ pub const Rolls = struct {
|
|
|
2969
3013
|
}
|
|
2970
3014
|
|
|
2971
3015
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2972
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3016
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2973
3017
|
val + 3
|
|
2974
3018
|
else if (showdown) num: {
|
|
2975
3019
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2997,7 +3041,7 @@ pub const Rolls = struct {
|
|
|
2997
3041
|
}
|
|
2998
3042
|
|
|
2999
3043
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
3000
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3044
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
3001
3045
|
val
|
|
3002
3046
|
else if (showdown)
|
|
3003
3047
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3013,13 +3057,12 @@ pub const Rolls = struct {
|
|
|
3013
3057
|
return hits;
|
|
3014
3058
|
}
|
|
3015
3059
|
|
|
3016
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3017
|
-
|
|
3060
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3061
|
+
assert(!showdown);
|
|
3062
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3018
3063
|
val + 1
|
|
3019
|
-
else if (showdown)
|
|
3020
|
-
battle.rng.range(u8, 2, 6)
|
|
3021
3064
|
else
|
|
3022
|
-
(battle.rng.next() & 3) + 2;
|
|
3065
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3023
3066
|
|
|
3024
3067
|
assert(pp >= 2 and pp <= 5);
|
|
3025
3068
|
try options.chance.spite(player, pp);
|
|
@@ -3033,7 +3076,7 @@ pub const Rolls = struct {
|
|
|
3033
3076
|
var i: u8 = 0;
|
|
3034
3077
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3035
3078
|
assert(mtype != .@"???");
|
|
3036
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3079
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3037
3080
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3038
3081
|
else if (showdown) ty: {
|
|
3039
3082
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3067,7 +3110,7 @@ pub const Rolls = struct {
|
|
|
3067
3110
|
assert(count <= 8);
|
|
3068
3111
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3069
3112
|
|
|
3070
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3113
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3071
3114
|
val == .true
|
|
3072
3115
|
else if (showdown)
|
|
3073
3116
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3089,7 +3132,7 @@ pub const Rolls = struct {
|
|
|
3089
3132
|
const moves = active.moves;
|
|
3090
3133
|
|
|
3091
3134
|
// TODO: consider throwing error instead of rerolling?
|
|
3092
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3135
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3093
3136
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3094
3137
|
else
|
|
3095
3138
|
null;
|
|
@@ -3126,7 +3169,7 @@ pub const Rolls = struct {
|
|
|
3126
3169
|
const moves = active.moves;
|
|
3127
3170
|
|
|
3128
3171
|
// TODO: consider throwing error instead of rerolling?
|
|
3129
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3172
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3130
3173
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3131
3174
|
else
|
|
3132
3175
|
null;
|
|
@@ -3170,7 +3213,7 @@ pub const Rolls = struct {
|
|
|
3170
3213
|
assert(n > 0 and n <= 6);
|
|
3171
3214
|
|
|
3172
3215
|
// TODO: consider throwing error instead of rerolling?
|
|
3173
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3216
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3174
3217
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3175
3218
|
break :val null;
|
|
3176
3219
|
} else null;
|
|
@@ -3196,7 +3239,7 @@ pub const Rolls = struct {
|
|
|
3196
3239
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3197
3240
|
|
|
3198
3241
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3199
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3242
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3200
3243
|
@intCast(val)
|
|
3201
3244
|
else if (showdown)
|
|
3202
3245
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3211,7 +3254,7 @@ pub const Rolls = struct {
|
|
|
3211
3254
|
}
|
|
3212
3255
|
|
|
3213
3256
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3214
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3257
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3215
3258
|
val
|
|
3216
3259
|
else if (showdown)
|
|
3217
3260
|
battle.rng.range(u8, 1, max)
|
|
@@ -3237,7 +3280,7 @@ pub const Rolls = struct {
|
|
|
3237
3280
|
}
|
|
3238
3281
|
|
|
3239
3282
|
// TODO: consider throwing error instead of rerolling?
|
|
3240
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3283
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3241
3284
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3242
3285
|
break :val val;
|
|
3243
3286
|
} else null;
|
|
@@ -3269,7 +3312,7 @@ pub const Rolls = struct {
|
|
|
3269
3312
|
}
|
|
3270
3313
|
|
|
3271
3314
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3272
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3315
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3273
3316
|
@intCast(val)
|
|
3274
3317
|
else if (showdown)
|
|
3275
3318
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3281,12 +3324,12 @@ pub const Rolls = struct {
|
|
|
3281
3324
|
};
|
|
3282
3325
|
|
|
3283
3326
|
assert(duration >= 1 and duration <= 6);
|
|
3284
|
-
options.chance.duration(
|
|
3327
|
+
options.chance.duration(player, duration);
|
|
3285
3328
|
return duration + 1;
|
|
3286
3329
|
}
|
|
3287
3330
|
|
|
3288
3331
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3289
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3332
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3290
3333
|
@intCast(val)
|
|
3291
3334
|
else if (showdown)
|
|
3292
3335
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3295,16 +3338,16 @@ pub const Rolls = struct {
|
|
|
3295
3338
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3296
3339
|
|
|
3297
3340
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3298
|
-
options.chance.duration(
|
|
3341
|
+
options.chance.duration(player, duration);
|
|
3299
3342
|
return duration;
|
|
3300
3343
|
}
|
|
3301
3344
|
|
|
3302
3345
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3303
|
-
const duration:
|
|
3346
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3304
3347
|
@intCast(val)
|
|
3305
3348
|
else if (showdown)
|
|
3306
3349
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3307
|
-
battle.rng.range(
|
|
3350
|
+
battle.rng.range(u3, 2, 6)
|
|
3308
3351
|
else duration: {
|
|
3309
3352
|
while (true) {
|
|
3310
3353
|
const r = battle.rng.next() & 7;
|
|
@@ -3313,12 +3356,12 @@ pub const Rolls = struct {
|
|
|
3313
3356
|
};
|
|
3314
3357
|
|
|
3315
3358
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3316
|
-
options.chance.duration(
|
|
3359
|
+
options.chance.duration(player, duration);
|
|
3317
3360
|
return duration;
|
|
3318
3361
|
}
|
|
3319
3362
|
|
|
3320
3363
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3321
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3364
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3322
3365
|
@intCast(val)
|
|
3323
3366
|
else if (showdown)
|
|
3324
3367
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3326,12 +3369,12 @@ pub const Rolls = struct {
|
|
|
3326
3369
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3327
3370
|
|
|
3328
3371
|
assert(duration >= 2 and duration <= 3);
|
|
3329
|
-
options.chance.duration(
|
|
3372
|
+
options.chance.duration(player, duration);
|
|
3330
3373
|
return duration;
|
|
3331
3374
|
}
|
|
3332
3375
|
|
|
3333
3376
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3334
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3377
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3335
3378
|
@intCast(val)
|
|
3336
3379
|
else if (showdown)
|
|
3337
3380
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3340,13 +3383,13 @@ pub const Rolls = struct {
|
|
|
3340
3383
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3341
3384
|
|
|
3342
3385
|
assert(duration >= 2 and duration <= 5);
|
|
3343
|
-
options.chance.duration(
|
|
3386
|
+
options.chance.duration(player, duration);
|
|
3344
3387
|
return duration + 1;
|
|
3345
3388
|
}
|
|
3346
3389
|
|
|
3347
3390
|
// TODO: consider sharing implementation with bindingDuration
|
|
3348
3391
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3349
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3392
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3350
3393
|
@intCast(val)
|
|
3351
3394
|
else if (showdown)
|
|
3352
3395
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3354,7 +3397,7 @@ pub const Rolls = struct {
|
|
|
3354
3397
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3355
3398
|
|
|
3356
3399
|
assert(duration >= 2 and duration <= 5);
|
|
3357
|
-
options.chance.duration(
|
|
3400
|
+
options.chance.duration(player, duration);
|
|
3358
3401
|
return duration + 1;
|
|
3359
3402
|
}
|
|
3360
3403
|
};
|
|
@@ -3467,7 +3510,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3467
3510
|
.Move => {
|
|
3468
3511
|
const side = battle.side(player);
|
|
3469
3512
|
|
|
3470
|
-
|
|
3513
|
+
const active = &side.active;
|
|
3471
3514
|
const stored = side.stored();
|
|
3472
3515
|
|
|
3473
3516
|
// While players are not given any input options on the cartridge in these cases,
|