@pkmn/engine 0.1.0-dev.30c63015 → 0.1.0-dev.3158fa12
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 +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 +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 +13 -17
- package/build/pkg/gen1.js +6 -10
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +40 -91
- package/build/pkg/index.js +6 -63
- 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 -71
- 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 +142 -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 +287 -89
- package/package.json +58 -132
- package/src/bin/install-pkmn-engine +30 -20
- package/src/bin/pkmn-debug +1 -1
- 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 +223 -215
- 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 +517 -411
- 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 +367 -336
- 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 +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/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,55 +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;
|
|
29
|
-
const
|
|
30
|
-
const
|
|
16
|
+
const Boosts = data.Boosts;
|
|
17
|
+
const Choice = common.Choice;
|
|
31
18
|
const Damage = protocol.Damage;
|
|
19
|
+
const Effectiveness = data.Effectiveness;
|
|
32
20
|
const End = protocol.End;
|
|
33
|
-
const
|
|
21
|
+
const expectEqual = std.testing.expectEqual;
|
|
22
|
+
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
23
|
+
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
34
24
|
const Fail = protocol.Fail;
|
|
35
|
-
const Heal = protocol.Heal;
|
|
36
|
-
const Immune = protocol.Immune;
|
|
37
|
-
const SetHP = protocol.SetHP;
|
|
38
|
-
const Start = protocol.Start;
|
|
39
|
-
|
|
40
25
|
const Gen12 = rng.Gen12;
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const ActivePokemon = data.ActivePokemon;
|
|
45
|
-
const Boosts = data.Boosts;
|
|
46
|
-
const Effectiveness = data.Effectiveness;
|
|
26
|
+
const Heal = protocol.Heal;
|
|
27
|
+
const ID = common.ID;
|
|
47
28
|
const Item = data.Item;
|
|
48
29
|
const Move = data.Move;
|
|
49
|
-
const
|
|
50
|
-
const
|
|
30
|
+
const Player = common.Player;
|
|
31
|
+
const Result = common.Result;
|
|
32
|
+
const showdown = pkmn.options.showdown;
|
|
51
33
|
const Side = data.Side;
|
|
52
|
-
const
|
|
34
|
+
const Start = protocol.Start;
|
|
53
35
|
const Stats = data.Stats;
|
|
54
36
|
const Status = data.Status;
|
|
55
37
|
const TriAttack = data.TriAttack;
|
|
@@ -111,6 +93,7 @@ pub const State = struct {
|
|
|
111
93
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
112
94
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
113
95
|
move: Move, // wCurPlayerMove
|
|
96
|
+
hit: u3 = 0,
|
|
114
97
|
from: Move = .None,
|
|
115
98
|
mslot: u4, // wCurMoveNum
|
|
116
99
|
first: bool, // wEnemyGoesFirst
|
|
@@ -145,11 +128,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
145
128
|
const p1 = battle.side(.P1);
|
|
146
129
|
const p2 = battle.side(.P2);
|
|
147
130
|
|
|
148
|
-
|
|
131
|
+
const p1_slot = findFirstAlive(p1);
|
|
149
132
|
assert(!showdown or p1_slot == 1);
|
|
150
133
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
151
134
|
|
|
152
|
-
|
|
135
|
+
const p2_slot = findFirstAlive(p2);
|
|
153
136
|
assert(!showdown or p2_slot == 1);
|
|
154
137
|
if (p2_slot == 0) return Result.Win;
|
|
155
138
|
|
|
@@ -261,11 +244,10 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
261
244
|
} else {
|
|
262
245
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
263
246
|
}
|
|
264
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
265
247
|
|
|
266
248
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
267
249
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
268
|
-
try options.log.damage(.{ battle.active(player), incoming,
|
|
250
|
+
try options.log.damage(.{ battle.active(player), incoming, .Spikes });
|
|
269
251
|
}
|
|
270
252
|
}
|
|
271
253
|
|
|
@@ -408,7 +390,9 @@ fn executeMove(
|
|
|
408
390
|
|
|
409
391
|
assert(choice.type == .Move);
|
|
410
392
|
var mslot: u4 = @intCast(choice.data);
|
|
393
|
+
_ = &mslot;
|
|
411
394
|
var move = Move.Pound; // TODO
|
|
395
|
+
_ = &move;
|
|
412
396
|
|
|
413
397
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
414
398
|
|
|
@@ -438,31 +422,22 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
438
422
|
if (volatiles.Recharging) {
|
|
439
423
|
volatiles.Recharging = false;
|
|
440
424
|
resetCant(volatiles);
|
|
441
|
-
try log.cant(.{ ident,
|
|
425
|
+
try log.cant(.{ ident, .Recharge });
|
|
442
426
|
return false;
|
|
443
427
|
}
|
|
444
428
|
|
|
445
429
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
446
430
|
const before = stored.status;
|
|
447
431
|
// Even if the EXT bit is set this will still correctly modify the sleep duration
|
|
448
|
-
// if (options.calc.modify(player, .sleep)) |extend| {
|
|
449
|
-
// if (!extend) stored.status = 0;
|
|
450
|
-
// } else {
|
|
451
432
|
stored.status -= 1;
|
|
452
|
-
// }
|
|
453
|
-
|
|
454
|
-
const duration = Status.duration(stored.status);
|
|
455
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
456
|
-
try options.chance.sleep(player, duration);
|
|
457
|
-
}
|
|
458
433
|
|
|
459
|
-
if (duration == 0) {
|
|
460
|
-
try log.curestatus(.{ ident, before,
|
|
434
|
+
if (Status.duration(stored.status) == 0) {
|
|
435
|
+
try log.curestatus(.{ ident, before, .Message });
|
|
461
436
|
stored.status = 0; // clears EXT if present
|
|
462
437
|
resetCant(volatiles);
|
|
463
438
|
volatiles.Nightmare = false;
|
|
464
439
|
} else {
|
|
465
|
-
try log.cant(.{ ident,
|
|
440
|
+
try log.cant(.{ ident, .Sleep });
|
|
466
441
|
if (move == .Snore or move == .SleepTalk) break :slp;
|
|
467
442
|
resetCant(volatiles);
|
|
468
443
|
return false;
|
|
@@ -471,7 +446,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
471
446
|
|
|
472
447
|
if (Status.is(stored.status, .FRZ) and !(move == .FlameWheel or move == .SacredFire)) {
|
|
473
448
|
resetCant(volatiles);
|
|
474
|
-
try log.cant(.{ ident,
|
|
449
|
+
try log.cant(.{ ident, .Freeze });
|
|
475
450
|
return false;
|
|
476
451
|
}
|
|
477
452
|
|
|
@@ -481,39 +456,28 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
481
456
|
// if (!showdown) volatiles.Flinch = false;
|
|
482
457
|
volatiles.Flinch = false;
|
|
483
458
|
resetCant(volatiles);
|
|
484
|
-
try log.cant(.{ ident,
|
|
459
|
+
try log.cant(.{ ident, .Flinch });
|
|
485
460
|
return false;
|
|
486
461
|
}
|
|
487
462
|
|
|
488
463
|
if (volatiles.disable.duration > 0) {
|
|
489
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
490
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
491
|
-
// } else {
|
|
492
464
|
volatiles.disable.duration -= 1;
|
|
493
|
-
// }
|
|
494
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
495
465
|
|
|
496
466
|
if (volatiles.disable.duration == 0) {
|
|
497
467
|
volatiles.disable.move = 0;
|
|
498
|
-
try log.end(.{ ident,
|
|
468
|
+
try log.end(.{ ident, .Disable });
|
|
499
469
|
}
|
|
500
470
|
}
|
|
501
471
|
|
|
502
472
|
if (volatiles.Confusion) {
|
|
503
473
|
assert(volatiles.confusion > 0);
|
|
504
|
-
|
|
505
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
506
|
-
// if (!extend) volatiles.confusion = 0;
|
|
507
|
-
// } else {
|
|
508
474
|
volatiles.confusion -= 1;
|
|
509
|
-
// }
|
|
510
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
511
475
|
|
|
512
476
|
if (volatiles.confusion == 0) {
|
|
513
477
|
volatiles.Confusion = false;
|
|
514
|
-
try log.end(.{ ident,
|
|
478
|
+
try log.end(.{ ident, .Confusion });
|
|
515
479
|
} else {
|
|
516
|
-
try log.activate(ident, .Confusion);
|
|
480
|
+
try log.activate(.{ ident, .Confusion });
|
|
517
481
|
|
|
518
482
|
if (try Rolls.confused(battle, player, options)) {
|
|
519
483
|
volatiles.BeatUp = false;
|
|
@@ -528,11 +492,11 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
528
492
|
}
|
|
529
493
|
|
|
530
494
|
if (volatiles.Attract) {
|
|
531
|
-
try log.activate(ident, .Attract);
|
|
495
|
+
try log.activate(.{ ident, .Attract });
|
|
532
496
|
|
|
533
497
|
if (try Rolls.attract(battle, player, options)) {
|
|
534
498
|
resetCant(volatiles);
|
|
535
|
-
try log.cant(.{ ident,
|
|
499
|
+
try log.cant(.{ ident, .Attract });
|
|
536
500
|
return false;
|
|
537
501
|
}
|
|
538
502
|
}
|
|
@@ -543,14 +507,14 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
543
507
|
if (m != .None and m == move) {
|
|
544
508
|
side.active.volatiles.Charging = false;
|
|
545
509
|
resetCant(volatiles);
|
|
546
|
-
try options.log.cant(.{ ident,
|
|
510
|
+
try options.log.cant(.{ ident, .Disable, move });
|
|
547
511
|
return false;
|
|
548
512
|
}
|
|
549
513
|
}
|
|
550
514
|
|
|
551
515
|
if (Status.is(stored.status, .PAR) and try Rolls.paralyzed(battle, player, options)) {
|
|
552
516
|
resetCant(volatiles);
|
|
553
|
-
try log.cant(.{ ident,
|
|
517
|
+
try log.cant(.{ ident, .Paralysis });
|
|
554
518
|
return false;
|
|
555
519
|
}
|
|
556
520
|
|
|
@@ -570,7 +534,7 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
570
534
|
|
|
571
535
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
572
536
|
var side = battle.side(player);
|
|
573
|
-
|
|
537
|
+
const volatiles = &side.active.volatiles;
|
|
574
538
|
|
|
575
539
|
// FIXME: pass in target to avoid lookup?
|
|
576
540
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
@@ -668,7 +632,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
668
632
|
}
|
|
669
633
|
assert(stage <= 4);
|
|
670
634
|
|
|
671
|
-
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);
|
|
672
636
|
}
|
|
673
637
|
|
|
674
638
|
pub fn calcDamage(
|
|
@@ -736,7 +700,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
736
700
|
|
|
737
701
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
738
702
|
if (state.damage <= 1) return;
|
|
739
|
-
const random = try Rolls.damage(battle, player, options);
|
|
703
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
740
704
|
options.calc.base(player.foe(), state.damage);
|
|
741
705
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
742
706
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -953,26 +917,26 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
953
917
|
const damage = @max(stored.stats.hp / 8, 1);
|
|
954
918
|
stored.hp -|= damage;
|
|
955
919
|
// As above, Pokémon Showdown uses damage [of] but its not relevant
|
|
956
|
-
try options.log.damage(.{ ident, stored,
|
|
920
|
+
try options.log.damage(.{ ident, stored, .LeechSeed });
|
|
957
921
|
|
|
958
922
|
const before = foe_stored.hp;
|
|
959
923
|
foe_stored.hp = @min(foe_stored.hp + damage, foe_stored.stats.hp);
|
|
960
924
|
// Pokémon Showdown uses the less specific heal here instead of drain... because reasons?
|
|
961
|
-
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored,
|
|
925
|
+
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored, .Silent });
|
|
962
926
|
if (stored.hp == 0) return true;
|
|
963
927
|
}
|
|
964
928
|
|
|
965
929
|
if (volatiles.Nightmare) {
|
|
966
930
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
967
931
|
// ibid
|
|
968
|
-
try options.log.damage(.{ ident, stored,
|
|
932
|
+
try options.log.damage(.{ ident, stored, .Nightmare });
|
|
969
933
|
if (stored.hp == 0) return true;
|
|
970
934
|
}
|
|
971
935
|
|
|
972
936
|
if (volatiles.Curse) {
|
|
973
937
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
974
938
|
// ibid
|
|
975
|
-
try options.log.damage(.{ ident, stored,
|
|
939
|
+
try options.log.damage(.{ ident, stored, .Curse });
|
|
976
940
|
if (stored.hp == 0) return true;
|
|
977
941
|
}
|
|
978
942
|
|
|
@@ -993,7 +957,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
993
957
|
future_sight.count -= 1;
|
|
994
958
|
assert(future_sight.count != 0);
|
|
995
959
|
if (future_sight.count == 1) {
|
|
996
|
-
try options.log.end(.{ battle.active(player),
|
|
960
|
+
try options.log.end(.{ battle.active(player), .FutureSight });
|
|
997
961
|
// TODO doMove
|
|
998
962
|
}
|
|
999
963
|
}
|
|
@@ -1002,7 +966,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1002
966
|
|
|
1003
967
|
// Weather
|
|
1004
968
|
if (battle.field.weather != .None) {
|
|
1005
|
-
assert(battle.field.weather_duration > 0);
|
|
1006
969
|
battle.field.weather_duration -= 1;
|
|
1007
970
|
|
|
1008
971
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1019,7 +982,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1019
982
|
try options.log.damage(.{
|
|
1020
983
|
battle.active(player),
|
|
1021
984
|
side.stored(),
|
|
1022
|
-
|
|
985
|
+
.Sandstorm,
|
|
1023
986
|
});
|
|
1024
987
|
}
|
|
1025
988
|
}
|
|
@@ -1032,20 +995,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1032
995
|
inline for (players) |player| {
|
|
1033
996
|
var side = battle.side(player);
|
|
1034
997
|
var volatiles = &side.active.volatiles;
|
|
998
|
+
const ident = battle.active(player);
|
|
1035
999
|
|
|
1036
1000
|
if (volatiles.bind.duration > 0 and !volatiles.Substitute) {
|
|
1037
1001
|
volatiles.bind.duration -= 1;
|
|
1038
1002
|
if (volatiles.bind.duration == 0) {
|
|
1039
1003
|
const reason = @intFromEnum(End.Bind) + volatiles.bind.reason - 1;
|
|
1040
|
-
try options.log.end(.{
|
|
1004
|
+
try options.log.end(.{ ident, @as(End, @enumFromInt(reason)) });
|
|
1041
1005
|
} else {
|
|
1042
1006
|
var reason = @intFromEnum(Activate.Bind) + volatiles.bind.reason - 1;
|
|
1043
|
-
try options.log.activate(
|
|
1007
|
+
try options.log.activate(.{ ident, @as(Activate, @enumFromInt(reason)) });
|
|
1044
1008
|
|
|
1045
1009
|
reason = @intFromEnum(Damage.Bind) + volatiles.bind.reason - 1;
|
|
1046
1010
|
side.stored().hp -|= @max(side.stored().stats.hp / 16, 1);
|
|
1047
1011
|
try options.log.damage(.{
|
|
1048
|
-
|
|
1012
|
+
ident,
|
|
1049
1013
|
side.stored(),
|
|
1050
1014
|
@as(Damage, @enumFromInt(reason)),
|
|
1051
1015
|
});
|
|
@@ -1060,12 +1024,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1060
1024
|
var volatiles = &side.active.volatiles;
|
|
1061
1025
|
|
|
1062
1026
|
if (volatiles.PerishSong) {
|
|
1063
|
-
assert(volatiles.perish_song > 0);
|
|
1064
1027
|
volatiles.perish_song -= 1;
|
|
1065
1028
|
|
|
1066
1029
|
assert(volatiles.perish_song < 3);
|
|
1067
1030
|
const reason = @intFromEnum(Start.PerishSong0) + volatiles.perish_song;
|
|
1068
|
-
try options.log.start(battle.active(player), @enumFromInt(reason));
|
|
1031
|
+
try options.log.start(.{ battle.active(player), @as(Start, @enumFromInt(reason)) });
|
|
1069
1032
|
|
|
1070
1033
|
if (volatiles.perish_song == 0) {
|
|
1071
1034
|
volatiles.PerishSong = false;
|
|
@@ -1083,14 +1046,14 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1083
1046
|
const before = stored.hp;
|
|
1084
1047
|
stored.hp = @min(stored.hp + @max(stored.stats.hp / 16, 1), stored.stats.hp);
|
|
1085
1048
|
if (stored.hp > before) {
|
|
1086
|
-
try options.log.heal(.{ battle.active(player), stored,
|
|
1049
|
+
try options.log.heal(.{ battle.active(player), stored, .Leftovers });
|
|
1087
1050
|
}
|
|
1088
1051
|
}
|
|
1089
1052
|
}
|
|
1090
1053
|
|
|
1091
1054
|
// Mystery Berry
|
|
1092
1055
|
inline for (players) |player| {
|
|
1093
|
-
|
|
1056
|
+
const stored = battle.side(player).stored();
|
|
1094
1057
|
|
|
1095
1058
|
if (stored.item == .MysteryBerry) {
|
|
1096
1059
|
// TODO stored and active moves, transform, etc
|
|
@@ -1105,8 +1068,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1105
1068
|
!side.active.volatiles.frozen and
|
|
1106
1069
|
try Rolls.defrost(battle, player, options))
|
|
1107
1070
|
{
|
|
1108
|
-
|
|
1109
|
-
try options.log.curestatus(.{ battle.active(player), side.stored().status, reason });
|
|
1071
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1110
1072
|
side.stored().status = 0;
|
|
1111
1073
|
}
|
|
1112
1074
|
}
|
|
@@ -1147,7 +1109,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1147
1109
|
stored.hp < stored.stats.hp;
|
|
1148
1110
|
if (proc) {
|
|
1149
1111
|
assert(stored.hp != 0);
|
|
1150
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1112
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1151
1113
|
stored.item = .None;
|
|
1152
1114
|
|
|
1153
1115
|
const offset = num - @intFromEnum(Item.Berry);
|
|
@@ -1164,7 +1126,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1164
1126
|
|
|
1165
1127
|
if (proc) {
|
|
1166
1128
|
assert(Status.any(stored.status));
|
|
1167
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1129
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1168
1130
|
stored.item = .None;
|
|
1169
1131
|
|
|
1170
1132
|
// FIXME wtf?
|
|
@@ -1172,21 +1134,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1172
1134
|
// side.active.volatiles.Nightmare = false;
|
|
1173
1135
|
if (stored.item == .MiracleBerry) {
|
|
1174
1136
|
side.active.volatiles.Confusion = false;
|
|
1175
|
-
try options.log.end(.{ ident,
|
|
1137
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1176
1138
|
}
|
|
1177
1139
|
stored.status = 0;
|
|
1178
1140
|
|
|
1179
1141
|
// FIXME recalc stats
|
|
1180
1142
|
|
|
1181
|
-
try options.log.curestatus(.{ ident, stored.status,
|
|
1143
|
+
try options.log.curestatus(.{ ident, stored.status, .Message });
|
|
1182
1144
|
}
|
|
1183
1145
|
} else if (num == @intFromEnum(Item.BitterBerry)) {
|
|
1184
1146
|
if (side.active.volatiles.Confusion) {
|
|
1185
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1147
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1186
1148
|
stored.item = .None;
|
|
1187
1149
|
|
|
1188
1150
|
side.active.volatiles.Confusion = false;
|
|
1189
|
-
try options.log.end(.{ ident,
|
|
1151
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1190
1152
|
}
|
|
1191
1153
|
}
|
|
1192
1154
|
}
|
|
@@ -1198,12 +1160,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1198
1160
|
var volatiles = &side.active.volatiles;
|
|
1199
1161
|
|
|
1200
1162
|
if (volatiles.Encore) {
|
|
1201
|
-
assert(volatiles.encore > 0);
|
|
1202
1163
|
volatiles.encore -= 1;
|
|
1203
1164
|
|
|
1204
1165
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
1205
1166
|
volatiles.Encore = false;
|
|
1206
|
-
try options.log.end(.{ battle.active(player),
|
|
1167
|
+
try options.log.end(.{ battle.active(player), .Encore });
|
|
1207
1168
|
}
|
|
1208
1169
|
}
|
|
1209
1170
|
}
|
|
@@ -1220,7 +1181,6 @@ fn updateSideCondition(
|
|
|
1220
1181
|
) !void {
|
|
1221
1182
|
var side = battle.side(player);
|
|
1222
1183
|
if (@field(side.conditions, key)) {
|
|
1223
|
-
assert(@field(side.conditions, value) > 0);
|
|
1224
1184
|
@field(side.conditions, value) -= 1;
|
|
1225
1185
|
|
|
1226
1186
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1233,7 +1193,7 @@ fn updateSideCondition(
|
|
|
1233
1193
|
fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
|
|
1234
1194
|
battle.turn += 1;
|
|
1235
1195
|
|
|
1236
|
-
if (
|
|
1196
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1237
1197
|
try options.log.tie(.{});
|
|
1238
1198
|
return Result.Tie;
|
|
1239
1199
|
} else if (battle.turn >= 65535) {
|
|
@@ -1256,7 +1216,7 @@ pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytyp
|
|
|
1256
1216
|
}
|
|
1257
1217
|
} else {
|
|
1258
1218
|
foe.active.volatiles.rage +|= 1;
|
|
1259
|
-
try options.log.activate(battle.active(player), .Rage);
|
|
1219
|
+
try options.log.activate(.{ battle.active(player), .Rage });
|
|
1260
1220
|
}
|
|
1261
1221
|
}
|
|
1262
1222
|
}
|
|
@@ -1279,7 +1239,7 @@ pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype)
|
|
|
1279
1239
|
|
|
1280
1240
|
if (foe.active.volatiles.DestinyBond) {
|
|
1281
1241
|
battle.side(player).stored().hp = 0;
|
|
1282
|
-
try options.log.activate(battle.active(player.foe()), .DestinyBond);
|
|
1242
|
+
try options.log.activate(.{ battle.active(player.foe()), .DestinyBond });
|
|
1283
1243
|
}
|
|
1284
1244
|
return true;
|
|
1285
1245
|
}
|
|
@@ -1292,14 +1252,14 @@ pub const Effects = struct {
|
|
|
1292
1252
|
const gender: u8 = @intFromEnum(side.stored().dvs.gender);
|
|
1293
1253
|
const foe_gender: u8 = @intFromEnum(foe.stored().dvs.gender);
|
|
1294
1254
|
if (gender + foe_gender != 1) {
|
|
1295
|
-
return options.log.immune(.{ battle.active(player.foe()),
|
|
1255
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1296
1256
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1297
1257
|
try options.log.lastmiss(.{});
|
|
1298
1258
|
return options.log.miss(.{battle.active(player)});
|
|
1299
1259
|
}
|
|
1300
1260
|
|
|
1301
1261
|
foe.active.volatiles.Attract = true;
|
|
1302
|
-
try options.log.start(battle.active(player.foe()), .Attract);
|
|
1262
|
+
try options.log.start(.{ battle.active(player.foe()), .Attract });
|
|
1303
1263
|
}
|
|
1304
1264
|
|
|
1305
1265
|
pub fn batonPass(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1311,14 +1271,32 @@ pub const Effects = struct {
|
|
|
1311
1271
|
}
|
|
1312
1272
|
|
|
1313
1273
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
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
|
+
}
|
|
1322
1300
|
}
|
|
1323
1301
|
|
|
1324
1302
|
pub const bide = struct {
|
|
@@ -1330,18 +1308,11 @@ pub const Effects = struct {
|
|
|
1330
1308
|
|
|
1331
1309
|
if (!volatiles.Bide) return;
|
|
1332
1310
|
|
|
1333
|
-
assert(volatiles.attacks > 0);
|
|
1334
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1335
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1336
|
-
// } else {
|
|
1337
1311
|
volatiles.attacks -= 1;
|
|
1338
|
-
|
|
1339
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1340
|
-
|
|
1341
|
-
if (volatiles.attacks != 0) return options.log.activate(ident, .Bide);
|
|
1312
|
+
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1342
1313
|
|
|
1343
1314
|
volatiles.Bide = false;
|
|
1344
|
-
try options.log.end(.{ ident,
|
|
1315
|
+
try options.log.end(.{ ident, .Bide });
|
|
1345
1316
|
|
|
1346
1317
|
state.damage = volatiles.bide *% 2;
|
|
1347
1318
|
volatiles.bide = 0;
|
|
@@ -1378,7 +1349,7 @@ pub const Effects = struct {
|
|
|
1378
1349
|
try options.log.curestatus(.{
|
|
1379
1350
|
battle.active(player.foe()),
|
|
1380
1351
|
foe_stored.status,
|
|
1381
|
-
|
|
1352
|
+
.Message,
|
|
1382
1353
|
});
|
|
1383
1354
|
foe_stored.status = 0;
|
|
1384
1355
|
}
|
|
@@ -1395,8 +1366,7 @@ pub const Effects = struct {
|
|
|
1395
1366
|
foe_stored.status = Status.init(.BRN);
|
|
1396
1367
|
foe.active.stats.atk = @max(foe.active.stats.atk / 2, 1);
|
|
1397
1368
|
|
|
1398
|
-
|
|
1399
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
|
|
1369
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
1400
1370
|
|
|
1401
1371
|
// TODO: check for status berry
|
|
1402
1372
|
}
|
|
@@ -1416,7 +1386,7 @@ pub const Effects = struct {
|
|
|
1416
1386
|
for (side.active.moves) |m| {
|
|
1417
1387
|
if (convertible(side.active, m.id)) n += 1;
|
|
1418
1388
|
}
|
|
1419
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
1389
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1420
1390
|
|
|
1421
1391
|
side.active.types.type1 = try Rolls.conversion(battle, player, n, options);
|
|
1422
1392
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1426,9 +1396,9 @@ pub const Effects = struct {
|
|
|
1426
1396
|
var side = battle.side(player);
|
|
1427
1397
|
|
|
1428
1398
|
const last = battle.foe(player).lastMove(false);
|
|
1429
|
-
if (last == .None) return options.log.fail(.{ battle.active(player),
|
|
1399
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1430
1400
|
const move = Move.get(last);
|
|
1431
|
-
if (move.type == .@"???") return options.log.fail(.{ battle.active(player),
|
|
1401
|
+
if (move.type == .@"???") return options.log.fail(.{ battle.active(player), .None });
|
|
1432
1402
|
|
|
1433
1403
|
side.active.types.type1 = try Rolls.conversion2(battle, player, move.type, options);
|
|
1434
1404
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1457,7 +1427,8 @@ pub const Effects = struct {
|
|
|
1457
1427
|
if (move.type.special() != (last == .MirrorCoat)) return;
|
|
1458
1428
|
if (state.damage == 0) return;
|
|
1459
1429
|
|
|
1460
|
-
|
|
1430
|
+
// NOTE: llvm/llvm-project#58557
|
|
1431
|
+
state.damage = smul(state.damage, 2);
|
|
1461
1432
|
state.miss = false;
|
|
1462
1433
|
}
|
|
1463
1434
|
|
|
@@ -1473,38 +1444,38 @@ pub const Effects = struct {
|
|
|
1473
1444
|
try options.log.lastmiss(.{});
|
|
1474
1445
|
return options.log.miss(.{battle.active(player)});
|
|
1475
1446
|
} else if (foe.active.volatiles.Substitute) {
|
|
1476
|
-
return options.log.
|
|
1447
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1477
1448
|
} else if (foe.active.volatiles.Curse) {
|
|
1478
|
-
return options.log.fail(.{ foe_ident,
|
|
1449
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1479
1450
|
}
|
|
1480
1451
|
|
|
1481
1452
|
foe.active.volatiles.Curse = true;
|
|
1482
|
-
try options.log.start(foe_ident, .Curse);
|
|
1453
|
+
try options.log.start(.{ foe_ident, .Curse });
|
|
1483
1454
|
|
|
1484
1455
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1485
|
-
try options.log.damage(.{ ident, side.stored(),
|
|
1456
|
+
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1486
1457
|
} else {
|
|
1487
|
-
|
|
1488
|
-
|
|
1458
|
+
const stats = &side.active.stats;
|
|
1459
|
+
const boosts = &side.active.boosts;
|
|
1489
1460
|
|
|
1490
1461
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1491
1462
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
1492
|
-
if (max_atk and max_def) return options.log.fail(.{ ident,
|
|
1463
|
+
if (max_atk and max_def) return options.log.fail(.{ ident, .None });
|
|
1493
1464
|
|
|
1494
1465
|
if (!(boosts.spe == -6 or stats.spe == 1)) {
|
|
1495
1466
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
1496
1467
|
try unboostStat(battle, side, stats, boosts, "spe", 1);
|
|
1497
|
-
try options.log.boost(.{ ident,
|
|
1468
|
+
try options.log.boost(.{ ident, .Speed, @as(i8, -1) });
|
|
1498
1469
|
}
|
|
1499
1470
|
if (!max_atk) {
|
|
1500
1471
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
1501
1472
|
try boostStat(battle, side, stats, boosts, "atk", 1);
|
|
1502
|
-
try options.log.boost(.{ ident,
|
|
1473
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 1) });
|
|
1503
1474
|
}
|
|
1504
1475
|
if (!max_def) {
|
|
1505
1476
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
1506
1477
|
try boostStat(battle, side, stats, boosts, "def", 1);
|
|
1507
|
-
try options.log.boost(.{ ident,
|
|
1478
|
+
try options.log.boost(.{ ident, .Defense, @as(i8, 1) });
|
|
1508
1479
|
}
|
|
1509
1480
|
}
|
|
1510
1481
|
}
|
|
@@ -1516,9 +1487,7 @@ pub const Effects = struct {
|
|
|
1516
1487
|
pub fn defrost(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1517
1488
|
var side = battle.side(player);
|
|
1518
1489
|
assert(Status.is(side.stored().status, .FRZ));
|
|
1519
|
-
|
|
1520
|
-
const reason: CureStatus = .Message;
|
|
1521
|
-
try options.log.curestatus(.{ battle.active(player), side.stored().status, reason });
|
|
1490
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1522
1491
|
side.stored().status = 0;
|
|
1523
1492
|
}
|
|
1524
1493
|
|
|
@@ -1532,14 +1501,10 @@ pub const Effects = struct {
|
|
|
1532
1501
|
var volatiles = &foe.active.volatiles;
|
|
1533
1502
|
const foe_ident = battle.active(player.foe());
|
|
1534
1503
|
|
|
1535
|
-
if (volatiles.disable.move != 0) {
|
|
1536
|
-
return options.log.fail(.{ foe_ident, Fail.None });
|
|
1537
|
-
}
|
|
1504
|
+
if (volatiles.disable.move != 0) return options.log.fail(.{ foe_ident, .None });
|
|
1538
1505
|
|
|
1539
1506
|
const last = battle.foe(player).lastMove(false);
|
|
1540
|
-
if (last == .None or last == .Struggle) {
|
|
1541
|
-
return options.log.fail(.{ foe_ident, Fail.None });
|
|
1542
|
-
}
|
|
1507
|
+
if (last == .None or last == .Struggle) return options.log.fail(.{ foe_ident, .None });
|
|
1543
1508
|
|
|
1544
1509
|
var slot: u3 = 0;
|
|
1545
1510
|
for (foe.active.moves) |m| {
|
|
@@ -1548,25 +1513,28 @@ pub const Effects = struct {
|
|
|
1548
1513
|
if (m.id == last) break;
|
|
1549
1514
|
}
|
|
1550
1515
|
const move = foe.active.move(slot);
|
|
1551
|
-
if (move.pp == 0) return options.log.fail(.{ foe_ident,
|
|
1516
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1552
1517
|
|
|
1553
1518
|
volatiles.disable.move = slot;
|
|
1554
1519
|
volatiles.disable.duration = Rolls.disableDuration(battle, player, options);
|
|
1555
1520
|
|
|
1556
|
-
try options.log.
|
|
1521
|
+
try options.log.start(.{ foe_ident, .Disable, move.id });
|
|
1557
1522
|
}
|
|
1558
1523
|
|
|
1559
1524
|
pub const double = struct {
|
|
1560
1525
|
pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1561
|
-
if (battle.foe(player).active.volatiles.Flying)
|
|
1526
|
+
if (!battle.foe(player).active.volatiles.Flying) return;
|
|
1527
|
+
state.damage = smul(state.damage, 2);
|
|
1562
1528
|
}
|
|
1563
1529
|
|
|
1564
1530
|
pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1565
|
-
if (battle.foe(player).active.volatiles.Underground)
|
|
1531
|
+
if (!battle.foe(player).active.volatiles.Underground) return;
|
|
1532
|
+
state.damage = smul(state.damage, 2);
|
|
1566
1533
|
}
|
|
1567
1534
|
|
|
1568
1535
|
pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1569
|
-
if (battle.foe(player).active.volatiles.minimized)
|
|
1536
|
+
if (!battle.foe(player).active.volatiles.minimized) return;
|
|
1537
|
+
state.damage = smul(state.damage, 2);
|
|
1570
1538
|
}
|
|
1571
1539
|
};
|
|
1572
1540
|
|
|
@@ -1579,7 +1547,7 @@ pub const Effects = struct {
|
|
|
1579
1547
|
if (stored.hp == stored.stats.hp) return;
|
|
1580
1548
|
stored.hp = @min(stored.stats.hp, stored.hp + @max(state.damage / 2, 1));
|
|
1581
1549
|
|
|
1582
|
-
try options.log.heal(.{ ident, stored,
|
|
1550
|
+
try options.log.heal(.{ ident, stored, .Drain, battle.active(player.foe()) });
|
|
1583
1551
|
}
|
|
1584
1552
|
|
|
1585
1553
|
pub fn encore(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1591,7 +1559,7 @@ pub const Effects = struct {
|
|
|
1591
1559
|
const failed = volatiles.Encore or
|
|
1592
1560
|
(last == .None or last == .Struggle or
|
|
1593
1561
|
last == .Encore or .last == .MirrorMove);
|
|
1594
|
-
if (failed) return options.log.fail(.{ foe_ident,
|
|
1562
|
+
if (failed) return options.log.fail(.{ foe_ident, .None });
|
|
1595
1563
|
|
|
1596
1564
|
var slot: u3 = 0;
|
|
1597
1565
|
for (foe.active.moves) |m| {
|
|
@@ -1600,7 +1568,7 @@ pub const Effects = struct {
|
|
|
1600
1568
|
if (m.id == last) break;
|
|
1601
1569
|
}
|
|
1602
1570
|
const move = foe.active.move(slot);
|
|
1603
|
-
if (move.pp == 0) return options.log.fail(.{ foe_ident,
|
|
1571
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1604
1572
|
|
|
1605
1573
|
volatiles.Encore = true;
|
|
1606
1574
|
volatiles.encore = Rolls.encoreDuration(battle, player, options);
|
|
@@ -1608,11 +1576,19 @@ pub const Effects = struct {
|
|
|
1608
1576
|
// TODO ???
|
|
1609
1577
|
_ = state;
|
|
1610
1578
|
|
|
1611
|
-
try options.log.
|
|
1579
|
+
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1612
1580
|
}
|
|
1613
1581
|
|
|
1614
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1615
|
-
|
|
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;
|
|
1616
1592
|
}
|
|
1617
1593
|
|
|
1618
1594
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1662,12 +1638,11 @@ pub const Effects = struct {
|
|
|
1662
1638
|
var side = battle.side(player);
|
|
1663
1639
|
|
|
1664
1640
|
if (side.active.volatiles.FocusEnergy) {
|
|
1665
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
1666
|
-
return;
|
|
1641
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
1667
1642
|
}
|
|
1668
1643
|
|
|
1669
1644
|
side.active.volatiles.FocusEnergy = true;
|
|
1670
|
-
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1645
|
+
try options.log.start(.{ battle.active(player), .FocusEnergy });
|
|
1671
1646
|
}
|
|
1672
1647
|
|
|
1673
1648
|
var SLOTS: [5]u4 = .{ 0, 0, 0, 0, 0 };
|
|
@@ -1677,7 +1652,7 @@ pub const Effects = struct {
|
|
|
1677
1652
|
|
|
1678
1653
|
const foe = battle.foe(player);
|
|
1679
1654
|
|
|
1680
|
-
if (state.first) return options.log.fail(.{ battle.active(player),
|
|
1655
|
+
if (state.first) return options.log.fail(.{ battle.active(player), .None });
|
|
1681
1656
|
|
|
1682
1657
|
var n: u4 = 0;
|
|
1683
1658
|
var i: u4 = 0;
|
|
@@ -1691,7 +1666,7 @@ pub const Effects = struct {
|
|
|
1691
1666
|
}
|
|
1692
1667
|
}
|
|
1693
1668
|
|
|
1694
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
1669
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1695
1670
|
|
|
1696
1671
|
const slot = try Rolls.forceSwitch(battle, player, SLOTS[0..i], n, options);
|
|
1697
1672
|
try switchIn(battle, player.foe(), slot, .Drag, options);
|
|
@@ -1704,11 +1679,11 @@ pub const Effects = struct {
|
|
|
1704
1679
|
try options.log.lastmiss(.{});
|
|
1705
1680
|
return options.log.miss(.{battle.active(player)});
|
|
1706
1681
|
} else if (foe.active.volatiles.Foresight) {
|
|
1707
|
-
return options.log.fail(.{ battle.active(player),
|
|
1682
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
1708
1683
|
}
|
|
1709
1684
|
|
|
1710
1685
|
foe.active.volatiles.Foresight = true;
|
|
1711
|
-
try options.log.start(battle.active(player.foe()), .FocusEnergy);
|
|
1686
|
+
try options.log.start(.{ battle.active(player.foe()), .FocusEnergy });
|
|
1712
1687
|
}
|
|
1713
1688
|
|
|
1714
1689
|
pub fn freezeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1728,10 +1703,10 @@ pub const Effects = struct {
|
|
|
1728
1703
|
if (!state.proc) return;
|
|
1729
1704
|
if (foe.conditions.Safeguard) return;
|
|
1730
1705
|
// Freeze Clause Mod
|
|
1731
|
-
if (
|
|
1706
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1732
1707
|
|
|
1733
1708
|
foe_stored.status = Status.init(.FRZ);
|
|
1734
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
1709
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
1735
1710
|
|
|
1736
1711
|
// TODO: check for status berry
|
|
1737
1712
|
|
|
@@ -1739,8 +1714,26 @@ pub const Effects = struct {
|
|
|
1739
1714
|
foe.active.volatiles.frozen = true;
|
|
1740
1715
|
}
|
|
1741
1716
|
|
|
1742
|
-
|
|
1743
|
-
|
|
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
|
+
}
|
|
1744
1737
|
}
|
|
1745
1738
|
|
|
1746
1739
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1774,7 +1767,7 @@ pub const Effects = struct {
|
|
|
1774
1767
|
var stored = side.stored();
|
|
1775
1768
|
const ident = battle.active(player);
|
|
1776
1769
|
|
|
1777
|
-
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident,
|
|
1770
|
+
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident, .None });
|
|
1778
1771
|
|
|
1779
1772
|
const rest = state.move == .Rest;
|
|
1780
1773
|
if (rest) {
|
|
@@ -1783,7 +1776,7 @@ pub const Effects = struct {
|
|
|
1783
1776
|
side.active.volatiles.Toxic = false;
|
|
1784
1777
|
stored.status = Status.slf(2);
|
|
1785
1778
|
recomputeStats(battle, side);
|
|
1786
|
-
try options.log.status(.{ ident, stored.status,
|
|
1779
|
+
try options.log.status(.{ ident, stored.status, .From, Move.Rest });
|
|
1787
1780
|
stored.hp = stored.stats.hp;
|
|
1788
1781
|
} else {
|
|
1789
1782
|
stored.hp = @min(stored.stats.hp, stored.hp + (stored.stats.hp / 2));
|
|
@@ -1821,7 +1814,7 @@ pub const Effects = struct {
|
|
|
1821
1814
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
1822
1815
|
const invulnerable = foe.active.volatiles.Underground or foe.active.volatiles.Flying;
|
|
1823
1816
|
if (!invulnerable and foe.active.types.includes(.Grass)) {
|
|
1824
|
-
return options.log.immune(.{ battle.active(player.foe()),
|
|
1817
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1825
1818
|
}
|
|
1826
1819
|
try checkHit(battle, player, state, options);
|
|
1827
1820
|
if (state.miss) {
|
|
@@ -1841,7 +1834,7 @@ pub const Effects = struct {
|
|
|
1841
1834
|
|
|
1842
1835
|
foe.active.volatiles.LeechSeed = true;
|
|
1843
1836
|
|
|
1844
|
-
try options.log.start(battle.active(player.foe()), .LeechSeed);
|
|
1837
|
+
try options.log.start(.{ battle.active(player.foe()), .LeechSeed });
|
|
1845
1838
|
}
|
|
1846
1839
|
|
|
1847
1840
|
pub fn lockOn(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1849,20 +1842,20 @@ pub const Effects = struct {
|
|
|
1849
1842
|
const foe_ident = battle.active(player.foe());
|
|
1850
1843
|
|
|
1851
1844
|
if (foe.active.volatiles.Substitute) {
|
|
1852
|
-
return options.log.
|
|
1845
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1853
1846
|
} else if (state.miss) {
|
|
1854
|
-
return options.log.fail(.{ foe_ident,
|
|
1847
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1855
1848
|
}
|
|
1856
1849
|
|
|
1857
1850
|
foe.active.volatiles.LockOn = true;
|
|
1858
|
-
try options.log.activate(battle.active(player), .LockOn);
|
|
1851
|
+
try options.log.activate(.{ battle.active(player), .LockOn });
|
|
1859
1852
|
}
|
|
1860
1853
|
|
|
1861
1854
|
const MAGNITUDE_POWER = [_]u8{ 10, 30, 50, 70, 90, 110, 150 };
|
|
1862
1855
|
|
|
1863
1856
|
pub fn magnitude(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1864
1857
|
const num = try Rolls.magnitude(battle, player, options);
|
|
1865
|
-
try options.log.
|
|
1858
|
+
try options.log.activate(.{ battle.active(player), .Magnitude, num });
|
|
1866
1859
|
state.bp = MAGNITUDE_POWER[num - 4];
|
|
1867
1860
|
}
|
|
1868
1861
|
|
|
@@ -1870,11 +1863,11 @@ pub const Effects = struct {
|
|
|
1870
1863
|
var foe = battle.foe(player);
|
|
1871
1864
|
|
|
1872
1865
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1873
|
-
if (!showdown) return options.log.fail(.{ battle.active(player.foe()),
|
|
1866
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1874
1867
|
try options.log.lastmiss(.{});
|
|
1875
1868
|
return options.log.miss(.{battle.active(player)});
|
|
1876
1869
|
} else if (foe.active.volatiles.Trapped) {
|
|
1877
|
-
return options.log.fail(.{ battle.active(player.foe()),
|
|
1870
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1878
1871
|
}
|
|
1879
1872
|
|
|
1880
1873
|
foe.active.volatiles.Trapped = true;
|
|
@@ -1901,7 +1894,29 @@ pub const Effects = struct {
|
|
|
1901
1894
|
}
|
|
1902
1895
|
|
|
1903
1896
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1904
|
-
|
|
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 });
|
|
1905
1920
|
}
|
|
1906
1921
|
|
|
1907
1922
|
pub fn mirrorMove(
|
|
@@ -1918,11 +1933,11 @@ pub const Effects = struct {
|
|
|
1918
1933
|
|
|
1919
1934
|
const last = foe.lastMove(false);
|
|
1920
1935
|
|
|
1921
|
-
if (last == .None) return options.log.fail(.{ battle.active(player),
|
|
1936
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1922
1937
|
const knows = knows: for (side.active.moves) |m| {
|
|
1923
1938
|
if (last == m.id) break :knows true;
|
|
1924
1939
|
} else false;
|
|
1925
|
-
if (knows) return options.log.fail(.{ battle.active(player),
|
|
1940
|
+
if (knows) return options.log.fail(.{ battle.active(player), .None });
|
|
1926
1941
|
|
|
1927
1942
|
// if (wPlayerCharging) ???
|
|
1928
1943
|
// wPlayerCharging = 1
|
|
@@ -1936,19 +1951,17 @@ pub const Effects = struct {
|
|
|
1936
1951
|
pub fn mist(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1937
1952
|
var side = battle.side(player);
|
|
1938
1953
|
|
|
1939
|
-
if (side.active.volatiles.Mist) {
|
|
1940
|
-
return options.log.fail(.{ battle.active(player), Fail.None });
|
|
1941
|
-
}
|
|
1954
|
+
if (side.active.volatiles.Mist) return options.log.fail(.{ battle.active(player), .None });
|
|
1942
1955
|
side.active.volatiles.Mist = true;
|
|
1943
1956
|
|
|
1944
|
-
try options.log.start(battle.active(player), .Mist);
|
|
1957
|
+
try options.log.start(.{ battle.active(player), .Mist });
|
|
1945
1958
|
}
|
|
1946
1959
|
|
|
1947
1960
|
pub fn weatherHeal(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1948
1961
|
var stored = battle.side(player).stored();
|
|
1949
1962
|
const ident = battle.active(player);
|
|
1950
1963
|
|
|
1951
|
-
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident,
|
|
1964
|
+
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident, .None });
|
|
1952
1965
|
|
|
1953
1966
|
stored.hp = switch (battle.field.weather) {
|
|
1954
1967
|
.Sun => stored.stats.hp,
|
|
@@ -1956,7 +1969,7 @@ pub const Effects = struct {
|
|
|
1956
1969
|
else => @min(stored.stats.hp, stored.hp + (stored.stats.hp / 4)),
|
|
1957
1970
|
};
|
|
1958
1971
|
|
|
1959
|
-
try options.log.heal(.{ ident, stored,
|
|
1972
|
+
try options.log.heal(.{ ident, stored, .None });
|
|
1960
1973
|
}
|
|
1961
1974
|
|
|
1962
1975
|
pub fn multiHit(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1969,17 +1982,17 @@ pub const Effects = struct {
|
|
|
1969
1982
|
const foe_ident = battle.active(player.foe());
|
|
1970
1983
|
|
|
1971
1984
|
if (foe_volatiles.Flying or foe_volatiles.Underground) {
|
|
1972
|
-
if (!showdown) return options.log.fail(.{ battle.active(player.foe()),
|
|
1985
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1973
1986
|
try options.log.lastmiss(.{});
|
|
1974
1987
|
return options.log.miss(.{battle.active(player)});
|
|
1975
1988
|
} else if (foe_volatiles.Substitute) {
|
|
1976
|
-
return options.log.
|
|
1989
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1977
1990
|
} else if (!Status.is(foe.stored().status, .SLP) or foe_volatiles.Nightmare) {
|
|
1978
|
-
return options.log.fail(.{ foe_ident,
|
|
1991
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1979
1992
|
}
|
|
1980
1993
|
|
|
1981
1994
|
foe_volatiles.Nightmare = true;
|
|
1982
|
-
try options.log.start(battle.active(player.foe()), .Nightmare);
|
|
1995
|
+
try options.log.start(.{ battle.active(player.foe()), .Nightmare });
|
|
1983
1996
|
}
|
|
1984
1997
|
|
|
1985
1998
|
pub const ohko = struct {
|
|
@@ -2003,9 +2016,9 @@ pub const Effects = struct {
|
|
|
2003
2016
|
const foe_ident = battle.active(player.foe());
|
|
2004
2017
|
|
|
2005
2018
|
state.damage = 0;
|
|
2006
|
-
if (state.immune()) return options.log.immune(.{ foe_ident,
|
|
2019
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
2007
2020
|
const delta = side.stored().level -| foe.stored().level;
|
|
2008
|
-
if (delta == 0) return options.log.immune(.{ foe_ident,
|
|
2021
|
+
if (delta == 0) return options.log.immune(.{ foe_ident, .OHKO });
|
|
2009
2022
|
|
|
2010
2023
|
state.accuracy = 2 * delta +| Gen12.percent(30);
|
|
2011
2024
|
try checkHit(battle, player, state, options);
|
|
@@ -2021,17 +2034,17 @@ pub const Effects = struct {
|
|
|
2021
2034
|
const ident = battle.active(player);
|
|
2022
2035
|
const foe_ident = battle.active(player.foe());
|
|
2023
2036
|
|
|
2024
|
-
if (state.miss) return options.log.fail(.{ foe_ident,
|
|
2037
|
+
if (state.miss) return options.log.fail(.{ foe_ident, .None });
|
|
2025
2038
|
if (foe.active.volatiles.Substitute) {
|
|
2026
|
-
return options.log.
|
|
2039
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2027
2040
|
}
|
|
2028
2041
|
|
|
2029
2042
|
const avg = @max((foe.stored().hp + side.stored().hp) / 2, 1);
|
|
2030
2043
|
foe.stored().hp -= avg;
|
|
2031
2044
|
side.stored().hp = avg;
|
|
2032
2045
|
|
|
2033
|
-
try options.log.sethp(.{ foe_ident, foe.stored(),
|
|
2034
|
-
try options.log.sethp(.{ ident, side.stored(),
|
|
2046
|
+
try options.log.sethp(.{ foe_ident, foe.stored(), .Silent });
|
|
2047
|
+
try options.log.sethp(.{ ident, side.stored(), .None });
|
|
2035
2048
|
}
|
|
2036
2049
|
|
|
2037
2050
|
pub fn paralyze(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2039,21 +2052,21 @@ pub const Effects = struct {
|
|
|
2039
2052
|
const foe_ident = battle.active(player.foe());
|
|
2040
2053
|
|
|
2041
2054
|
if (Status.is(foe.stored().status, .PAR)) {
|
|
2042
|
-
return options.log.fail(.{ foe_ident,
|
|
2055
|
+
return options.log.fail(.{ foe_ident, .Paralysis });
|
|
2043
2056
|
}
|
|
2044
|
-
if (state.immune()) return options.log.immune(.{ foe_ident,
|
|
2045
|
-
if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident,
|
|
2057
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
2058
|
+
if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident, .None });
|
|
2046
2059
|
if (state.miss) {
|
|
2047
2060
|
try options.log.lastmiss(.{});
|
|
2048
2061
|
return options.log.miss(.{battle.active(player)});
|
|
2049
2062
|
} else if (foe.active.volatiles.Substitute) {
|
|
2050
|
-
return options.log.
|
|
2063
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2051
2064
|
}
|
|
2052
2065
|
|
|
2053
2066
|
foe.stored().status = Status.init(.PAR);
|
|
2054
2067
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2055
2068
|
|
|
2056
|
-
try options.log.status(.{ foe_ident, foe.stored().status,
|
|
2069
|
+
try options.log.status(.{ foe_ident, foe.stored().status, .None });
|
|
2057
2070
|
}
|
|
2058
2071
|
|
|
2059
2072
|
pub fn paralyzeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2069,8 +2082,7 @@ pub const Effects = struct {
|
|
|
2069
2082
|
foe_stored.status = Status.init(.PAR);
|
|
2070
2083
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2071
2084
|
|
|
2072
|
-
|
|
2073
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
|
|
2085
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2074
2086
|
|
|
2075
2087
|
// TODO: check for status berry
|
|
2076
2088
|
}
|
|
@@ -2086,15 +2098,15 @@ pub const Effects = struct {
|
|
|
2086
2098
|
var foe = battle.foe(player);
|
|
2087
2099
|
|
|
2088
2100
|
if (foe.active.volatiles.PerishSong) {
|
|
2089
|
-
return options.log.fail(.{ battle.active(player.foe()),
|
|
2101
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2090
2102
|
} else if (!side.active.volatiles.PerishSong) {
|
|
2091
2103
|
side.active.volatiles.PerishSong = true;
|
|
2092
2104
|
side.active.volatiles.perish_song = 4;
|
|
2093
|
-
try options.log.start(battle.active(player), .PerishSong3Silent);
|
|
2105
|
+
try options.log.start(.{ battle.active(player), .PerishSong3Silent });
|
|
2094
2106
|
}
|
|
2095
2107
|
foe.active.volatiles.PerishSong = true;
|
|
2096
2108
|
foe.active.volatiles.perish_song = 4;
|
|
2097
|
-
try options.log.start(battle.active(player.foe()), .PerishSong3Silent);
|
|
2109
|
+
try options.log.start(.{ battle.active(player.foe()), .PerishSong3Silent });
|
|
2098
2110
|
|
|
2099
2111
|
try options.log.fieldactivate(.{}); // FIXME .PerishSong
|
|
2100
2112
|
}
|
|
@@ -2106,7 +2118,7 @@ pub const Effects = struct {
|
|
|
2106
2118
|
|
|
2107
2119
|
const toxic = state.move == .Toxic;
|
|
2108
2120
|
if (state.immune() or foe.active.types.includes(.Poison)) {
|
|
2109
|
-
return options.log.immune(.{ foe_ident,
|
|
2121
|
+
return options.log.immune(.{ foe_ident, .None });
|
|
2110
2122
|
} else if (Status.any(foe_stored.status)) {
|
|
2111
2123
|
// Pokémon Showdown considers Toxic to be a status even in Generation II and so
|
|
2112
2124
|
// will not include a fail reason for Toxic vs. Poison or vice-versa...
|
|
@@ -2120,7 +2132,7 @@ pub const Effects = struct {
|
|
|
2120
2132
|
else
|
|
2121
2133
|
Fail.None });
|
|
2122
2134
|
} else if (foe.active.volatiles.Substitute) {
|
|
2123
|
-
return options.log.
|
|
2135
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2124
2136
|
} else if (state.miss) {
|
|
2125
2137
|
try options.log.lastmiss(.{});
|
|
2126
2138
|
return options.log.miss(.{battle.active(player)});
|
|
@@ -2133,7 +2145,7 @@ pub const Effects = struct {
|
|
|
2133
2145
|
foe.active.volatiles.toxic = 0;
|
|
2134
2146
|
}
|
|
2135
2147
|
|
|
2136
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
2148
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2137
2149
|
|
|
2138
2150
|
// TODO: check for status berry
|
|
2139
2151
|
}
|
|
@@ -2149,8 +2161,8 @@ pub const Effects = struct {
|
|
|
2149
2161
|
if (foe.conditions.Safeguard) return;
|
|
2150
2162
|
|
|
2151
2163
|
foe_stored.status = Status.init(.PSN);
|
|
2152
|
-
|
|
2153
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status,
|
|
2164
|
+
|
|
2165
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2154
2166
|
|
|
2155
2167
|
// TODO: check for status berry
|
|
2156
2168
|
}
|
|
@@ -2165,9 +2177,7 @@ pub const Effects = struct {
|
|
|
2165
2177
|
|
|
2166
2178
|
const failed = !state.first or volatiles.Substitute or
|
|
2167
2179
|
!try Rolls.protect(battle, player, volatiles.protect, options);
|
|
2168
|
-
if (failed) {
|
|
2169
|
-
return options.log.fail(.{ battle.active(player), Fail.None });
|
|
2170
|
-
}
|
|
2180
|
+
if (failed) return options.log.fail(.{ battle.active(player), .None });
|
|
2171
2181
|
|
|
2172
2182
|
volatiles.protect = @max(8, volatiles.protect + 1);
|
|
2173
2183
|
if (state.move == .Endure) {
|
|
@@ -2184,7 +2194,7 @@ pub const Effects = struct {
|
|
|
2184
2194
|
|
|
2185
2195
|
// Pokémon Showdown should fail when the enemy has no boosts/unboosts but doesn't
|
|
2186
2196
|
if (!showdown and @as(u32, @bitCast(foe.active.boosts)) == 0) {
|
|
2187
|
-
return options.log.fail(.{ battle.active(player),
|
|
2197
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2188
2198
|
}
|
|
2189
2199
|
|
|
2190
2200
|
side.active.boosts = foe.active.boosts;
|
|
@@ -2193,7 +2203,7 @@ pub const Effects = struct {
|
|
|
2193
2203
|
|
|
2194
2204
|
pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2195
2205
|
// TODO: remove switching variable and just look at Choice saved in Battle struct?
|
|
2196
|
-
if (battle.foe(player).active.volatiles.switching) state.damage
|
|
2206
|
+
if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
|
|
2197
2207
|
}
|
|
2198
2208
|
|
|
2199
2209
|
pub const rage = struct {
|
|
@@ -2203,11 +2213,10 @@ pub const Effects = struct {
|
|
|
2203
2213
|
}
|
|
2204
2214
|
|
|
2205
2215
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2206
|
-
|
|
2207
|
-
var volatiles = &side.active.volatiles;
|
|
2216
|
+
const side = battle.side(player);
|
|
2208
2217
|
|
|
2209
|
-
assert(volatiles.Rage);
|
|
2210
|
-
state.damage
|
|
2218
|
+
assert(side.active.volatiles.Rage);
|
|
2219
|
+
state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
|
|
2211
2220
|
}
|
|
2212
2221
|
};
|
|
2213
2222
|
|
|
@@ -2219,7 +2228,7 @@ pub const Effects = struct {
|
|
|
2219
2228
|
var side = battle.side(player);
|
|
2220
2229
|
|
|
2221
2230
|
if (side.active.volatiles.LeechSeed) {
|
|
2222
|
-
try options.log.end(.{ battle.active(player),
|
|
2231
|
+
try options.log.end(.{ battle.active(player), .LeechSeedFrom });
|
|
2223
2232
|
side.active.volatiles.LeechSeed = false;
|
|
2224
2233
|
}
|
|
2225
2234
|
|
|
@@ -2238,18 +2247,14 @@ pub const Effects = struct {
|
|
|
2238
2247
|
pub fn recoil(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2239
2248
|
var side = battle.side(player);
|
|
2240
2249
|
var stored = side.stored();
|
|
2250
|
+
const ident = battle.active(player);
|
|
2241
2251
|
|
|
2242
2252
|
assert(state.damage > 0);
|
|
2243
2253
|
|
|
2244
2254
|
const damage: i16 = @intCast(@max(state.damage / 4, 1));
|
|
2245
2255
|
stored.hp = @intCast(@max(@as(i16, @intCast(stored.hp)) - damage, 0));
|
|
2246
2256
|
|
|
2247
|
-
try options.log.damage(.{
|
|
2248
|
-
battle.active(player),
|
|
2249
|
-
stored,
|
|
2250
|
-
Damage.RecoilOf,
|
|
2251
|
-
battle.active(player.foe()),
|
|
2252
|
-
});
|
|
2257
|
+
try options.log.damage(.{ ident, stored, .RecoilOf, battle.active(player.foe()) });
|
|
2253
2258
|
}
|
|
2254
2259
|
|
|
2255
2260
|
pub fn reversal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2306,13 +2311,13 @@ pub const Effects = struct {
|
|
|
2306
2311
|
side.active.volatiles.attacks += 1;
|
|
2307
2312
|
side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
|
|
2308
2313
|
|
|
2309
|
-
if (side.active.volatiles.DefenseCurl) state.damage
|
|
2314
|
+
if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
|
|
2310
2315
|
}
|
|
2311
2316
|
};
|
|
2312
2317
|
|
|
2313
2318
|
pub fn sandstorm(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2314
2319
|
if (battle.field.weather != .Sandstorm) return weather(battle, .Sandstorm, options);
|
|
2315
|
-
return options.log.fail(.{ battle.active(player),
|
|
2320
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2316
2321
|
}
|
|
2317
2322
|
|
|
2318
2323
|
pub fn safeguard(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2337,7 +2342,7 @@ pub const Effects = struct {
|
|
|
2337
2342
|
var side = battle.side(player);
|
|
2338
2343
|
|
|
2339
2344
|
if (@field(side.conditions, key)) {
|
|
2340
|
-
return options.log.fail(.{ battle.active(player),
|
|
2345
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2341
2346
|
}
|
|
2342
2347
|
|
|
2343
2348
|
@field(side.conditions, key) = true;
|
|
@@ -2353,9 +2358,9 @@ pub const Effects = struct {
|
|
|
2353
2358
|
|
|
2354
2359
|
if (showdown) {
|
|
2355
2360
|
// yes, actually "Splash". '|nothing' gets blindly mapped to '|-activate||move:Splash'
|
|
2356
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2361
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2357
2362
|
} else {
|
|
2358
|
-
try options.log.fail(.{ battle.active(player),
|
|
2363
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2359
2364
|
}
|
|
2360
2365
|
}
|
|
2361
2366
|
|
|
@@ -2372,33 +2377,32 @@ pub const Effects = struct {
|
|
|
2372
2377
|
}
|
|
2373
2378
|
|
|
2374
2379
|
if (foe.active.volatiles.Substitute) {
|
|
2375
|
-
return options.log.
|
|
2380
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2376
2381
|
}
|
|
2377
2382
|
|
|
2378
2383
|
// Sleep Clause Mod
|
|
2379
|
-
if (
|
|
2384
|
+
if (pkmn.options.mod) {
|
|
2380
2385
|
for (foe.pokemon) |p| {
|
|
2381
2386
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2382
2387
|
}
|
|
2383
2388
|
}
|
|
2384
2389
|
|
|
2385
2390
|
foe_stored.status = Status.slp(Rolls.sleepDuration(battle, player, options));
|
|
2386
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
2391
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .From, state.move });
|
|
2387
2392
|
}
|
|
2388
2393
|
|
|
2389
2394
|
pub fn sleepTalk(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2390
2395
|
const side = battle.side(player);
|
|
2391
2396
|
|
|
2392
2397
|
if (!Status.is(side.stored().status, .SLP)) {
|
|
2393
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
2394
|
-
return;
|
|
2398
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2395
2399
|
}
|
|
2396
2400
|
|
|
2397
2401
|
var n: u3 = 0;
|
|
2398
2402
|
for (side.active.moves, 0..) |m, i| {
|
|
2399
2403
|
if (state.mslot - 1 != i and Move.get(m.id).extra.sleep_talk) n += 1;
|
|
2400
2404
|
}
|
|
2401
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
2405
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
2402
2406
|
|
|
2403
2407
|
_ = try Rolls.sleepTalk(battle, player, n, state.mslot, options); // TODO
|
|
2404
2408
|
}
|
|
@@ -2407,55 +2411,75 @@ pub const Effects = struct {
|
|
|
2407
2411
|
if (Status.is(battle.side(player).stored().status, .SLP)) return true;
|
|
2408
2412
|
state.damage = 0;
|
|
2409
2413
|
state.miss = true; // TODO is this necessary?
|
|
2410
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
2414
|
+
if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2411
2415
|
return false;
|
|
2412
2416
|
}
|
|
2413
2417
|
|
|
2414
2418
|
pub fn spikes(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2415
2419
|
var foe = battle.foe(player);
|
|
2416
|
-
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player),
|
|
2420
|
+
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player), .None });
|
|
2417
2421
|
|
|
2418
2422
|
foe.conditions.Spikes = true;
|
|
2419
2423
|
try options.log.sidestart(.{ player.foe(), protocol.Side.Spikes });
|
|
2420
2424
|
}
|
|
2421
2425
|
|
|
2422
2426
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2423
|
-
|
|
2424
|
-
// var foe = battle.foe(player);
|
|
2427
|
+
var foe = battle.foe(player);
|
|
2425
2428
|
|
|
2426
|
-
|
|
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
|
+
}
|
|
2427
2433
|
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
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
|
+
}
|
|
2442
|
+
|
|
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);
|
|
2452
|
+
|
|
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 });
|
|
2434
2458
|
}
|
|
2435
2459
|
|
|
2436
2460
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2437
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2461
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2438
2462
|
}
|
|
2439
2463
|
|
|
2440
2464
|
pub fn substitute(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2441
2465
|
var side = battle.side(player);
|
|
2442
2466
|
if (side.active.volatiles.Substitute) {
|
|
2443
|
-
return options.log.fail(.{ battle.active(player),
|
|
2467
|
+
return options.log.fail(.{ battle.active(player), .Substitute });
|
|
2444
2468
|
}
|
|
2445
2469
|
|
|
2446
2470
|
assert(side.stored().stats.hp <= 1023);
|
|
2447
2471
|
const hp: u8 = @intCast(side.stored().stats.hp / 4);
|
|
2448
2472
|
|
|
2449
2473
|
if (hp == 0 or side.stored().hp <= hp) {
|
|
2450
|
-
return options.log.fail(.{ battle.active(player),
|
|
2474
|
+
return options.log.fail(.{ battle.active(player), .Weak });
|
|
2451
2475
|
}
|
|
2452
2476
|
|
|
2453
2477
|
side.stored().hp -= hp;
|
|
2454
2478
|
side.active.volatiles.substitute = hp;
|
|
2455
2479
|
side.active.volatiles.Substitute = true;
|
|
2456
2480
|
side.active.volatiles.bind = .{};
|
|
2457
|
-
try options.log.start(battle.active(player), .Substitute);
|
|
2458
|
-
try options.log.damage(.{ battle.active(player), side.stored(),
|
|
2481
|
+
try options.log.start(.{ battle.active(player), .Substitute });
|
|
2482
|
+
try options.log.damage(.{ battle.active(player), side.stored(), .None });
|
|
2459
2483
|
}
|
|
2460
2484
|
|
|
2461
2485
|
pub fn sunnyDay(battle: anytype, _: Player, _: *State, options: anytype) !void {
|
|
@@ -2471,7 +2495,7 @@ pub const Effects = struct {
|
|
|
2471
2495
|
pub fn teleport(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2472
2496
|
if (showdown) return;
|
|
2473
2497
|
|
|
2474
|
-
try options.log.fail(.{ battle.active(player),
|
|
2498
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2475
2499
|
try options.log.laststill(.{});
|
|
2476
2500
|
}
|
|
2477
2501
|
|
|
@@ -2513,9 +2537,9 @@ pub const Effects = struct {
|
|
|
2513
2537
|
side.active.volatiles.dirty = false;
|
|
2514
2538
|
|
|
2515
2539
|
if (foe.active.volatiles.Transform) {
|
|
2516
|
-
return options.log.fail(.{ battle.active(player),
|
|
2540
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2517
2541
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2518
|
-
if (!showdown) return options.log.fail(.{ battle.active(player),
|
|
2542
|
+
if (!showdown) return options.log.fail(.{ battle.active(player), .None });
|
|
2519
2543
|
try options.log.lastmiss(.{});
|
|
2520
2544
|
return options.log.miss(.{battle.active(player)});
|
|
2521
2545
|
}
|
|
@@ -2564,8 +2588,8 @@ pub const Effects = struct {
|
|
|
2564
2588
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2565
2589
|
var side = battle.side(player);
|
|
2566
2590
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2591
|
+
const stats = &side.active.stats;
|
|
2592
|
+
const boosts = &side.active.boosts;
|
|
2569
2593
|
|
|
2570
2594
|
if (!state.proc) return;
|
|
2571
2595
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
@@ -2584,9 +2608,9 @@ pub const Effects = struct {
|
|
|
2584
2608
|
var side = battle.side(player);
|
|
2585
2609
|
const ident = battle.active(player);
|
|
2586
2610
|
|
|
2587
|
-
if (state.miss) return log.fail(.{ ident,
|
|
2611
|
+
if (state.miss) return log.fail(.{ ident, .None });
|
|
2588
2612
|
|
|
2589
|
-
|
|
2613
|
+
const stats = &side.active.stats;
|
|
2590
2614
|
var boosts = &side.active.boosts;
|
|
2591
2615
|
|
|
2592
2616
|
const move = Move.get(state.move);
|
|
@@ -2594,7 +2618,7 @@ pub const Effects = struct {
|
|
|
2594
2618
|
.AttackUp1, .AttackUp2, .Rage, .Swagger => {
|
|
2595
2619
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2596
2620
|
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE) {
|
|
2597
|
-
return log.fail(.{ ident,
|
|
2621
|
+
return log.fail(.{ ident, .None });
|
|
2598
2622
|
}
|
|
2599
2623
|
const n: u2 = if (move.effect == .AttackUp2 or move.effect == .Swagger) 2 else 1;
|
|
2600
2624
|
try boostStat(battle, side, stats, boosts, "atk", n);
|
|
@@ -2604,41 +2628,41 @@ pub const Effects = struct {
|
|
|
2604
2628
|
.DefenseUp1, .DefenseUp2, .SkullBash => {
|
|
2605
2629
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2606
2630
|
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE) {
|
|
2607
|
-
return log.fail(.{ ident,
|
|
2631
|
+
return log.fail(.{ ident, .None });
|
|
2608
2632
|
}
|
|
2609
2633
|
const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
|
|
2610
2634
|
try boostStat(battle, side, stats, boosts, "def", n);
|
|
2611
|
-
try log.boost(.{ ident,
|
|
2635
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2612
2636
|
},
|
|
2613
2637
|
.SpeedUp2 => {
|
|
2614
2638
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2615
2639
|
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE) {
|
|
2616
|
-
return log.fail(.{ ident,
|
|
2640
|
+
return log.fail(.{ ident, .None });
|
|
2617
2641
|
}
|
|
2618
2642
|
try boostStat(battle, side, stats, boosts, "spe", 2);
|
|
2619
|
-
try log.boost(.{ ident,
|
|
2643
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2620
2644
|
},
|
|
2621
2645
|
.SpAtkUp1 => {
|
|
2622
2646
|
assert(boosts.spa >= -6 and boosts.spa <= 6);
|
|
2623
2647
|
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE) {
|
|
2624
|
-
return log.fail(.{ ident,
|
|
2648
|
+
return log.fail(.{ ident, .None });
|
|
2625
2649
|
}
|
|
2626
2650
|
try boostStat(battle, side, stats, boosts, "spa", 1);
|
|
2627
|
-
try log.boost(.{ ident,
|
|
2651
|
+
try log.boost(.{ ident, .SpecialAttack, 1 });
|
|
2628
2652
|
},
|
|
2629
2653
|
.SpDefUp2 => {
|
|
2630
2654
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2631
2655
|
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE) {
|
|
2632
|
-
return log.fail(.{ ident,
|
|
2656
|
+
return log.fail(.{ ident, .None });
|
|
2633
2657
|
}
|
|
2634
2658
|
try boostStat(battle, side, stats, boosts, "spd", 2);
|
|
2635
|
-
try log.boost(.{ ident,
|
|
2659
|
+
try log.boost(.{ ident, .SpecialDefense, 2 });
|
|
2636
2660
|
},
|
|
2637
2661
|
.EvasionUp1 => {
|
|
2638
2662
|
assert(boosts.evasion >= -6 and boosts.evasion <= 6);
|
|
2639
|
-
if (boosts.evasion == 6) return log.fail(.{ ident,
|
|
2663
|
+
if (boosts.evasion == 6) return log.fail(.{ ident, .None });
|
|
2640
2664
|
boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
|
|
2641
|
-
try log.boost(.{ ident,
|
|
2665
|
+
try log.boost(.{ ident, .Evasion, 1 });
|
|
2642
2666
|
if (state.move == .Minimize) side.active.volatiles.minimized = true;
|
|
2643
2667
|
},
|
|
2644
2668
|
else => unreachable,
|
|
@@ -2651,13 +2675,13 @@ pub const Effects = struct {
|
|
|
2651
2675
|
const foe_ident = battle.active(player.foe());
|
|
2652
2676
|
|
|
2653
2677
|
if (foe.active.volatiles.Mist) {
|
|
2654
|
-
try log.activate(foe_ident, .Mist);
|
|
2655
|
-
return log.fail(.{ foe_ident,
|
|
2678
|
+
try log.activate(.{ foe_ident, .Mist });
|
|
2679
|
+
return log.fail(.{ foe_ident, .None });
|
|
2656
2680
|
}
|
|
2657
2681
|
|
|
2658
2682
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2659
2683
|
|
|
2660
|
-
|
|
2684
|
+
const stats = &foe.active.stats;
|
|
2661
2685
|
var boosts = &foe.active.boosts;
|
|
2662
2686
|
|
|
2663
2687
|
const move = Move.get(state.move);
|
|
@@ -2675,7 +2699,7 @@ pub const Effects = struct {
|
|
|
2675
2699
|
}
|
|
2676
2700
|
const n: u2 = if (move.effect == .AttackDown2) 2 else 1;
|
|
2677
2701
|
try unboostStat(battle, foe, stats, boosts, "atk", n);
|
|
2678
|
-
try log.boost(.{ foe_ident,
|
|
2702
|
+
try log.boost(.{ foe_ident, .Attack, -@as(i8, n) });
|
|
2679
2703
|
},
|
|
2680
2704
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2681
2705
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
@@ -2691,7 +2715,7 @@ pub const Effects = struct {
|
|
|
2691
2715
|
}
|
|
2692
2716
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2693
2717
|
try unboostStat(battle, foe, stats, boosts, "def", n);
|
|
2694
|
-
try log.boost(.{ foe_ident,
|
|
2718
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2695
2719
|
},
|
|
2696
2720
|
.SpeedDown1, .SpeedDown2, .SpeedDownChance => {
|
|
2697
2721
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
@@ -2705,7 +2729,7 @@ pub const Effects = struct {
|
|
|
2705
2729
|
}
|
|
2706
2730
|
const n: u2 = if (move.effect == .SpeedDown2) 2 else 1;
|
|
2707
2731
|
try unboostStat(battle, foe, stats, boosts, "spe", n);
|
|
2708
|
-
try log.boost(.{ foe_ident,
|
|
2732
|
+
try log.boost(.{ foe_ident, .Speed, -@as(i8, n) });
|
|
2709
2733
|
},
|
|
2710
2734
|
.SpDefDownChance => {
|
|
2711
2735
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
@@ -2715,7 +2739,7 @@ pub const Effects = struct {
|
|
|
2715
2739
|
return options.log.miss(.{battle.active(player)});
|
|
2716
2740
|
} else if (stats.spd == 1) return;
|
|
2717
2741
|
try unboostStat(battle, foe, stats, boosts, "spd", 1);
|
|
2718
|
-
try log.boost(.{ foe_ident,
|
|
2742
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2719
2743
|
},
|
|
2720
2744
|
.AccuracyDown1, .AccuracyDownChance => {
|
|
2721
2745
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
@@ -2727,7 +2751,7 @@ pub const Effects = struct {
|
|
|
2727
2751
|
return options.log.miss(.{battle.active(player)});
|
|
2728
2752
|
}
|
|
2729
2753
|
boosts.accuracy = @intCast(@max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2730
|
-
try log.boost(.{ foe_ident,
|
|
2754
|
+
try log.boost(.{ foe_ident, .Accuracy, -1 });
|
|
2731
2755
|
},
|
|
2732
2756
|
else => unreachable,
|
|
2733
2757
|
}
|
|
@@ -2741,9 +2765,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2741
2765
|
}
|
|
2742
2766
|
|
|
2743
2767
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2744
|
-
|
|
2768
|
+
const stats = unmodifiedStats(battle, side);
|
|
2745
2769
|
inline for (STATS) |s| {
|
|
2746
|
-
|
|
2770
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2747
2771
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2748
2772
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2749
2773
|
}
|
|
@@ -2766,7 +2790,7 @@ fn boostStat(
|
|
|
2766
2790
|
n: u2,
|
|
2767
2791
|
) !void {
|
|
2768
2792
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2769
|
-
|
|
2793
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2770
2794
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2771
2795
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2772
2796
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2789,7 +2813,7 @@ fn unboostStat(
|
|
|
2789
2813
|
n: u2,
|
|
2790
2814
|
) !void {
|
|
2791
2815
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2792
|
-
|
|
2816
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2793
2817
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2794
2818
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2795
2819
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2815,9 +2839,15 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2815
2839
|
return t != .@"???" and !active.types.includes(t);
|
|
2816
2840
|
}
|
|
2817
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
|
+
|
|
2818
2848
|
pub const Rolls = struct {
|
|
2819
2849
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2820
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2850
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2821
2851
|
player == .P1
|
|
2822
2852
|
else if (showdown)
|
|
2823
2853
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2829,7 +2859,7 @@ pub const Rolls = struct {
|
|
|
2829
2859
|
}
|
|
2830
2860
|
|
|
2831
2861
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2832
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2862
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2833
2863
|
val == .true
|
|
2834
2864
|
else if (showdown)
|
|
2835
2865
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2841,7 +2871,7 @@ pub const Rolls = struct {
|
|
|
2841
2871
|
}
|
|
2842
2872
|
|
|
2843
2873
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2844
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2874
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2845
2875
|
val == .true
|
|
2846
2876
|
else if (showdown)
|
|
2847
2877
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2852,20 +2882,22 @@ pub const Rolls = struct {
|
|
|
2852
2882
|
return ok;
|
|
2853
2883
|
}
|
|
2854
2884
|
|
|
2855
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2856
|
-
|
|
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|
|
|
2857
2888
|
val == .true
|
|
2858
2889
|
else if (showdown)
|
|
2859
2890
|
battle.rng.chance(u8, rate, 256)
|
|
2860
2891
|
else
|
|
2861
2892
|
battle.rng.next() < rate;
|
|
2862
2893
|
|
|
2863
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2894
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2864
2895
|
return crit;
|
|
2865
2896
|
}
|
|
2866
2897
|
|
|
2867
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2868
|
-
|
|
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|
|
|
2869
2901
|
val
|
|
2870
2902
|
else roll: {
|
|
2871
2903
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2876,12 +2908,12 @@ pub const Rolls = struct {
|
|
|
2876
2908
|
};
|
|
2877
2909
|
|
|
2878
2910
|
assert(roll >= 217 and roll <= 255);
|
|
2879
|
-
try options.chance.damage(player, roll);
|
|
2911
|
+
try options.chance.damage(player, i, roll);
|
|
2880
2912
|
return roll;
|
|
2881
2913
|
}
|
|
2882
2914
|
|
|
2883
2915
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2884
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2916
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2885
2917
|
val == .true
|
|
2886
2918
|
else if (showdown)
|
|
2887
2919
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2893,7 +2925,7 @@ pub const Rolls = struct {
|
|
|
2893
2925
|
}
|
|
2894
2926
|
|
|
2895
2927
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2896
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2928
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2897
2929
|
val == .true
|
|
2898
2930
|
else if (showdown)
|
|
2899
2931
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2905,7 +2937,7 @@ pub const Rolls = struct {
|
|
|
2905
2937
|
}
|
|
2906
2938
|
|
|
2907
2939
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2908
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2940
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2909
2941
|
val == .true
|
|
2910
2942
|
else if (showdown)
|
|
2911
2943
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2917,7 +2949,7 @@ pub const Rolls = struct {
|
|
|
2917
2949
|
}
|
|
2918
2950
|
|
|
2919
2951
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2920
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2952
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2921
2953
|
val == .true
|
|
2922
2954
|
else if (showdown)
|
|
2923
2955
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2930,7 +2962,7 @@ pub const Rolls = struct {
|
|
|
2930
2962
|
|
|
2931
2963
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2932
2964
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2933
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2965
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2934
2966
|
val == .true
|
|
2935
2967
|
else if (showdown)
|
|
2936
2968
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2942,7 +2974,7 @@ pub const Rolls = struct {
|
|
|
2942
2974
|
}
|
|
2943
2975
|
|
|
2944
2976
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2945
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2977
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2946
2978
|
val == .true
|
|
2947
2979
|
else if (showdown)
|
|
2948
2980
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2954,7 +2986,7 @@ pub const Rolls = struct {
|
|
|
2954
2986
|
}
|
|
2955
2987
|
|
|
2956
2988
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2957
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2989
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2958
2990
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2959
2991
|
else if (showdown)
|
|
2960
2992
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2970,7 +3002,7 @@ pub const Rolls = struct {
|
|
|
2970
3002
|
}
|
|
2971
3003
|
|
|
2972
3004
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2973
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3005
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2974
3006
|
(val - 1) * 40
|
|
2975
3007
|
else if (showdown) power: {
|
|
2976
3008
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2992,7 +3024,7 @@ pub const Rolls = struct {
|
|
|
2992
3024
|
}
|
|
2993
3025
|
|
|
2994
3026
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2995
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3027
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2996
3028
|
val + 3
|
|
2997
3029
|
else if (showdown) num: {
|
|
2998
3030
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -3020,7 +3052,7 @@ pub const Rolls = struct {
|
|
|
3020
3052
|
}
|
|
3021
3053
|
|
|
3022
3054
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
3023
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3055
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
3024
3056
|
val
|
|
3025
3057
|
else if (showdown)
|
|
3026
3058
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3036,13 +3068,12 @@ pub const Rolls = struct {
|
|
|
3036
3068
|
return hits;
|
|
3037
3069
|
}
|
|
3038
3070
|
|
|
3039
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3040
|
-
|
|
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|
|
|
3041
3074
|
val + 1
|
|
3042
|
-
else if (showdown)
|
|
3043
|
-
battle.rng.range(u8, 2, 6)
|
|
3044
3075
|
else
|
|
3045
|
-
(battle.rng.next() & 3) + 2;
|
|
3076
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3046
3077
|
|
|
3047
3078
|
assert(pp >= 2 and pp <= 5);
|
|
3048
3079
|
try options.chance.spite(player, pp);
|
|
@@ -3056,7 +3087,7 @@ pub const Rolls = struct {
|
|
|
3056
3087
|
var i: u8 = 0;
|
|
3057
3088
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3058
3089
|
assert(mtype != .@"???");
|
|
3059
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3090
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3060
3091
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3061
3092
|
else if (showdown) ty: {
|
|
3062
3093
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3090,7 +3121,7 @@ pub const Rolls = struct {
|
|
|
3090
3121
|
assert(count <= 8);
|
|
3091
3122
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3092
3123
|
|
|
3093
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3124
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3094
3125
|
val == .true
|
|
3095
3126
|
else if (showdown)
|
|
3096
3127
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3112,7 +3143,7 @@ pub const Rolls = struct {
|
|
|
3112
3143
|
const moves = active.moves;
|
|
3113
3144
|
|
|
3114
3145
|
// TODO: consider throwing error instead of rerolling?
|
|
3115
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3146
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3116
3147
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3117
3148
|
else
|
|
3118
3149
|
null;
|
|
@@ -3149,7 +3180,7 @@ pub const Rolls = struct {
|
|
|
3149
3180
|
const moves = active.moves;
|
|
3150
3181
|
|
|
3151
3182
|
// TODO: consider throwing error instead of rerolling?
|
|
3152
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3183
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3153
3184
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3154
3185
|
else
|
|
3155
3186
|
null;
|
|
@@ -3193,7 +3224,7 @@ pub const Rolls = struct {
|
|
|
3193
3224
|
assert(n > 0 and n <= 6);
|
|
3194
3225
|
|
|
3195
3226
|
// TODO: consider throwing error instead of rerolling?
|
|
3196
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3227
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3197
3228
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3198
3229
|
break :val null;
|
|
3199
3230
|
} else null;
|
|
@@ -3219,7 +3250,7 @@ pub const Rolls = struct {
|
|
|
3219
3250
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3220
3251
|
|
|
3221
3252
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3222
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3253
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3223
3254
|
@intCast(val)
|
|
3224
3255
|
else if (showdown)
|
|
3225
3256
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3234,7 +3265,7 @@ pub const Rolls = struct {
|
|
|
3234
3265
|
}
|
|
3235
3266
|
|
|
3236
3267
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3237
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3268
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3238
3269
|
val
|
|
3239
3270
|
else if (showdown)
|
|
3240
3271
|
battle.rng.range(u8, 1, max)
|
|
@@ -3260,7 +3291,7 @@ pub const Rolls = struct {
|
|
|
3260
3291
|
}
|
|
3261
3292
|
|
|
3262
3293
|
// TODO: consider throwing error instead of rerolling?
|
|
3263
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3294
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3264
3295
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3265
3296
|
break :val val;
|
|
3266
3297
|
} else null;
|
|
@@ -3292,7 +3323,7 @@ pub const Rolls = struct {
|
|
|
3292
3323
|
}
|
|
3293
3324
|
|
|
3294
3325
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3295
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3326
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3296
3327
|
@intCast(val)
|
|
3297
3328
|
else if (showdown)
|
|
3298
3329
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3304,12 +3335,12 @@ pub const Rolls = struct {
|
|
|
3304
3335
|
};
|
|
3305
3336
|
|
|
3306
3337
|
assert(duration >= 1 and duration <= 6);
|
|
3307
|
-
options.chance.duration(
|
|
3338
|
+
options.chance.duration(player, duration);
|
|
3308
3339
|
return duration + 1;
|
|
3309
3340
|
}
|
|
3310
3341
|
|
|
3311
3342
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3312
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3343
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3313
3344
|
@intCast(val)
|
|
3314
3345
|
else if (showdown)
|
|
3315
3346
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3318,16 +3349,16 @@ pub const Rolls = struct {
|
|
|
3318
3349
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3319
3350
|
|
|
3320
3351
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3321
|
-
options.chance.duration(
|
|
3352
|
+
options.chance.duration(player, duration);
|
|
3322
3353
|
return duration;
|
|
3323
3354
|
}
|
|
3324
3355
|
|
|
3325
3356
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3326
|
-
const duration:
|
|
3357
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3327
3358
|
@intCast(val)
|
|
3328
3359
|
else if (showdown)
|
|
3329
3360
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3330
|
-
battle.rng.range(
|
|
3361
|
+
battle.rng.range(u3, 2, 6)
|
|
3331
3362
|
else duration: {
|
|
3332
3363
|
while (true) {
|
|
3333
3364
|
const r = battle.rng.next() & 7;
|
|
@@ -3336,12 +3367,12 @@ pub const Rolls = struct {
|
|
|
3336
3367
|
};
|
|
3337
3368
|
|
|
3338
3369
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3339
|
-
options.chance.duration(
|
|
3370
|
+
options.chance.duration(player, duration);
|
|
3340
3371
|
return duration;
|
|
3341
3372
|
}
|
|
3342
3373
|
|
|
3343
3374
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3344
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3375
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3345
3376
|
@intCast(val)
|
|
3346
3377
|
else if (showdown)
|
|
3347
3378
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3349,12 +3380,12 @@ pub const Rolls = struct {
|
|
|
3349
3380
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3350
3381
|
|
|
3351
3382
|
assert(duration >= 2 and duration <= 3);
|
|
3352
|
-
options.chance.duration(
|
|
3383
|
+
options.chance.duration(player, duration);
|
|
3353
3384
|
return duration;
|
|
3354
3385
|
}
|
|
3355
3386
|
|
|
3356
3387
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3357
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3388
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3358
3389
|
@intCast(val)
|
|
3359
3390
|
else if (showdown)
|
|
3360
3391
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3363,13 +3394,13 @@ pub const Rolls = struct {
|
|
|
3363
3394
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3364
3395
|
|
|
3365
3396
|
assert(duration >= 2 and duration <= 5);
|
|
3366
|
-
options.chance.duration(
|
|
3397
|
+
options.chance.duration(player, duration);
|
|
3367
3398
|
return duration + 1;
|
|
3368
3399
|
}
|
|
3369
3400
|
|
|
3370
3401
|
// TODO: consider sharing implementation with bindingDuration
|
|
3371
3402
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3372
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3403
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3373
3404
|
@intCast(val)
|
|
3374
3405
|
else if (showdown)
|
|
3375
3406
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3377,7 +3408,7 @@ pub const Rolls = struct {
|
|
|
3377
3408
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3378
3409
|
|
|
3379
3410
|
assert(duration >= 2 and duration <= 5);
|
|
3380
|
-
options.chance.duration(
|
|
3411
|
+
options.chance.duration(player, duration);
|
|
3381
3412
|
return duration + 1;
|
|
3382
3413
|
}
|
|
3383
3414
|
};
|
|
@@ -3490,7 +3521,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3490
3521
|
.Move => {
|
|
3491
3522
|
const side = battle.side(player);
|
|
3492
3523
|
|
|
3493
|
-
|
|
3524
|
+
const active = &side.active;
|
|
3494
3525
|
const stored = side.stored();
|
|
3495
3526
|
|
|
3496
3527
|
// While players are not given any input options on the cartridge in these cases,
|