@pkmn/engine 0.1.0-dev.d4119c62 → 0.1.0-dev.d4bd80b1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -3
- package/README.md +46 -18
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +110 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +25 -3
- package/build/pkg/addon.js +39 -71
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +40 -90
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +109 -107
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +53 -78
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +152 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +359 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +33 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +330 -104
- package/package.json +60 -134
- package/src/bin/install-pkmn-engine +32 -21
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +255 -0
- package/src/lib/{binding → bindings}/node.zig +8 -21
- package/src/lib/bindings/wasm.zig +59 -0
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +39 -8
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +14 -10
- package/src/lib/common/options.zig +6 -0
- package/src/lib/common/protocol.zig +63 -67
- 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 +379 -258
- 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 +217 -163
- package/src/lib/node.zig +18 -0
- package/src/lib/pkmn.zig +37 -15
- package/src/lib/wasm.zig +17 -0
- package/src/tools/display/dom.ts +95 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/binding/wasm.zig +0 -12
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -1,50 +1,37 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
4
|
-
|
|
5
|
-
const common = @import("../common/data.zig");
|
|
6
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
|
-
const protocol = @import("../common/protocol.zig");
|
|
8
|
-
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
11
5
|
const data = @import("data.zig");
|
|
12
6
|
const generated = @import("generated.zig");
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
18
|
-
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
19
|
-
|
|
20
|
-
const showdown = pkmn.options.showdown;
|
|
21
|
-
|
|
22
|
-
const Choice = common.Choice;
|
|
23
|
-
const ID = common.ID;
|
|
24
|
-
const Player = common.Player;
|
|
25
|
-
const Result = common.Result;
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
9
|
+
const rng = @import("../common/rng.zig");
|
|
10
|
+
const std = @import("std");
|
|
26
11
|
|
|
27
12
|
const Activate = protocol.Activate;
|
|
13
|
+
const ActivePokemon = data.ActivePokemon;
|
|
14
|
+
const assert = std.debug.assert;
|
|
28
15
|
const Boost = protocol.Boost;
|
|
16
|
+
const Boosts = data.Boosts;
|
|
17
|
+
const Choice = common.Choice;
|
|
29
18
|
const Damage = protocol.Damage;
|
|
19
|
+
const Effectiveness = data.Effectiveness;
|
|
30
20
|
const End = protocol.End;
|
|
21
|
+
const expectEqual = std.testing.expectEqual;
|
|
22
|
+
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
23
|
+
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
31
24
|
const Fail = protocol.Fail;
|
|
32
|
-
const Heal = protocol.Heal;
|
|
33
|
-
const Start = protocol.Start;
|
|
34
|
-
|
|
35
25
|
const Gen12 = rng.Gen12;
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const ActivePokemon = data.ActivePokemon;
|
|
40
|
-
const Boosts = data.Boosts;
|
|
41
|
-
const Effectiveness = data.Effectiveness;
|
|
26
|
+
const Heal = protocol.Heal;
|
|
27
|
+
const ID = common.ID;
|
|
42
28
|
const Item = data.Item;
|
|
43
29
|
const Move = data.Move;
|
|
44
|
-
const
|
|
45
|
-
const
|
|
30
|
+
const Player = common.Player;
|
|
31
|
+
const Result = common.Result;
|
|
32
|
+
const showdown = pkmn.options.showdown;
|
|
46
33
|
const Side = data.Side;
|
|
47
|
-
const
|
|
34
|
+
const Start = protocol.Start;
|
|
48
35
|
const Stats = data.Stats;
|
|
49
36
|
const Status = data.Status;
|
|
50
37
|
const TriAttack = data.TriAttack;
|
|
@@ -106,6 +93,7 @@ pub const State = struct {
|
|
|
106
93
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
94
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
95
|
move: Move, // wCurPlayerMove
|
|
96
|
+
hit: u3 = 0,
|
|
109
97
|
from: Move = .None,
|
|
110
98
|
mslot: u4, // wCurMoveNum
|
|
111
99
|
first: bool, // wEnemyGoesFirst
|
|
@@ -140,11 +128,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
140
128
|
const p1 = battle.side(.P1);
|
|
141
129
|
const p2 = battle.side(.P2);
|
|
142
130
|
|
|
143
|
-
|
|
131
|
+
const p1_slot = findFirstAlive(p1);
|
|
144
132
|
assert(!showdown or p1_slot == 1);
|
|
145
133
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
146
134
|
|
|
147
|
-
|
|
135
|
+
const p2_slot = findFirstAlive(p2);
|
|
148
136
|
assert(!showdown or p2_slot == 1);
|
|
149
137
|
if (p2_slot == 0) return Result.Win;
|
|
150
138
|
|
|
@@ -256,7 +244,6 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
256
244
|
} else {
|
|
257
245
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
246
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
247
|
|
|
261
248
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
249
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
@@ -403,7 +390,9 @@ fn executeMove(
|
|
|
403
390
|
|
|
404
391
|
assert(choice.type == .Move);
|
|
405
392
|
var mslot: u4 = @intCast(choice.data);
|
|
393
|
+
_ = &mslot;
|
|
406
394
|
var move = Move.Pound; // TODO
|
|
395
|
+
_ = &move;
|
|
407
396
|
|
|
408
397
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
409
398
|
|
|
@@ -440,18 +429,9 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
440
429
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
441
430
|
const before = stored.status;
|
|
442
431
|
// 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
432
|
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
433
|
|
|
454
|
-
if (duration == 0) {
|
|
434
|
+
if (Status.duration(stored.status) == 0) {
|
|
455
435
|
try log.curestatus(.{ ident, before, .Message });
|
|
456
436
|
stored.status = 0; // clears EXT if present
|
|
457
437
|
resetCant(volatiles);
|
|
@@ -481,12 +461,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
481
461
|
}
|
|
482
462
|
|
|
483
463
|
if (volatiles.disable.duration > 0) {
|
|
484
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
485
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
486
|
-
// } else {
|
|
487
464
|
volatiles.disable.duration -= 1;
|
|
488
|
-
// }
|
|
489
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
490
465
|
|
|
491
466
|
if (volatiles.disable.duration == 0) {
|
|
492
467
|
volatiles.disable.move = 0;
|
|
@@ -496,13 +471,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
496
471
|
|
|
497
472
|
if (volatiles.Confusion) {
|
|
498
473
|
assert(volatiles.confusion > 0);
|
|
499
|
-
|
|
500
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
501
|
-
// if (!extend) volatiles.confusion = 0;
|
|
502
|
-
// } else {
|
|
503
474
|
volatiles.confusion -= 1;
|
|
504
|
-
// }
|
|
505
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
506
475
|
|
|
507
476
|
if (volatiles.confusion == 0) {
|
|
508
477
|
volatiles.Confusion = false;
|
|
@@ -565,7 +534,7 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
565
534
|
|
|
566
535
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
567
536
|
var side = battle.side(player);
|
|
568
|
-
|
|
537
|
+
const volatiles = &side.active.volatiles;
|
|
569
538
|
|
|
570
539
|
// FIXME: pass in target to avoid lookup?
|
|
571
540
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
@@ -663,7 +632,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
663
632
|
}
|
|
664
633
|
assert(stage <= 4);
|
|
665
634
|
|
|
666
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
635
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
667
636
|
}
|
|
668
637
|
|
|
669
638
|
pub fn calcDamage(
|
|
@@ -731,7 +700,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
731
700
|
|
|
732
701
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
733
702
|
if (state.damage <= 1) return;
|
|
734
|
-
const random = try Rolls.damage(battle, player, options);
|
|
703
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
735
704
|
options.calc.base(player.foe(), state.damage);
|
|
736
705
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
737
706
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -997,7 +966,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
997
966
|
|
|
998
967
|
// Weather
|
|
999
968
|
if (battle.field.weather != .None) {
|
|
1000
|
-
assert(battle.field.weather_duration > 0);
|
|
1001
969
|
battle.field.weather_duration -= 1;
|
|
1002
970
|
|
|
1003
971
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1056,7 +1024,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1056
1024
|
var volatiles = &side.active.volatiles;
|
|
1057
1025
|
|
|
1058
1026
|
if (volatiles.PerishSong) {
|
|
1059
|
-
assert(volatiles.perish_song > 0);
|
|
1060
1027
|
volatiles.perish_song -= 1;
|
|
1061
1028
|
|
|
1062
1029
|
assert(volatiles.perish_song < 3);
|
|
@@ -1086,7 +1053,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1086
1053
|
|
|
1087
1054
|
// Mystery Berry
|
|
1088
1055
|
inline for (players) |player| {
|
|
1089
|
-
|
|
1056
|
+
const stored = battle.side(player).stored();
|
|
1090
1057
|
|
|
1091
1058
|
if (stored.item == .MysteryBerry) {
|
|
1092
1059
|
// TODO stored and active moves, transform, etc
|
|
@@ -1193,7 +1160,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1193
1160
|
var volatiles = &side.active.volatiles;
|
|
1194
1161
|
|
|
1195
1162
|
if (volatiles.Encore) {
|
|
1196
|
-
assert(volatiles.encore > 0);
|
|
1197
1163
|
volatiles.encore -= 1;
|
|
1198
1164
|
|
|
1199
1165
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
@@ -1215,7 +1181,6 @@ fn updateSideCondition(
|
|
|
1215
1181
|
) !void {
|
|
1216
1182
|
var side = battle.side(player);
|
|
1217
1183
|
if (@field(side.conditions, key)) {
|
|
1218
|
-
assert(@field(side.conditions, value) > 0);
|
|
1219
1184
|
@field(side.conditions, value) -= 1;
|
|
1220
1185
|
|
|
1221
1186
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1228,7 +1193,7 @@ fn updateSideCondition(
|
|
|
1228
1193
|
fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
|
|
1229
1194
|
battle.turn += 1;
|
|
1230
1195
|
|
|
1231
|
-
if (
|
|
1196
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1232
1197
|
try options.log.tie(.{});
|
|
1233
1198
|
return Result.Tie;
|
|
1234
1199
|
} else if (battle.turn >= 65535) {
|
|
@@ -1306,14 +1271,32 @@ pub const Effects = struct {
|
|
|
1306
1271
|
}
|
|
1307
1272
|
|
|
1308
1273
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1274
|
+
assert(!state.miss);
|
|
1275
|
+
var side = battle.side(player);
|
|
1276
|
+
|
|
1277
|
+
const ident = battle.active(player);
|
|
1278
|
+
|
|
1279
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1280
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1281
|
+
return options.log.fail(.{ ident, .None });
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1285
|
+
if (side.stored().hp <= hp) {
|
|
1286
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1287
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1288
|
+
return options.log.fail(.{ ident, .None });
|
|
1289
|
+
} else {
|
|
1290
|
+
side.stored().hp -= hp;
|
|
1291
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1292
|
+
|
|
1293
|
+
const before = side.active.boosts.atk;
|
|
1294
|
+
var i: usize = 0;
|
|
1295
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1296
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1297
|
+
}
|
|
1298
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1299
|
+
}
|
|
1317
1300
|
}
|
|
1318
1301
|
|
|
1319
1302
|
pub const bide = struct {
|
|
@@ -1325,14 +1308,7 @@ pub const Effects = struct {
|
|
|
1325
1308
|
|
|
1326
1309
|
if (!volatiles.Bide) return;
|
|
1327
1310
|
|
|
1328
|
-
assert(volatiles.attacks > 0);
|
|
1329
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1330
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1331
|
-
// } else {
|
|
1332
1311
|
volatiles.attacks -= 1;
|
|
1333
|
-
// }
|
|
1334
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1335
|
-
|
|
1336
1312
|
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1337
1313
|
|
|
1338
1314
|
volatiles.Bide = false;
|
|
@@ -1451,7 +1427,8 @@ pub const Effects = struct {
|
|
|
1451
1427
|
if (move.type.special() != (last == .MirrorCoat)) return;
|
|
1452
1428
|
if (state.damage == 0) return;
|
|
1453
1429
|
|
|
1454
|
-
|
|
1430
|
+
// NOTE: llvm/llvm-project#58557
|
|
1431
|
+
state.damage = smul(state.damage, 2);
|
|
1455
1432
|
state.miss = false;
|
|
1456
1433
|
}
|
|
1457
1434
|
|
|
@@ -1478,8 +1455,8 @@ pub const Effects = struct {
|
|
|
1478
1455
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1479
1456
|
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1480
1457
|
} else {
|
|
1481
|
-
|
|
1482
|
-
|
|
1458
|
+
const stats = &side.active.stats;
|
|
1459
|
+
const boosts = &side.active.boosts;
|
|
1483
1460
|
|
|
1484
1461
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1485
1462
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
@@ -1546,15 +1523,18 @@ pub const Effects = struct {
|
|
|
1546
1523
|
|
|
1547
1524
|
pub const double = struct {
|
|
1548
1525
|
pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1549
|
-
if (battle.foe(player).active.volatiles.Flying)
|
|
1526
|
+
if (!battle.foe(player).active.volatiles.Flying) return;
|
|
1527
|
+
state.damage = smul(state.damage, 2);
|
|
1550
1528
|
}
|
|
1551
1529
|
|
|
1552
1530
|
pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1553
|
-
if (battle.foe(player).active.volatiles.Underground)
|
|
1531
|
+
if (!battle.foe(player).active.volatiles.Underground) return;
|
|
1532
|
+
state.damage = smul(state.damage, 2);
|
|
1554
1533
|
}
|
|
1555
1534
|
|
|
1556
1535
|
pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1557
|
-
if (battle.foe(player).active.volatiles.minimized)
|
|
1536
|
+
if (!battle.foe(player).active.volatiles.minimized) return;
|
|
1537
|
+
state.damage = smul(state.damage, 2);
|
|
1558
1538
|
}
|
|
1559
1539
|
};
|
|
1560
1540
|
|
|
@@ -1599,8 +1579,16 @@ pub const Effects = struct {
|
|
|
1599
1579
|
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1600
1580
|
}
|
|
1601
1581
|
|
|
1602
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1603
|
-
|
|
1582
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1583
|
+
var side = battle.side(player);
|
|
1584
|
+
var stored = side.stored();
|
|
1585
|
+
|
|
1586
|
+
stored.hp = 0;
|
|
1587
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1588
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1589
|
+
if (!showdown) stored.status = 0;
|
|
1590
|
+
side.active.volatiles.LeechSeed = false;
|
|
1591
|
+
side.active.volatiles.DestinyBond = false;
|
|
1604
1592
|
}
|
|
1605
1593
|
|
|
1606
1594
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1715,7 +1703,7 @@ pub const Effects = struct {
|
|
|
1715
1703
|
if (!state.proc) return;
|
|
1716
1704
|
if (foe.conditions.Safeguard) return;
|
|
1717
1705
|
// Freeze Clause Mod
|
|
1718
|
-
if (
|
|
1706
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1719
1707
|
|
|
1720
1708
|
foe_stored.status = Status.init(.FRZ);
|
|
1721
1709
|
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
@@ -1726,8 +1714,26 @@ pub const Effects = struct {
|
|
|
1726
1714
|
foe.active.volatiles.frozen = true;
|
|
1727
1715
|
}
|
|
1728
1716
|
|
|
1729
|
-
|
|
1730
|
-
|
|
1717
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1718
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1719
|
+
var side = battle.side(player);
|
|
1720
|
+
var volatiles = &side.active.volatiles;
|
|
1721
|
+
|
|
1722
|
+
if (state.miss) {
|
|
1723
|
+
volatiles.fury_cutter = 0;
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1728
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1729
|
+
volatiles.fury_cutter += 1;
|
|
1730
|
+
|
|
1731
|
+
if (showdown) {
|
|
1732
|
+
state.bp *= 2;
|
|
1733
|
+
assert(state.bp <= 160);
|
|
1734
|
+
} else {
|
|
1735
|
+
state.damage = smul(state.damage, 2);
|
|
1736
|
+
}
|
|
1731
1737
|
}
|
|
1732
1738
|
|
|
1733
1739
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1888,7 +1894,29 @@ pub const Effects = struct {
|
|
|
1888
1894
|
}
|
|
1889
1895
|
|
|
1890
1896
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1891
|
-
|
|
1897
|
+
var side = battle.side(player);
|
|
1898
|
+
|
|
1899
|
+
if (state.miss) {
|
|
1900
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1901
|
+
try options.log.lastmiss(.{});
|
|
1902
|
+
return options.log.miss(.{battle.active(player)});
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
const move = battle.foe(player).lastMove(false);
|
|
1906
|
+
if (move == .None or move == .Struggle) {
|
|
1907
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
var slot: u4 = 0;
|
|
1911
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1912
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1913
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1914
|
+
}
|
|
1915
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1916
|
+
|
|
1917
|
+
side.active.move(slot).id = move;
|
|
1918
|
+
side.active.move(slot).pp = 5;
|
|
1919
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1892
1920
|
}
|
|
1893
1921
|
|
|
1894
1922
|
pub fn mirrorMove(
|
|
@@ -2175,7 +2203,7 @@ pub const Effects = struct {
|
|
|
2175
2203
|
|
|
2176
2204
|
pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2177
2205
|
// TODO: remove switching variable and just look at Choice saved in Battle struct?
|
|
2178
|
-
if (battle.foe(player).active.volatiles.switching) state.damage
|
|
2206
|
+
if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
|
|
2179
2207
|
}
|
|
2180
2208
|
|
|
2181
2209
|
pub const rage = struct {
|
|
@@ -2185,11 +2213,10 @@ pub const Effects = struct {
|
|
|
2185
2213
|
}
|
|
2186
2214
|
|
|
2187
2215
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2188
|
-
|
|
2189
|
-
var volatiles = &side.active.volatiles;
|
|
2216
|
+
const side = battle.side(player);
|
|
2190
2217
|
|
|
2191
|
-
assert(volatiles.Rage);
|
|
2192
|
-
state.damage
|
|
2218
|
+
assert(side.active.volatiles.Rage);
|
|
2219
|
+
state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
|
|
2193
2220
|
}
|
|
2194
2221
|
};
|
|
2195
2222
|
|
|
@@ -2284,7 +2311,7 @@ pub const Effects = struct {
|
|
|
2284
2311
|
side.active.volatiles.attacks += 1;
|
|
2285
2312
|
side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
|
|
2286
2313
|
|
|
2287
|
-
if (side.active.volatiles.DefenseCurl) state.damage
|
|
2314
|
+
if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
|
|
2288
2315
|
}
|
|
2289
2316
|
};
|
|
2290
2317
|
|
|
@@ -2354,7 +2381,7 @@ pub const Effects = struct {
|
|
|
2354
2381
|
}
|
|
2355
2382
|
|
|
2356
2383
|
// Sleep Clause Mod
|
|
2357
|
-
if (
|
|
2384
|
+
if (pkmn.options.mod) {
|
|
2358
2385
|
for (foe.pokemon) |p| {
|
|
2359
2386
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2360
2387
|
}
|
|
@@ -2397,17 +2424,37 @@ pub const Effects = struct {
|
|
|
2397
2424
|
}
|
|
2398
2425
|
|
|
2399
2426
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2400
|
-
|
|
2401
|
-
|
|
2427
|
+
var foe = battle.foe(player);
|
|
2428
|
+
|
|
2429
|
+
const last = foe.lastMove(false);
|
|
2430
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2431
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
var slot: u4 = 0;
|
|
2435
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2436
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2437
|
+
}
|
|
2438
|
+
// If the move was called via Metronome it might not exist
|
|
2439
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2440
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2441
|
+
}
|
|
2402
2442
|
|
|
2403
|
-
//
|
|
2443
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2444
|
+
const pp = @min(
|
|
2445
|
+
foe.active.move(slot).pp,
|
|
2446
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2447
|
+
);
|
|
2448
|
+
foe.active.move(slot).pp -= pp;
|
|
2449
|
+
if (!foe.active.volatiles.Transform) {
|
|
2450
|
+
var move_slot = foe.stored().move(slot);
|
|
2451
|
+
assert(move_slot.pp > 0);
|
|
2404
2452
|
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
// }
|
|
2453
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2454
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2411
2458
|
}
|
|
2412
2459
|
|
|
2413
2460
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2541,8 +2588,8 @@ pub const Effects = struct {
|
|
|
2541
2588
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2542
2589
|
var side = battle.side(player);
|
|
2543
2590
|
|
|
2544
|
-
|
|
2545
|
-
|
|
2591
|
+
const stats = &side.active.stats;
|
|
2592
|
+
const boosts = &side.active.boosts;
|
|
2546
2593
|
|
|
2547
2594
|
if (!state.proc) return;
|
|
2548
2595
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
@@ -2563,7 +2610,7 @@ pub const Effects = struct {
|
|
|
2563
2610
|
|
|
2564
2611
|
if (state.miss) return log.fail(.{ ident, .None });
|
|
2565
2612
|
|
|
2566
|
-
|
|
2613
|
+
const stats = &side.active.stats;
|
|
2567
2614
|
var boosts = &side.active.boosts;
|
|
2568
2615
|
|
|
2569
2616
|
const move = Move.get(state.move);
|
|
@@ -2634,7 +2681,7 @@ pub const Effects = struct {
|
|
|
2634
2681
|
|
|
2635
2682
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2636
2683
|
|
|
2637
|
-
|
|
2684
|
+
const stats = &foe.active.stats;
|
|
2638
2685
|
var boosts = &foe.active.boosts;
|
|
2639
2686
|
|
|
2640
2687
|
const move = Move.get(state.move);
|
|
@@ -2718,9 +2765,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2718
2765
|
}
|
|
2719
2766
|
|
|
2720
2767
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2721
|
-
|
|
2768
|
+
const stats = unmodifiedStats(battle, side);
|
|
2722
2769
|
inline for (STATS) |s| {
|
|
2723
|
-
|
|
2770
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2724
2771
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2725
2772
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2726
2773
|
}
|
|
@@ -2743,7 +2790,7 @@ fn boostStat(
|
|
|
2743
2790
|
n: u2,
|
|
2744
2791
|
) !void {
|
|
2745
2792
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2746
|
-
|
|
2793
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2747
2794
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2748
2795
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2749
2796
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2766,7 +2813,7 @@ fn unboostStat(
|
|
|
2766
2813
|
n: u2,
|
|
2767
2814
|
) !void {
|
|
2768
2815
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2769
|
-
|
|
2816
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2770
2817
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2771
2818
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2772
2819
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2792,9 +2839,15 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2792
2839
|
return t != .@"???" and !active.types.includes(t);
|
|
2793
2840
|
}
|
|
2794
2841
|
|
|
2842
|
+
// NOTE: llvm/llvm-project#58557
|
|
2843
|
+
fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
|
2844
|
+
if (!comptime builtin.target.isWasm()) return a *| b;
|
|
2845
|
+
return @max(a *% b, std.math.maxInt(@TypeOf(a)));
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2795
2848
|
pub const Rolls = struct {
|
|
2796
2849
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2797
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2850
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2798
2851
|
player == .P1
|
|
2799
2852
|
else if (showdown)
|
|
2800
2853
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2806,7 +2859,7 @@ pub const Rolls = struct {
|
|
|
2806
2859
|
}
|
|
2807
2860
|
|
|
2808
2861
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2809
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2862
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2810
2863
|
val == .true
|
|
2811
2864
|
else if (showdown)
|
|
2812
2865
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2818,7 +2871,7 @@ pub const Rolls = struct {
|
|
|
2818
2871
|
}
|
|
2819
2872
|
|
|
2820
2873
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2821
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2874
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2822
2875
|
val == .true
|
|
2823
2876
|
else if (showdown)
|
|
2824
2877
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2829,20 +2882,22 @@ pub const Rolls = struct {
|
|
|
2829
2882
|
return ok;
|
|
2830
2883
|
}
|
|
2831
2884
|
|
|
2832
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2833
|
-
|
|
2885
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2886
|
+
_ = .{ player, options };
|
|
2887
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2834
2888
|
val == .true
|
|
2835
2889
|
else if (showdown)
|
|
2836
2890
|
battle.rng.chance(u8, rate, 256)
|
|
2837
2891
|
else
|
|
2838
2892
|
battle.rng.next() < rate;
|
|
2839
2893
|
|
|
2840
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2894
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2841
2895
|
return crit;
|
|
2842
2896
|
}
|
|
2843
2897
|
|
|
2844
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2845
|
-
|
|
2898
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2899
|
+
_ = .{ player, options };
|
|
2900
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2846
2901
|
val
|
|
2847
2902
|
else roll: {
|
|
2848
2903
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2853,12 +2908,12 @@ pub const Rolls = struct {
|
|
|
2853
2908
|
};
|
|
2854
2909
|
|
|
2855
2910
|
assert(roll >= 217 and roll <= 255);
|
|
2856
|
-
try options.chance.damage(player, roll);
|
|
2911
|
+
try options.chance.damage(player, i, roll);
|
|
2857
2912
|
return roll;
|
|
2858
2913
|
}
|
|
2859
2914
|
|
|
2860
2915
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2861
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2916
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2862
2917
|
val == .true
|
|
2863
2918
|
else if (showdown)
|
|
2864
2919
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2870,7 +2925,7 @@ pub const Rolls = struct {
|
|
|
2870
2925
|
}
|
|
2871
2926
|
|
|
2872
2927
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2873
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2928
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2874
2929
|
val == .true
|
|
2875
2930
|
else if (showdown)
|
|
2876
2931
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2882,7 +2937,7 @@ pub const Rolls = struct {
|
|
|
2882
2937
|
}
|
|
2883
2938
|
|
|
2884
2939
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2885
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2940
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2886
2941
|
val == .true
|
|
2887
2942
|
else if (showdown)
|
|
2888
2943
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2894,7 +2949,7 @@ pub const Rolls = struct {
|
|
|
2894
2949
|
}
|
|
2895
2950
|
|
|
2896
2951
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2897
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2952
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2898
2953
|
val == .true
|
|
2899
2954
|
else if (showdown)
|
|
2900
2955
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2907,7 +2962,7 @@ pub const Rolls = struct {
|
|
|
2907
2962
|
|
|
2908
2963
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2909
2964
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2910
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2965
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2911
2966
|
val == .true
|
|
2912
2967
|
else if (showdown)
|
|
2913
2968
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2919,7 +2974,7 @@ pub const Rolls = struct {
|
|
|
2919
2974
|
}
|
|
2920
2975
|
|
|
2921
2976
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2922
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2977
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2923
2978
|
val == .true
|
|
2924
2979
|
else if (showdown)
|
|
2925
2980
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2931,7 +2986,7 @@ pub const Rolls = struct {
|
|
|
2931
2986
|
}
|
|
2932
2987
|
|
|
2933
2988
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2934
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2989
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2935
2990
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2936
2991
|
else if (showdown)
|
|
2937
2992
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2947,7 +3002,7 @@ pub const Rolls = struct {
|
|
|
2947
3002
|
}
|
|
2948
3003
|
|
|
2949
3004
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2950
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3005
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2951
3006
|
(val - 1) * 40
|
|
2952
3007
|
else if (showdown) power: {
|
|
2953
3008
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2969,7 +3024,7 @@ pub const Rolls = struct {
|
|
|
2969
3024
|
}
|
|
2970
3025
|
|
|
2971
3026
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2972
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3027
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2973
3028
|
val + 3
|
|
2974
3029
|
else if (showdown) num: {
|
|
2975
3030
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2997,7 +3052,7 @@ pub const Rolls = struct {
|
|
|
2997
3052
|
}
|
|
2998
3053
|
|
|
2999
3054
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
3000
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3055
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
3001
3056
|
val
|
|
3002
3057
|
else if (showdown)
|
|
3003
3058
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3013,13 +3068,12 @@ pub const Rolls = struct {
|
|
|
3013
3068
|
return hits;
|
|
3014
3069
|
}
|
|
3015
3070
|
|
|
3016
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3017
|
-
|
|
3071
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3072
|
+
assert(!showdown);
|
|
3073
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3018
3074
|
val + 1
|
|
3019
|
-
else if (showdown)
|
|
3020
|
-
battle.rng.range(u8, 2, 6)
|
|
3021
3075
|
else
|
|
3022
|
-
(battle.rng.next() & 3) + 2;
|
|
3076
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3023
3077
|
|
|
3024
3078
|
assert(pp >= 2 and pp <= 5);
|
|
3025
3079
|
try options.chance.spite(player, pp);
|
|
@@ -3033,7 +3087,7 @@ pub const Rolls = struct {
|
|
|
3033
3087
|
var i: u8 = 0;
|
|
3034
3088
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3035
3089
|
assert(mtype != .@"???");
|
|
3036
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3090
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3037
3091
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3038
3092
|
else if (showdown) ty: {
|
|
3039
3093
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3067,7 +3121,7 @@ pub const Rolls = struct {
|
|
|
3067
3121
|
assert(count <= 8);
|
|
3068
3122
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3069
3123
|
|
|
3070
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3124
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3071
3125
|
val == .true
|
|
3072
3126
|
else if (showdown)
|
|
3073
3127
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3089,7 +3143,7 @@ pub const Rolls = struct {
|
|
|
3089
3143
|
const moves = active.moves;
|
|
3090
3144
|
|
|
3091
3145
|
// TODO: consider throwing error instead of rerolling?
|
|
3092
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3146
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3093
3147
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3094
3148
|
else
|
|
3095
3149
|
null;
|
|
@@ -3126,7 +3180,7 @@ pub const Rolls = struct {
|
|
|
3126
3180
|
const moves = active.moves;
|
|
3127
3181
|
|
|
3128
3182
|
// TODO: consider throwing error instead of rerolling?
|
|
3129
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3183
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3130
3184
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3131
3185
|
else
|
|
3132
3186
|
null;
|
|
@@ -3170,7 +3224,7 @@ pub const Rolls = struct {
|
|
|
3170
3224
|
assert(n > 0 and n <= 6);
|
|
3171
3225
|
|
|
3172
3226
|
// TODO: consider throwing error instead of rerolling?
|
|
3173
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3227
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3174
3228
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3175
3229
|
break :val null;
|
|
3176
3230
|
} else null;
|
|
@@ -3196,7 +3250,7 @@ pub const Rolls = struct {
|
|
|
3196
3250
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3197
3251
|
|
|
3198
3252
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3199
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3253
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3200
3254
|
@intCast(val)
|
|
3201
3255
|
else if (showdown)
|
|
3202
3256
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3211,7 +3265,7 @@ pub const Rolls = struct {
|
|
|
3211
3265
|
}
|
|
3212
3266
|
|
|
3213
3267
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3214
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3268
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3215
3269
|
val
|
|
3216
3270
|
else if (showdown)
|
|
3217
3271
|
battle.rng.range(u8, 1, max)
|
|
@@ -3237,7 +3291,7 @@ pub const Rolls = struct {
|
|
|
3237
3291
|
}
|
|
3238
3292
|
|
|
3239
3293
|
// TODO: consider throwing error instead of rerolling?
|
|
3240
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3294
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3241
3295
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3242
3296
|
break :val val;
|
|
3243
3297
|
} else null;
|
|
@@ -3269,7 +3323,7 @@ pub const Rolls = struct {
|
|
|
3269
3323
|
}
|
|
3270
3324
|
|
|
3271
3325
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3272
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3326
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3273
3327
|
@intCast(val)
|
|
3274
3328
|
else if (showdown)
|
|
3275
3329
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3281,12 +3335,12 @@ pub const Rolls = struct {
|
|
|
3281
3335
|
};
|
|
3282
3336
|
|
|
3283
3337
|
assert(duration >= 1 and duration <= 6);
|
|
3284
|
-
options.chance.duration(
|
|
3338
|
+
options.chance.duration(player, duration);
|
|
3285
3339
|
return duration + 1;
|
|
3286
3340
|
}
|
|
3287
3341
|
|
|
3288
3342
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3289
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3343
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3290
3344
|
@intCast(val)
|
|
3291
3345
|
else if (showdown)
|
|
3292
3346
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3295,16 +3349,16 @@ pub const Rolls = struct {
|
|
|
3295
3349
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3296
3350
|
|
|
3297
3351
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3298
|
-
options.chance.duration(
|
|
3352
|
+
options.chance.duration(player, duration);
|
|
3299
3353
|
return duration;
|
|
3300
3354
|
}
|
|
3301
3355
|
|
|
3302
3356
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3303
|
-
const duration:
|
|
3357
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3304
3358
|
@intCast(val)
|
|
3305
3359
|
else if (showdown)
|
|
3306
3360
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3307
|
-
battle.rng.range(
|
|
3361
|
+
battle.rng.range(u3, 2, 6)
|
|
3308
3362
|
else duration: {
|
|
3309
3363
|
while (true) {
|
|
3310
3364
|
const r = battle.rng.next() & 7;
|
|
@@ -3313,12 +3367,12 @@ pub const Rolls = struct {
|
|
|
3313
3367
|
};
|
|
3314
3368
|
|
|
3315
3369
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3316
|
-
options.chance.duration(
|
|
3370
|
+
options.chance.duration(player, duration);
|
|
3317
3371
|
return duration;
|
|
3318
3372
|
}
|
|
3319
3373
|
|
|
3320
3374
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3321
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3375
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3322
3376
|
@intCast(val)
|
|
3323
3377
|
else if (showdown)
|
|
3324
3378
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3326,12 +3380,12 @@ pub const Rolls = struct {
|
|
|
3326
3380
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3327
3381
|
|
|
3328
3382
|
assert(duration >= 2 and duration <= 3);
|
|
3329
|
-
options.chance.duration(
|
|
3383
|
+
options.chance.duration(player, duration);
|
|
3330
3384
|
return duration;
|
|
3331
3385
|
}
|
|
3332
3386
|
|
|
3333
3387
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3334
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3388
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3335
3389
|
@intCast(val)
|
|
3336
3390
|
else if (showdown)
|
|
3337
3391
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3340,13 +3394,13 @@ pub const Rolls = struct {
|
|
|
3340
3394
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3341
3395
|
|
|
3342
3396
|
assert(duration >= 2 and duration <= 5);
|
|
3343
|
-
options.chance.duration(
|
|
3397
|
+
options.chance.duration(player, duration);
|
|
3344
3398
|
return duration + 1;
|
|
3345
3399
|
}
|
|
3346
3400
|
|
|
3347
3401
|
// TODO: consider sharing implementation with bindingDuration
|
|
3348
3402
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3349
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3403
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3350
3404
|
@intCast(val)
|
|
3351
3405
|
else if (showdown)
|
|
3352
3406
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3354,7 +3408,7 @@ pub const Rolls = struct {
|
|
|
3354
3408
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3355
3409
|
|
|
3356
3410
|
assert(duration >= 2 and duration <= 5);
|
|
3357
|
-
options.chance.duration(
|
|
3411
|
+
options.chance.duration(player, duration);
|
|
3358
3412
|
return duration + 1;
|
|
3359
3413
|
}
|
|
3360
3414
|
};
|
|
@@ -3467,7 +3521,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3467
3521
|
.Move => {
|
|
3468
3522
|
const side = battle.side(player);
|
|
3469
3523
|
|
|
3470
|
-
|
|
3524
|
+
const active = &side.active;
|
|
3471
3525
|
const stored = side.stored();
|
|
3472
3526
|
|
|
3473
3527
|
// While players are not given any input options on the cartridge in these cases,
|