@pkmn/engine 0.1.0-dev.d56bdadd → 0.1.0-dev.d73cd76c
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 +66 -32
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +47 -0
- package/build/pkg/addon/browser.js.map +1 -0
- package/build/pkg/addon/node.d.ts +3 -0
- package/build/pkg/addon/node.js +110 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon/wasm.d.ts +2 -0
- package/build/pkg/addon/wasm.js +53 -0
- package/build/pkg/addon/wasm.js.map +1 -0
- package/build/pkg/addon.d.ts +26 -4
- package/build/pkg/addon.js +39 -71
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data/layout.json +15 -2
- package/build/pkg/data/protocol.json +23 -9
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.d.ts +15 -18
- package/build/pkg/gen1.js +28 -19
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +42 -91
- package/build/pkg/index.js +23 -70
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +29 -1
- package/build/pkg/protocol.js +109 -107
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +84 -73
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +66 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +152 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +360 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +157 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +357 -135
- package/package.json +61 -132
- package/src/bin/install-pkmn-engine +47 -21
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/bindings/c.zig +201 -0
- package/src/lib/{binding → bindings}/node.zig +15 -32
- package/src/lib/bindings/wasm.zig +51 -0
- package/src/lib/common/array.zig +70 -0
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +45 -29
- package/src/lib/common/js.zig +3 -7
- package/src/lib/common/optional.zig +27 -23
- package/src/lib/common/options.zig +6 -0
- package/src/lib/common/protocol.zig +565 -552
- package/src/lib/common/rational.zig +340 -253
- package/src/lib/common/rng.zig +7 -9
- package/src/lib/common/util.zig +53 -1
- package/src/lib/gen1/calc.zig +724 -175
- package/src/lib/gen1/chance.zig +486 -300
- 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 +3 -4
- package/src/lib/gen1/data.zig +29 -32
- package/src/lib/gen1/helpers.zig +113 -561
- package/src/lib/gen1/mechanics.zig +611 -488
- package/src/lib/gen2/calc.zig +35 -28
- package/src/lib/gen2/chance.zig +111 -511
- 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 +7 -5
- package/src/lib/gen2/data.zig +27 -29
- package/src/lib/gen2/generated.zig +22 -29
- package/src/lib/gen2/helpers.zig +68 -39
- package/src/lib/gen2/mechanics.zig +504 -430
- package/src/lib/napi.h +2 -0
- package/src/lib/node.zig +14 -0
- package/src/lib/pkmn.zig +125 -29
- package/src/lib/wasm.zig +13 -0
- package/src/tools/display/dom.ts +108 -0
- package/src/tools/display/ui.css +354 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/binding/wasm.zig +0 -12
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -1,50 +1,37 @@
|
|
|
1
|
-
const std = @import("std");
|
|
2
|
-
|
|
3
|
-
const pkmn = @import("../pkmn.zig");
|
|
4
|
-
|
|
5
|
-
const common = @import("../common/data.zig");
|
|
6
1
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
|
-
const protocol = @import("../common/protocol.zig");
|
|
8
|
-
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
2
|
|
|
3
|
+
const builtin = @import("builtin");
|
|
4
|
+
const common = @import("../common/data.zig");
|
|
11
5
|
const data = @import("data.zig");
|
|
12
6
|
const generated = @import("generated.zig");
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
18
|
-
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
19
|
-
|
|
20
|
-
const showdown = pkmn.options.showdown;
|
|
21
|
-
|
|
22
|
-
const Choice = common.Choice;
|
|
23
|
-
const ID = common.ID;
|
|
24
|
-
const Player = common.Player;
|
|
25
|
-
const Result = common.Result;
|
|
7
|
+
const pkmn = @import("../pkmn.zig");
|
|
8
|
+
const protocol = @import("../common/protocol.zig");
|
|
9
|
+
const rng = @import("../common/rng.zig");
|
|
10
|
+
const std = @import("std");
|
|
26
11
|
|
|
27
12
|
const Activate = protocol.Activate;
|
|
13
|
+
const ActivePokemon = data.ActivePokemon;
|
|
14
|
+
const assert = std.debug.assert;
|
|
28
15
|
const Boost = protocol.Boost;
|
|
29
|
-
const
|
|
16
|
+
const Boosts = data.Boosts;
|
|
17
|
+
const Choice = common.Choice;
|
|
30
18
|
const Damage = protocol.Damage;
|
|
19
|
+
const Effectiveness = data.Effectiveness;
|
|
31
20
|
const End = protocol.End;
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
21
|
+
const expectEqual = std.testing.expectEqual;
|
|
22
|
+
const expectEqualSlices = std.testing.expectEqualSlices;
|
|
23
|
+
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
24
|
+
const Fail = protocol.Fail;
|
|
35
25
|
const Gen12 = rng.Gen12;
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
const ActivePokemon = data.ActivePokemon;
|
|
40
|
-
const Boosts = data.Boosts;
|
|
41
|
-
const Effectiveness = data.Effectiveness;
|
|
26
|
+
const Heal = protocol.Heal;
|
|
27
|
+
const ID = common.ID;
|
|
42
28
|
const Item = data.Item;
|
|
43
29
|
const Move = data.Move;
|
|
44
|
-
const
|
|
45
|
-
const
|
|
30
|
+
const Player = common.Player;
|
|
31
|
+
const Result = common.Result;
|
|
32
|
+
const showdown = pkmn.options.showdown;
|
|
46
33
|
const Side = data.Side;
|
|
47
|
-
const
|
|
34
|
+
const Start = protocol.Start;
|
|
48
35
|
const Stats = data.Stats;
|
|
49
36
|
const Status = data.Status;
|
|
50
37
|
const TriAttack = data.TriAttack;
|
|
@@ -106,6 +93,7 @@ pub const State = struct {
|
|
|
106
93
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
107
94
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
108
95
|
move: Move, // wCurPlayerMove
|
|
96
|
+
hit: u3 = 0,
|
|
109
97
|
from: Move = .None,
|
|
110
98
|
mslot: u4, // wCurMoveNum
|
|
111
99
|
first: bool, // wEnemyGoesFirst
|
|
@@ -140,13 +128,13 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
140
128
|
const p1 = battle.side(.P1);
|
|
141
129
|
const p2 = battle.side(.P2);
|
|
142
130
|
|
|
143
|
-
|
|
131
|
+
const p1_slot = findFirstAlive(p1);
|
|
144
132
|
assert(!showdown or p1_slot == 1);
|
|
145
|
-
if (p1_slot == 0) return if (findFirstAlive(p2) == 0)
|
|
133
|
+
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) .Tie else .Lose;
|
|
146
134
|
|
|
147
|
-
|
|
135
|
+
const p2_slot = findFirstAlive(p2);
|
|
148
136
|
assert(!showdown or p2_slot == 1);
|
|
149
|
-
if (p2_slot == 0) return
|
|
137
|
+
if (p2_slot == 0) return .Win;
|
|
150
138
|
|
|
151
139
|
try switchIn(battle, .P1, p1_slot, .Initial, options);
|
|
152
140
|
try switchIn(battle, .P2, p2_slot, .Initial, options);
|
|
@@ -252,15 +240,14 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
252
240
|
statusModify(incoming.status, &active.stats);
|
|
253
241
|
|
|
254
242
|
if (kind == .Drag) {
|
|
255
|
-
try options.log.drag(battle.active(player), incoming);
|
|
243
|
+
try options.log.drag(.{ battle.active(player), incoming });
|
|
256
244
|
} else {
|
|
257
|
-
try options.log.switched(battle.active(player), incoming);
|
|
245
|
+
try options.log.switched(.{ battle.active(player), incoming });
|
|
258
246
|
}
|
|
259
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
260
247
|
|
|
261
248
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
262
249
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
263
|
-
try options.log.damage(battle.active(player), incoming, .Spikes);
|
|
250
|
+
try options.log.damage(.{ battle.active(player), incoming, .Spikes });
|
|
264
251
|
}
|
|
265
252
|
}
|
|
266
253
|
|
|
@@ -403,7 +390,9 @@ fn executeMove(
|
|
|
403
390
|
|
|
404
391
|
assert(choice.type == .Move);
|
|
405
392
|
var mslot: u4 = @intCast(choice.data);
|
|
393
|
+
_ = &mslot;
|
|
406
394
|
var move = Move.Pound; // TODO
|
|
395
|
+
_ = &move;
|
|
407
396
|
|
|
408
397
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
409
398
|
|
|
@@ -433,31 +422,22 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
433
422
|
if (volatiles.Recharging) {
|
|
434
423
|
volatiles.Recharging = false;
|
|
435
424
|
resetCant(volatiles);
|
|
436
|
-
try log.cant(ident, .Recharge);
|
|
425
|
+
try log.cant(.{ ident, .Recharge });
|
|
437
426
|
return false;
|
|
438
427
|
}
|
|
439
428
|
|
|
440
429
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
441
430
|
const before = stored.status;
|
|
442
431
|
// Even if the EXT bit is set this will still correctly modify the sleep duration
|
|
443
|
-
// if (options.calc.modify(player, .sleep)) |extend| {
|
|
444
|
-
// if (!extend) stored.status = 0;
|
|
445
|
-
// } else {
|
|
446
432
|
stored.status -= 1;
|
|
447
|
-
// }
|
|
448
|
-
|
|
449
|
-
const duration = Status.duration(stored.status);
|
|
450
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
451
|
-
try options.chance.sleep(player, duration);
|
|
452
|
-
}
|
|
453
433
|
|
|
454
|
-
if (duration == 0) {
|
|
455
|
-
try log.curestatus(ident, before, .Message);
|
|
434
|
+
if (Status.duration(stored.status) == 0) {
|
|
435
|
+
try log.curestatus(.{ ident, before, .Message });
|
|
456
436
|
stored.status = 0; // clears EXT if present
|
|
457
437
|
resetCant(volatiles);
|
|
458
438
|
volatiles.Nightmare = false;
|
|
459
439
|
} else {
|
|
460
|
-
try log.cant(ident, .Sleep);
|
|
440
|
+
try log.cant(.{ ident, .Sleep });
|
|
461
441
|
if (move == .Snore or move == .SleepTalk) break :slp;
|
|
462
442
|
resetCant(volatiles);
|
|
463
443
|
return false;
|
|
@@ -466,7 +446,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
466
446
|
|
|
467
447
|
if (Status.is(stored.status, .FRZ) and !(move == .FlameWheel or move == .SacredFire)) {
|
|
468
448
|
resetCant(volatiles);
|
|
469
|
-
try log.cant(ident, .Freeze);
|
|
449
|
+
try log.cant(.{ ident, .Freeze });
|
|
470
450
|
return false;
|
|
471
451
|
}
|
|
472
452
|
|
|
@@ -476,39 +456,28 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
476
456
|
// if (!showdown) volatiles.Flinch = false;
|
|
477
457
|
volatiles.Flinch = false;
|
|
478
458
|
resetCant(volatiles);
|
|
479
|
-
try log.cant(ident, .Flinch);
|
|
459
|
+
try log.cant(.{ ident, .Flinch });
|
|
480
460
|
return false;
|
|
481
461
|
}
|
|
482
462
|
|
|
483
463
|
if (volatiles.disable.duration > 0) {
|
|
484
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
485
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
486
|
-
// } else {
|
|
487
464
|
volatiles.disable.duration -= 1;
|
|
488
|
-
// }
|
|
489
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
490
465
|
|
|
491
466
|
if (volatiles.disable.duration == 0) {
|
|
492
467
|
volatiles.disable.move = 0;
|
|
493
|
-
try log.end(ident, .Disable);
|
|
468
|
+
try log.end(.{ ident, .Disable });
|
|
494
469
|
}
|
|
495
470
|
}
|
|
496
471
|
|
|
497
472
|
if (volatiles.Confusion) {
|
|
498
473
|
assert(volatiles.confusion > 0);
|
|
499
|
-
|
|
500
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
501
|
-
// if (!extend) volatiles.confusion = 0;
|
|
502
|
-
// } else {
|
|
503
474
|
volatiles.confusion -= 1;
|
|
504
|
-
// }
|
|
505
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
506
475
|
|
|
507
476
|
if (volatiles.confusion == 0) {
|
|
508
477
|
volatiles.Confusion = false;
|
|
509
|
-
try log.end(ident, .Confusion);
|
|
478
|
+
try log.end(.{ ident, .Confusion });
|
|
510
479
|
} else {
|
|
511
|
-
try log.activate(ident, .Confusion);
|
|
480
|
+
try log.activate(.{ ident, .Confusion });
|
|
512
481
|
|
|
513
482
|
if (try Rolls.confused(battle, player, options)) {
|
|
514
483
|
volatiles.BeatUp = false;
|
|
@@ -523,11 +492,11 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
523
492
|
}
|
|
524
493
|
|
|
525
494
|
if (volatiles.Attract) {
|
|
526
|
-
try log.activate(ident, .Attract);
|
|
495
|
+
try log.activate(.{ ident, .Attract });
|
|
527
496
|
|
|
528
497
|
if (try Rolls.attract(battle, player, options)) {
|
|
529
498
|
resetCant(volatiles);
|
|
530
|
-
try log.cant(ident, .Attract);
|
|
499
|
+
try log.cant(.{ ident, .Attract });
|
|
531
500
|
return false;
|
|
532
501
|
}
|
|
533
502
|
}
|
|
@@ -538,14 +507,14 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
538
507
|
if (m != .None and m == move) {
|
|
539
508
|
side.active.volatiles.Charging = false;
|
|
540
509
|
resetCant(volatiles);
|
|
541
|
-
try options.log.
|
|
510
|
+
try options.log.cant(.{ ident, .Disable, move });
|
|
542
511
|
return false;
|
|
543
512
|
}
|
|
544
513
|
}
|
|
545
514
|
|
|
546
515
|
if (Status.is(stored.status, .PAR) and try Rolls.paralyzed(battle, player, options)) {
|
|
547
516
|
resetCant(volatiles);
|
|
548
|
-
try log.cant(ident, .Paralysis);
|
|
517
|
+
try log.cant(.{ ident, .Paralysis });
|
|
549
518
|
return false;
|
|
550
519
|
}
|
|
551
520
|
|
|
@@ -565,11 +534,11 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
565
534
|
|
|
566
535
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
567
536
|
var side = battle.side(player);
|
|
568
|
-
|
|
537
|
+
const volatiles = &side.active.volatiles;
|
|
569
538
|
|
|
570
539
|
// FIXME: pass in target to avoid lookup?
|
|
571
540
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
572
|
-
try options.log.
|
|
541
|
+
try options.log.move(.{ battle.active(player), state.move, battle.active(target), state.from });
|
|
573
542
|
|
|
574
543
|
const charging = false; // TODO charging uses doUse
|
|
575
544
|
const skip_pp = charging or state.move == .Struggle or
|
|
@@ -663,7 +632,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
663
632
|
}
|
|
664
633
|
assert(stage <= 4);
|
|
665
634
|
|
|
666
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
635
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
667
636
|
}
|
|
668
637
|
|
|
669
638
|
pub fn calcDamage(
|
|
@@ -713,7 +682,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
713
682
|
|
|
714
683
|
// Type effectiveness matchup precedence only matters with (NVE, SE)
|
|
715
684
|
if (!showdown and (eff1 + eff2) == Effectiveness.mismatch and
|
|
716
|
-
types.type1.precedence(
|
|
685
|
+
Type.precedence(state.type, types.type1) > Type.precedence(state.type, types.type2))
|
|
717
686
|
{
|
|
718
687
|
assert(eff2 != neutral);
|
|
719
688
|
d = d *% eff2 / 10;
|
|
@@ -731,7 +700,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
731
700
|
|
|
732
701
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
733
702
|
if (state.damage <= 1) return;
|
|
734
|
-
const random = try Rolls.damage(battle, player, options);
|
|
703
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
735
704
|
options.calc.base(player.foe(), state.damage);
|
|
736
705
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
737
706
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -849,14 +818,14 @@ fn checkFaint(
|
|
|
849
818
|
if (foe_fainted) try faint(battle, player.foe(), !more, options);
|
|
850
819
|
|
|
851
820
|
if (player_out and foe_out) {
|
|
852
|
-
try options.log.tie();
|
|
853
|
-
return
|
|
821
|
+
try options.log.tie(.{});
|
|
822
|
+
return .Tie;
|
|
854
823
|
} else if (player_out) {
|
|
855
|
-
try options.log.win(player.foe());
|
|
856
|
-
return if (player == .P1)
|
|
824
|
+
try options.log.win(.{player.foe()});
|
|
825
|
+
return if (player == .P1) .Lose else .Win;
|
|
857
826
|
} else if (foe_out) {
|
|
858
|
-
try options.log.win(player);
|
|
859
|
-
return if (player == .P1)
|
|
827
|
+
try options.log.win(.{player});
|
|
828
|
+
return if (player == .P1) .Win else .Lose;
|
|
860
829
|
}
|
|
861
830
|
|
|
862
831
|
const foe_choice: Choice.Type = if (foe_fainted) .Switch else .Pass;
|
|
@@ -885,19 +854,19 @@ fn faint(battle: anytype, player: Player, done: bool, options: anytype) !void {
|
|
|
885
854
|
side.stored().stats.spe;
|
|
886
855
|
}
|
|
887
856
|
|
|
888
|
-
try options.log.faint(battle.active(player), done);
|
|
857
|
+
try options.log.faint(.{ battle.active(player), done });
|
|
889
858
|
options.calc.capped(player);
|
|
890
859
|
}
|
|
891
860
|
|
|
892
861
|
// TODO: see if we can move *before* applyDamage and include failuretext logic
|
|
893
862
|
pub fn reportOutcome(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
894
863
|
const foe_ident = battle.active(player.foe());
|
|
895
|
-
if (state.crit) try options.log.crit(foe_ident);
|
|
864
|
+
if (state.crit) try options.log.crit(.{foe_ident});
|
|
896
865
|
if (!state.immune()) {
|
|
897
866
|
if (state.effectiveness > Effectiveness.neutral) {
|
|
898
|
-
try options.log.supereffective(foe_ident);
|
|
867
|
+
try options.log.supereffective(.{foe_ident});
|
|
899
868
|
} else if (state.effectiveness < Effectiveness.neutral) {
|
|
900
|
-
try options.log.resisted(foe_ident);
|
|
869
|
+
try options.log.resisted(.{foe_ident});
|
|
901
870
|
}
|
|
902
871
|
}
|
|
903
872
|
}
|
|
@@ -930,8 +899,8 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
930
899
|
}
|
|
931
900
|
|
|
932
901
|
stored.hp -|= damage;
|
|
933
|
-
// Pokémon Showdown uses
|
|
934
|
-
try options.log.damage(ident, stored, if (brn) Damage.Burn else Damage.Poison);
|
|
902
|
+
// Pokémon Showdown uses damage [of] here but its not relevant in Generation II
|
|
903
|
+
try options.log.damage(.{ ident, stored, if (brn) Damage.Burn else Damage.Poison });
|
|
935
904
|
if (stored.hp == 0) return true;
|
|
936
905
|
}
|
|
937
906
|
|
|
@@ -947,27 +916,27 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
947
916
|
|
|
948
917
|
const damage = @max(stored.stats.hp / 8, 1);
|
|
949
918
|
stored.hp -|= damage;
|
|
950
|
-
// As above, Pokémon Showdown uses
|
|
951
|
-
try options.log.damage(ident, stored, .LeechSeed);
|
|
919
|
+
// As above, Pokémon Showdown uses damage [of] but its not relevant
|
|
920
|
+
try options.log.damage(.{ ident, stored, .LeechSeed });
|
|
952
921
|
|
|
953
922
|
const before = foe_stored.hp;
|
|
954
923
|
foe_stored.hp = @min(foe_stored.hp + damage, foe_stored.stats.hp);
|
|
955
924
|
// Pokémon Showdown uses the less specific heal here instead of drain... because reasons?
|
|
956
|
-
if (foe_stored.hp > before) try options.log.heal(foe_ident, foe_stored, .Silent);
|
|
925
|
+
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored, .Silent });
|
|
957
926
|
if (stored.hp == 0) return true;
|
|
958
927
|
}
|
|
959
928
|
|
|
960
929
|
if (volatiles.Nightmare) {
|
|
961
930
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
962
931
|
// ibid
|
|
963
|
-
try options.log.damage(ident, stored, .Nightmare);
|
|
932
|
+
try options.log.damage(.{ ident, stored, .Nightmare });
|
|
964
933
|
if (stored.hp == 0) return true;
|
|
965
934
|
}
|
|
966
935
|
|
|
967
936
|
if (volatiles.Curse) {
|
|
968
937
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
969
938
|
// ibid
|
|
970
|
-
try options.log.damage(ident, stored, .Curse);
|
|
939
|
+
try options.log.damage(.{ ident, stored, .Curse });
|
|
971
940
|
if (stored.hp == 0) return true;
|
|
972
941
|
}
|
|
973
942
|
|
|
@@ -988,7 +957,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
988
957
|
future_sight.count -= 1;
|
|
989
958
|
assert(future_sight.count != 0);
|
|
990
959
|
if (future_sight.count == 1) {
|
|
991
|
-
try options.log.end(battle.active(player), .FutureSight);
|
|
960
|
+
try options.log.end(.{ battle.active(player), .FutureSight });
|
|
992
961
|
// TODO doMove
|
|
993
962
|
}
|
|
994
963
|
}
|
|
@@ -997,21 +966,24 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
997
966
|
|
|
998
967
|
// Weather
|
|
999
968
|
if (battle.field.weather != .None) {
|
|
1000
|
-
assert(battle.field.weather_duration > 0);
|
|
1001
969
|
battle.field.weather_duration -= 1;
|
|
1002
970
|
|
|
1003
971
|
if (battle.field.weather_duration == 0) {
|
|
1004
972
|
battle.field.weather = .None;
|
|
1005
|
-
try options.log.weather(battle.field.weather, .None);
|
|
973
|
+
try options.log.weather(.{ battle.field.weather, protocol.Weather.None });
|
|
1006
974
|
} else {
|
|
1007
|
-
try options.log.weather(battle.field.weather, .Upkeep);
|
|
975
|
+
try options.log.weather(.{ battle.field.weather, protocol.Weather.Upkeep });
|
|
1008
976
|
if (battle.field.weather == .Sandstorm) {
|
|
1009
977
|
inline for (players) |player| {
|
|
1010
978
|
var side = battle.side(player);
|
|
1011
979
|
const active = side.active;
|
|
1012
980
|
if (!(active.volatiles.Underground or active.types.sandstormImmune())) {
|
|
1013
981
|
side.stored().hp -|= @max(side.stored().stats.hp / 8, 1);
|
|
1014
|
-
try options.log.damage(
|
|
982
|
+
try options.log.damage(.{
|
|
983
|
+
battle.active(player),
|
|
984
|
+
side.stored(),
|
|
985
|
+
.Sandstorm,
|
|
986
|
+
});
|
|
1015
987
|
}
|
|
1016
988
|
}
|
|
1017
989
|
if (try checkFaint(battle, .P1, options)) |r| return r;
|
|
@@ -1023,19 +995,24 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1023
995
|
inline for (players) |player| {
|
|
1024
996
|
var side = battle.side(player);
|
|
1025
997
|
var volatiles = &side.active.volatiles;
|
|
998
|
+
const ident = battle.active(player);
|
|
1026
999
|
|
|
1027
1000
|
if (volatiles.bind.duration > 0 and !volatiles.Substitute) {
|
|
1028
1001
|
volatiles.bind.duration -= 1;
|
|
1029
1002
|
if (volatiles.bind.duration == 0) {
|
|
1030
1003
|
const reason = @intFromEnum(End.Bind) + volatiles.bind.reason - 1;
|
|
1031
|
-
try options.log.end(
|
|
1004
|
+
try options.log.end(.{ ident, @as(End, @enumFromInt(reason)) });
|
|
1032
1005
|
} else {
|
|
1033
1006
|
var reason = @intFromEnum(Activate.Bind) + volatiles.bind.reason - 1;
|
|
1034
|
-
try options.log.activate(
|
|
1007
|
+
try options.log.activate(.{ ident, @as(Activate, @enumFromInt(reason)) });
|
|
1035
1008
|
|
|
1036
1009
|
reason = @intFromEnum(Damage.Bind) + volatiles.bind.reason - 1;
|
|
1037
1010
|
side.stored().hp -|= @max(side.stored().stats.hp / 16, 1);
|
|
1038
|
-
try options.log.damage(
|
|
1011
|
+
try options.log.damage(.{
|
|
1012
|
+
ident,
|
|
1013
|
+
side.stored(),
|
|
1014
|
+
@as(Damage, @enumFromInt(reason)),
|
|
1015
|
+
});
|
|
1039
1016
|
}
|
|
1040
1017
|
}
|
|
1041
1018
|
}
|
|
@@ -1047,12 +1024,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1047
1024
|
var volatiles = &side.active.volatiles;
|
|
1048
1025
|
|
|
1049
1026
|
if (volatiles.PerishSong) {
|
|
1050
|
-
assert(volatiles.perish_song > 0);
|
|
1051
1027
|
volatiles.perish_song -= 1;
|
|
1052
1028
|
|
|
1053
1029
|
assert(volatiles.perish_song < 3);
|
|
1054
1030
|
const reason = @intFromEnum(Start.PerishSong0) + volatiles.perish_song;
|
|
1055
|
-
try options.log.start(battle.active(player), @enumFromInt(reason));
|
|
1031
|
+
try options.log.start(.{ battle.active(player), @as(Start, @enumFromInt(reason)) });
|
|
1056
1032
|
|
|
1057
1033
|
if (volatiles.perish_song == 0) {
|
|
1058
1034
|
volatiles.PerishSong = false;
|
|
@@ -1069,13 +1045,15 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1069
1045
|
if (stored.item == .Leftovers) {
|
|
1070
1046
|
const before = stored.hp;
|
|
1071
1047
|
stored.hp = @min(stored.hp + @max(stored.stats.hp / 16, 1), stored.stats.hp);
|
|
1072
|
-
if (stored.hp > before)
|
|
1048
|
+
if (stored.hp > before) {
|
|
1049
|
+
try options.log.heal(.{ battle.active(player), stored, .Leftovers });
|
|
1050
|
+
}
|
|
1073
1051
|
}
|
|
1074
1052
|
}
|
|
1075
1053
|
|
|
1076
1054
|
// Mystery Berry
|
|
1077
1055
|
inline for (players) |player| {
|
|
1078
|
-
|
|
1056
|
+
const stored = battle.side(player).stored();
|
|
1079
1057
|
|
|
1080
1058
|
if (stored.item == .MysteryBerry) {
|
|
1081
1059
|
// TODO stored and active moves, transform, etc
|
|
@@ -1090,7 +1068,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1090
1068
|
!side.active.volatiles.frozen and
|
|
1091
1069
|
try Rolls.defrost(battle, player, options))
|
|
1092
1070
|
{
|
|
1093
|
-
try options.log.curestatus(battle.active(player), side.stored().status, .Message);
|
|
1071
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1094
1072
|
side.stored().status = 0;
|
|
1095
1073
|
}
|
|
1096
1074
|
}
|
|
@@ -1131,14 +1109,14 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1131
1109
|
stored.hp < stored.stats.hp;
|
|
1132
1110
|
if (proc) {
|
|
1133
1111
|
assert(stored.hp != 0);
|
|
1134
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1112
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1135
1113
|
stored.item = .None;
|
|
1136
1114
|
|
|
1137
1115
|
const offset = num - @intFromEnum(Item.Berry);
|
|
1138
1116
|
assert(offset < 3);
|
|
1139
1117
|
stored.hp = @min(stored.hp + ((offset + 1) * 10), stored.stats.hp);
|
|
1140
1118
|
const reason: Heal = @enumFromInt(@intFromEnum(Heal.Berry) + offset);
|
|
1141
|
-
try options.log.heal(ident, stored, reason);
|
|
1119
|
+
try options.log.heal(.{ ident, stored, reason });
|
|
1142
1120
|
}
|
|
1143
1121
|
} else if (num < @intFromEnum(Item.BitterBerry)) {
|
|
1144
1122
|
const proc = if (stored.item == .MiracleBerry)
|
|
@@ -1148,7 +1126,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1148
1126
|
|
|
1149
1127
|
if (proc) {
|
|
1150
1128
|
assert(Status.any(stored.status));
|
|
1151
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1129
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1152
1130
|
stored.item = .None;
|
|
1153
1131
|
|
|
1154
1132
|
// FIXME wtf?
|
|
@@ -1156,21 +1134,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1156
1134
|
// side.active.volatiles.Nightmare = false;
|
|
1157
1135
|
if (stored.item == .MiracleBerry) {
|
|
1158
1136
|
side.active.volatiles.Confusion = false;
|
|
1159
|
-
try options.log.end(ident, .Confusion);
|
|
1137
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1160
1138
|
}
|
|
1161
1139
|
stored.status = 0;
|
|
1162
1140
|
|
|
1163
1141
|
// FIXME recalc stats
|
|
1164
1142
|
|
|
1165
|
-
try options.log.curestatus(ident, stored.status, .Message);
|
|
1143
|
+
try options.log.curestatus(.{ ident, stored.status, .Message });
|
|
1166
1144
|
}
|
|
1167
1145
|
} else if (num == @intFromEnum(Item.BitterBerry)) {
|
|
1168
1146
|
if (side.active.volatiles.Confusion) {
|
|
1169
|
-
try options.log.enditem(ident, stored.item, .Eat);
|
|
1147
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1170
1148
|
stored.item = .None;
|
|
1171
1149
|
|
|
1172
1150
|
side.active.volatiles.Confusion = false;
|
|
1173
|
-
try options.log.end(ident, .Confusion);
|
|
1151
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1174
1152
|
}
|
|
1175
1153
|
}
|
|
1176
1154
|
}
|
|
@@ -1182,12 +1160,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1182
1160
|
var volatiles = &side.active.volatiles;
|
|
1183
1161
|
|
|
1184
1162
|
if (volatiles.Encore) {
|
|
1185
|
-
assert(volatiles.encore > 0);
|
|
1186
1163
|
volatiles.encore -= 1;
|
|
1187
1164
|
|
|
1188
1165
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
1189
1166
|
volatiles.Encore = false;
|
|
1190
|
-
try options.log.end(battle.active(player), .Encore);
|
|
1167
|
+
try options.log.end(.{ battle.active(player), .Encore });
|
|
1191
1168
|
}
|
|
1192
1169
|
}
|
|
1193
1170
|
}
|
|
@@ -1204,12 +1181,11 @@ fn updateSideCondition(
|
|
|
1204
1181
|
) !void {
|
|
1205
1182
|
var side = battle.side(player);
|
|
1206
1183
|
if (@field(side.conditions, key)) {
|
|
1207
|
-
assert(@field(side.conditions, value) > 0);
|
|
1208
1184
|
@field(side.conditions, value) -= 1;
|
|
1209
1185
|
|
|
1210
1186
|
if (@field(side.conditions, value) == 0) {
|
|
1211
1187
|
@field(side.conditions, key) = false;
|
|
1212
|
-
try options.log.sideend(player, @field(protocol.Side, key));
|
|
1188
|
+
try options.log.sideend(.{ player, @field(protocol.Side, key) });
|
|
1213
1189
|
}
|
|
1214
1190
|
}
|
|
1215
1191
|
}
|
|
@@ -1217,16 +1193,16 @@ fn updateSideCondition(
|
|
|
1217
1193
|
fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result {
|
|
1218
1194
|
battle.turn += 1;
|
|
1219
1195
|
|
|
1220
|
-
if (
|
|
1221
|
-
try options.log.tie();
|
|
1222
|
-
return
|
|
1196
|
+
if (pkmn.options.mod and battle.turn >= 1000) {
|
|
1197
|
+
try options.log.tie(.{});
|
|
1198
|
+
return .Tie;
|
|
1223
1199
|
} else if (battle.turn >= 65535) {
|
|
1224
|
-
return
|
|
1200
|
+
return .Error;
|
|
1225
1201
|
}
|
|
1226
1202
|
|
|
1227
|
-
try options.log.turn(battle.turn);
|
|
1203
|
+
try options.log.turn(.{battle.turn});
|
|
1228
1204
|
|
|
1229
|
-
return
|
|
1205
|
+
return .Default;
|
|
1230
1206
|
}
|
|
1231
1207
|
|
|
1232
1208
|
pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1240,7 +1216,7 @@ pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytyp
|
|
|
1240
1216
|
}
|
|
1241
1217
|
} else {
|
|
1242
1218
|
foe.active.volatiles.rage +|= 1;
|
|
1243
|
-
try options.log.activate(battle.active(player), .Rage);
|
|
1219
|
+
try options.log.activate(.{ battle.active(player), .Rage });
|
|
1244
1220
|
}
|
|
1245
1221
|
}
|
|
1246
1222
|
}
|
|
@@ -1263,7 +1239,7 @@ pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype)
|
|
|
1263
1239
|
|
|
1264
1240
|
if (foe.active.volatiles.DestinyBond) {
|
|
1265
1241
|
battle.side(player).stored().hp = 0;
|
|
1266
|
-
try options.log.activate(battle.active(player.foe()), .DestinyBond);
|
|
1242
|
+
try options.log.activate(.{ battle.active(player.foe()), .DestinyBond });
|
|
1267
1243
|
}
|
|
1268
1244
|
return true;
|
|
1269
1245
|
}
|
|
@@ -1276,14 +1252,14 @@ pub const Effects = struct {
|
|
|
1276
1252
|
const gender: u8 = @intFromEnum(side.stored().dvs.gender);
|
|
1277
1253
|
const foe_gender: u8 = @intFromEnum(foe.stored().dvs.gender);
|
|
1278
1254
|
if (gender + foe_gender != 1) {
|
|
1279
|
-
return options.log.immune(battle.active(player.foe()), .None);
|
|
1255
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1280
1256
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1281
|
-
try options.log.lastmiss();
|
|
1282
|
-
return options.log.miss(battle.active(player));
|
|
1257
|
+
try options.log.lastmiss(.{});
|
|
1258
|
+
return options.log.miss(.{battle.active(player)});
|
|
1283
1259
|
}
|
|
1284
1260
|
|
|
1285
1261
|
foe.active.volatiles.Attract = true;
|
|
1286
|
-
try options.log.start(battle.active(player.foe()), .Attract);
|
|
1262
|
+
try options.log.start(.{ battle.active(player.foe()), .Attract });
|
|
1287
1263
|
}
|
|
1288
1264
|
|
|
1289
1265
|
pub fn batonPass(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1295,14 +1271,32 @@ pub const Effects = struct {
|
|
|
1295
1271
|
}
|
|
1296
1272
|
|
|
1297
1273
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
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
|
+
}
|
|
1306
1300
|
}
|
|
1307
1301
|
|
|
1308
1302
|
pub const bide = struct {
|
|
@@ -1314,18 +1308,11 @@ pub const Effects = struct {
|
|
|
1314
1308
|
|
|
1315
1309
|
if (!volatiles.Bide) return;
|
|
1316
1310
|
|
|
1317
|
-
assert(volatiles.attacks > 0);
|
|
1318
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1319
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1320
|
-
// } else {
|
|
1321
1311
|
volatiles.attacks -= 1;
|
|
1322
|
-
|
|
1323
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1324
|
-
|
|
1325
|
-
if (volatiles.attacks != 0) return options.log.activate(ident, .Bide);
|
|
1312
|
+
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1326
1313
|
|
|
1327
1314
|
volatiles.Bide = false;
|
|
1328
|
-
try options.log.end(ident, .Bide);
|
|
1315
|
+
try options.log.end(.{ ident, .Bide });
|
|
1329
1316
|
|
|
1330
1317
|
state.damage = volatiles.bide *% 2;
|
|
1331
1318
|
volatiles.bide = 0;
|
|
@@ -1359,11 +1346,11 @@ pub const Effects = struct {
|
|
|
1359
1346
|
if (Status.any(foe_stored.status)) {
|
|
1360
1347
|
if (Status.is(foe_stored.status, .FRZ)) {
|
|
1361
1348
|
assert(Move.get(state.move).type == .Fire or state.move == .TriAttack);
|
|
1362
|
-
try options.log.curestatus(
|
|
1349
|
+
try options.log.curestatus(.{
|
|
1363
1350
|
battle.active(player.foe()),
|
|
1364
1351
|
foe_stored.status,
|
|
1365
1352
|
.Message,
|
|
1366
|
-
);
|
|
1353
|
+
});
|
|
1367
1354
|
foe_stored.status = 0;
|
|
1368
1355
|
}
|
|
1369
1356
|
return;
|
|
@@ -1378,7 +1365,8 @@ pub const Effects = struct {
|
|
|
1378
1365
|
|
|
1379
1366
|
foe_stored.status = Status.init(.BRN);
|
|
1380
1367
|
foe.active.stats.atk = @max(foe.active.stats.atk / 2, 1);
|
|
1381
|
-
|
|
1368
|
+
|
|
1369
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
1382
1370
|
|
|
1383
1371
|
// TODO: check for status berry
|
|
1384
1372
|
}
|
|
@@ -1398,7 +1386,7 @@ pub const Effects = struct {
|
|
|
1398
1386
|
for (side.active.moves) |m| {
|
|
1399
1387
|
if (convertible(side.active, m.id)) n += 1;
|
|
1400
1388
|
}
|
|
1401
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
1389
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1402
1390
|
|
|
1403
1391
|
side.active.types.type1 = try Rolls.conversion(battle, player, n, options);
|
|
1404
1392
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1408,9 +1396,9 @@ pub const Effects = struct {
|
|
|
1408
1396
|
var side = battle.side(player);
|
|
1409
1397
|
|
|
1410
1398
|
const last = battle.foe(player).lastMove(false);
|
|
1411
|
-
if (last == .None) return options.log.fail(battle.active(player), .None);
|
|
1399
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1412
1400
|
const move = Move.get(last);
|
|
1413
|
-
if (move.type == .@"???") return options.log.fail(battle.active(player), .None);
|
|
1401
|
+
if (move.type == .@"???") return options.log.fail(.{ battle.active(player), .None });
|
|
1414
1402
|
|
|
1415
1403
|
side.active.types.type1 = try Rolls.conversion2(battle, player, move.type, options);
|
|
1416
1404
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1439,7 +1427,8 @@ pub const Effects = struct {
|
|
|
1439
1427
|
if (move.type.special() != (last == .MirrorCoat)) return;
|
|
1440
1428
|
if (state.damage == 0) return;
|
|
1441
1429
|
|
|
1442
|
-
|
|
1430
|
+
// NOTE: llvm/llvm-project#58557
|
|
1431
|
+
state.damage = smul(state.damage, 2);
|
|
1443
1432
|
state.miss = false;
|
|
1444
1433
|
}
|
|
1445
1434
|
|
|
@@ -1452,41 +1441,41 @@ pub const Effects = struct {
|
|
|
1452
1441
|
|
|
1453
1442
|
if (side.active.types.includes(.Ghost)) {
|
|
1454
1443
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1455
|
-
try options.log.lastmiss();
|
|
1456
|
-
return options.log.miss(battle.active(player));
|
|
1444
|
+
try options.log.lastmiss(.{});
|
|
1445
|
+
return options.log.miss(.{battle.active(player)});
|
|
1457
1446
|
} else if (foe.active.volatiles.Substitute) {
|
|
1458
|
-
return options.log.
|
|
1447
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1459
1448
|
} else if (foe.active.volatiles.Curse) {
|
|
1460
|
-
return options.log.fail(foe_ident, .None);
|
|
1449
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1461
1450
|
}
|
|
1462
1451
|
|
|
1463
1452
|
foe.active.volatiles.Curse = true;
|
|
1464
|
-
try options.log.start(foe_ident, .Curse);
|
|
1453
|
+
try options.log.start(.{ foe_ident, .Curse });
|
|
1465
1454
|
|
|
1466
1455
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1467
|
-
try options.log.damage(ident, side.stored(), .Curse);
|
|
1456
|
+
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1468
1457
|
} else {
|
|
1469
|
-
|
|
1470
|
-
|
|
1458
|
+
const stats = &side.active.stats;
|
|
1459
|
+
const boosts = &side.active.boosts;
|
|
1471
1460
|
|
|
1472
1461
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1473
1462
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
1474
|
-
if (max_atk and max_def) return options.log.fail(ident, .None);
|
|
1463
|
+
if (max_atk and max_def) return options.log.fail(.{ ident, .None });
|
|
1475
1464
|
|
|
1476
1465
|
if (!(boosts.spe == -6 or stats.spe == 1)) {
|
|
1477
1466
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
1478
1467
|
try unboostStat(battle, side, stats, boosts, "spe", 1);
|
|
1479
|
-
try options.log.boost(ident, .Speed, @as(i8, -1));
|
|
1468
|
+
try options.log.boost(.{ ident, .Speed, @as(i8, -1) });
|
|
1480
1469
|
}
|
|
1481
1470
|
if (!max_atk) {
|
|
1482
1471
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
1483
1472
|
try boostStat(battle, side, stats, boosts, "atk", 1);
|
|
1484
|
-
try options.log.boost(ident, .Attack, @as(i8, 1));
|
|
1473
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 1) });
|
|
1485
1474
|
}
|
|
1486
1475
|
if (!max_def) {
|
|
1487
1476
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
1488
1477
|
try boostStat(battle, side, stats, boosts, "def", 1);
|
|
1489
|
-
try options.log.boost(ident, .Defense, @as(i8, 1));
|
|
1478
|
+
try options.log.boost(.{ ident, .Defense, @as(i8, 1) });
|
|
1490
1479
|
}
|
|
1491
1480
|
}
|
|
1492
1481
|
}
|
|
@@ -1498,14 +1487,13 @@ pub const Effects = struct {
|
|
|
1498
1487
|
pub fn defrost(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1499
1488
|
var side = battle.side(player);
|
|
1500
1489
|
assert(Status.is(side.stored().status, .FRZ));
|
|
1501
|
-
|
|
1502
|
-
try options.log.curestatus(battle.active(player), side.stored().status, .Message);
|
|
1490
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1503
1491
|
side.stored().status = 0;
|
|
1504
1492
|
}
|
|
1505
1493
|
|
|
1506
1494
|
pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1507
1495
|
battle.side(player).active.volatiles.DestinyBond = true;
|
|
1508
|
-
try options.log.singlemove(battle.active(player), Move.DestinyBond);
|
|
1496
|
+
try options.log.singlemove(.{ battle.active(player), Move.DestinyBond });
|
|
1509
1497
|
}
|
|
1510
1498
|
|
|
1511
1499
|
pub fn disable(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -1513,14 +1501,10 @@ pub const Effects = struct {
|
|
|
1513
1501
|
var volatiles = &foe.active.volatiles;
|
|
1514
1502
|
const foe_ident = battle.active(player.foe());
|
|
1515
1503
|
|
|
1516
|
-
if (volatiles.disable.move != 0) {
|
|
1517
|
-
return options.log.fail(foe_ident, .None);
|
|
1518
|
-
}
|
|
1504
|
+
if (volatiles.disable.move != 0) return options.log.fail(.{ foe_ident, .None });
|
|
1519
1505
|
|
|
1520
1506
|
const last = battle.foe(player).lastMove(false);
|
|
1521
|
-
if (last == .None or last == .Struggle) {
|
|
1522
|
-
return options.log.fail(foe_ident, .None);
|
|
1523
|
-
}
|
|
1507
|
+
if (last == .None or last == .Struggle) return options.log.fail(.{ foe_ident, .None });
|
|
1524
1508
|
|
|
1525
1509
|
var slot: u3 = 0;
|
|
1526
1510
|
for (foe.active.moves) |m| {
|
|
@@ -1529,37 +1513,41 @@ pub const Effects = struct {
|
|
|
1529
1513
|
if (m.id == last) break;
|
|
1530
1514
|
}
|
|
1531
1515
|
const move = foe.active.move(slot);
|
|
1532
|
-
if (move.pp == 0) return options.log.fail(foe_ident, .None);
|
|
1516
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1533
1517
|
|
|
1534
1518
|
volatiles.disable.move = slot;
|
|
1535
1519
|
volatiles.disable.duration = Rolls.disableDuration(battle, player, options);
|
|
1536
1520
|
|
|
1537
|
-
try options.log.
|
|
1521
|
+
try options.log.start(.{ foe_ident, .Disable, move.id });
|
|
1538
1522
|
}
|
|
1539
1523
|
|
|
1540
1524
|
pub const double = struct {
|
|
1541
1525
|
pub fn flying(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1542
|
-
if (battle.foe(player).active.volatiles.Flying)
|
|
1526
|
+
if (!battle.foe(player).active.volatiles.Flying) return;
|
|
1527
|
+
state.damage = smul(state.damage, 2);
|
|
1543
1528
|
}
|
|
1544
1529
|
|
|
1545
1530
|
pub fn underground(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1546
|
-
if (battle.foe(player).active.volatiles.Underground)
|
|
1531
|
+
if (!battle.foe(player).active.volatiles.Underground) return;
|
|
1532
|
+
state.damage = smul(state.damage, 2);
|
|
1547
1533
|
}
|
|
1548
1534
|
|
|
1549
1535
|
pub fn minimize(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1550
|
-
if (battle.foe(player).active.volatiles.minimized)
|
|
1536
|
+
if (!battle.foe(player).active.volatiles.minimized) return;
|
|
1537
|
+
state.damage = smul(state.damage, 2);
|
|
1551
1538
|
}
|
|
1552
1539
|
};
|
|
1553
1540
|
|
|
1554
1541
|
pub fn drainHP(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1555
1542
|
var stored = battle.side(player).stored();
|
|
1543
|
+
const ident = battle.active(player);
|
|
1556
1544
|
|
|
1557
1545
|
assert(state.damage != 0);
|
|
1558
1546
|
|
|
1559
1547
|
if (stored.hp == stored.stats.hp) return;
|
|
1560
1548
|
stored.hp = @min(stored.stats.hp, stored.hp + @max(state.damage / 2, 1));
|
|
1561
1549
|
|
|
1562
|
-
try options.log.
|
|
1550
|
+
try options.log.heal(.{ ident, stored, .Drain, battle.active(player.foe()) });
|
|
1563
1551
|
}
|
|
1564
1552
|
|
|
1565
1553
|
pub fn encore(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1570,8 +1558,8 @@ pub const Effects = struct {
|
|
|
1570
1558
|
const last = battle.foe(player).lastMove(true);
|
|
1571
1559
|
const failed = volatiles.Encore or
|
|
1572
1560
|
(last == .None or last == .Struggle or
|
|
1573
|
-
|
|
1574
|
-
if (failed) return options.log.fail(foe_ident, .None);
|
|
1561
|
+
last == .Encore or .last == .MirrorMove);
|
|
1562
|
+
if (failed) return options.log.fail(.{ foe_ident, .None });
|
|
1575
1563
|
|
|
1576
1564
|
var slot: u3 = 0;
|
|
1577
1565
|
for (foe.active.moves) |m| {
|
|
@@ -1580,7 +1568,7 @@ pub const Effects = struct {
|
|
|
1580
1568
|
if (m.id == last) break;
|
|
1581
1569
|
}
|
|
1582
1570
|
const move = foe.active.move(slot);
|
|
1583
|
-
if (move.pp == 0) return options.log.fail(foe_ident, .None);
|
|
1571
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1584
1572
|
|
|
1585
1573
|
volatiles.Encore = true;
|
|
1586
1574
|
volatiles.encore = Rolls.encoreDuration(battle, player, options);
|
|
@@ -1588,11 +1576,19 @@ pub const Effects = struct {
|
|
|
1588
1576
|
// TODO ???
|
|
1589
1577
|
_ = state;
|
|
1590
1578
|
|
|
1591
|
-
try options.log.
|
|
1579
|
+
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1592
1580
|
}
|
|
1593
1581
|
|
|
1594
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1595
|
-
|
|
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;
|
|
1596
1592
|
}
|
|
1597
1593
|
|
|
1598
1594
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1642,12 +1638,11 @@ pub const Effects = struct {
|
|
|
1642
1638
|
var side = battle.side(player);
|
|
1643
1639
|
|
|
1644
1640
|
if (side.active.volatiles.FocusEnergy) {
|
|
1645
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
1646
|
-
return;
|
|
1641
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
1647
1642
|
}
|
|
1648
1643
|
|
|
1649
1644
|
side.active.volatiles.FocusEnergy = true;
|
|
1650
|
-
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1645
|
+
try options.log.start(.{ battle.active(player), .FocusEnergy });
|
|
1651
1646
|
}
|
|
1652
1647
|
|
|
1653
1648
|
var SLOTS: [5]u4 = .{ 0, 0, 0, 0, 0 };
|
|
@@ -1657,7 +1652,7 @@ pub const Effects = struct {
|
|
|
1657
1652
|
|
|
1658
1653
|
const foe = battle.foe(player);
|
|
1659
1654
|
|
|
1660
|
-
if (state.first) return options.log.fail(battle.active(player), .None);
|
|
1655
|
+
if (state.first) return options.log.fail(.{ battle.active(player), .None });
|
|
1661
1656
|
|
|
1662
1657
|
var n: u4 = 0;
|
|
1663
1658
|
var i: u4 = 0;
|
|
@@ -1671,7 +1666,7 @@ pub const Effects = struct {
|
|
|
1671
1666
|
}
|
|
1672
1667
|
}
|
|
1673
1668
|
|
|
1674
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
1669
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1675
1670
|
|
|
1676
1671
|
const slot = try Rolls.forceSwitch(battle, player, SLOTS[0..i], n, options);
|
|
1677
1672
|
try switchIn(battle, player.foe(), slot, .Drag, options);
|
|
@@ -1681,14 +1676,14 @@ pub const Effects = struct {
|
|
|
1681
1676
|
var foe = battle.foe(player);
|
|
1682
1677
|
|
|
1683
1678
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1684
|
-
try options.log.lastmiss();
|
|
1685
|
-
return options.log.miss(battle.active(player));
|
|
1679
|
+
try options.log.lastmiss(.{});
|
|
1680
|
+
return options.log.miss(.{battle.active(player)});
|
|
1686
1681
|
} else if (foe.active.volatiles.Foresight) {
|
|
1687
|
-
return options.log.fail(battle.active(player), .None);
|
|
1682
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
1688
1683
|
}
|
|
1689
1684
|
|
|
1690
1685
|
foe.active.volatiles.Foresight = true;
|
|
1691
|
-
try options.log.start(battle.active(player.foe()), .FocusEnergy);
|
|
1686
|
+
try options.log.start(.{ battle.active(player.foe()), .FocusEnergy });
|
|
1692
1687
|
}
|
|
1693
1688
|
|
|
1694
1689
|
pub fn freezeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1708,10 +1703,10 @@ pub const Effects = struct {
|
|
|
1708
1703
|
if (!state.proc) return;
|
|
1709
1704
|
if (foe.conditions.Safeguard) return;
|
|
1710
1705
|
// Freeze Clause Mod
|
|
1711
|
-
if (
|
|
1706
|
+
if (pkmn.options.mod) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1712
1707
|
|
|
1713
1708
|
foe_stored.status = Status.init(.FRZ);
|
|
1714
|
-
try options.log.status(foe_ident, foe_stored.status, .None);
|
|
1709
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
1715
1710
|
|
|
1716
1711
|
// TODO: check for status berry
|
|
1717
1712
|
|
|
@@ -1719,8 +1714,26 @@ pub const Effects = struct {
|
|
|
1719
1714
|
foe.active.volatiles.frozen = true;
|
|
1720
1715
|
}
|
|
1721
1716
|
|
|
1722
|
-
|
|
1723
|
-
|
|
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
|
+
}
|
|
1724
1737
|
}
|
|
1725
1738
|
|
|
1726
1739
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1746,7 +1759,7 @@ pub const Effects = struct {
|
|
|
1746
1759
|
inline for (STATS) |s| @field(foe.active.stats, s) = @field(stats, s);
|
|
1747
1760
|
statusModify(foe.stored().status, &foe.active.stats);
|
|
1748
1761
|
|
|
1749
|
-
try options.log.clearallboost();
|
|
1762
|
+
try options.log.clearallboost(.{});
|
|
1750
1763
|
}
|
|
1751
1764
|
|
|
1752
1765
|
pub fn heal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1754,7 +1767,7 @@ pub const Effects = struct {
|
|
|
1754
1767
|
var stored = side.stored();
|
|
1755
1768
|
const ident = battle.active(player);
|
|
1756
1769
|
|
|
1757
|
-
if (stored.stats.hp == stored.hp) return options.log.fail(ident, .None);
|
|
1770
|
+
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident, .None });
|
|
1758
1771
|
|
|
1759
1772
|
const rest = state.move == .Rest;
|
|
1760
1773
|
if (rest) {
|
|
@@ -1763,19 +1776,19 @@ pub const Effects = struct {
|
|
|
1763
1776
|
side.active.volatiles.Toxic = false;
|
|
1764
1777
|
stored.status = Status.slf(2);
|
|
1765
1778
|
recomputeStats(battle, side);
|
|
1766
|
-
try options.log.
|
|
1779
|
+
try options.log.status(.{ ident, stored.status, .From, Move.Rest });
|
|
1767
1780
|
stored.hp = stored.stats.hp;
|
|
1768
1781
|
} else {
|
|
1769
1782
|
stored.hp = @min(stored.stats.hp, stored.hp + (stored.stats.hp / 2));
|
|
1770
1783
|
}
|
|
1771
|
-
try options.log.heal(ident, stored, if (rest) Heal.Silent else Heal.None);
|
|
1784
|
+
try options.log.heal(.{ ident, stored, if (rest) Heal.Silent else Heal.None });
|
|
1772
1785
|
}
|
|
1773
1786
|
|
|
1774
1787
|
pub fn healBell(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1775
1788
|
var side = battle.side(player);
|
|
1776
1789
|
side.active.volatiles.Nightmare = false;
|
|
1777
1790
|
|
|
1778
|
-
try options.log.cureteam(battle.active(player));
|
|
1791
|
+
try options.log.cureteam(.{battle.active(player)});
|
|
1779
1792
|
for (&side.pokemon) |*p| p.status = 0;
|
|
1780
1793
|
|
|
1781
1794
|
// We technically only need to recompute stats if the active Pokémon was burned/paralyzed,
|
|
@@ -1791,7 +1804,7 @@ pub const Effects = struct {
|
|
|
1791
1804
|
|
|
1792
1805
|
pub fn hyperBeam(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1793
1806
|
battle.side(player).active.volatiles.Recharging = true;
|
|
1794
|
-
try options.log.mustrecharge(battle.active(player));
|
|
1807
|
+
try options.log.mustrecharge(.{battle.active(player)});
|
|
1795
1808
|
}
|
|
1796
1809
|
|
|
1797
1810
|
pub fn leechSeed(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1801,12 +1814,12 @@ pub const Effects = struct {
|
|
|
1801
1814
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
1802
1815
|
const invulnerable = foe.active.volatiles.Underground or foe.active.volatiles.Flying;
|
|
1803
1816
|
if (!invulnerable and foe.active.types.includes(.Grass)) {
|
|
1804
|
-
return options.log.immune(battle.active(player.foe()), .None);
|
|
1817
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1805
1818
|
}
|
|
1806
1819
|
try checkHit(battle, player, state, options);
|
|
1807
1820
|
if (state.miss) {
|
|
1808
|
-
try options.log.lastmiss();
|
|
1809
|
-
return options.log.miss(battle.active(player));
|
|
1821
|
+
try options.log.lastmiss(.{});
|
|
1822
|
+
return options.log.miss(.{battle.active(player)});
|
|
1810
1823
|
}
|
|
1811
1824
|
if (foe.active.volatiles.LeechSeed) return;
|
|
1812
1825
|
} else {
|
|
@@ -1814,14 +1827,14 @@ pub const Effects = struct {
|
|
|
1814
1827
|
const miss =
|
|
1815
1828
|
state.miss or foe.active.types.includes(.Grass) or foe.active.volatiles.LeechSeed;
|
|
1816
1829
|
if (miss) {
|
|
1817
|
-
try options.log.lastmiss();
|
|
1818
|
-
return options.log.miss(battle.active(player));
|
|
1830
|
+
try options.log.lastmiss(.{});
|
|
1831
|
+
return options.log.miss(.{battle.active(player)});
|
|
1819
1832
|
}
|
|
1820
1833
|
}
|
|
1821
1834
|
|
|
1822
1835
|
foe.active.volatiles.LeechSeed = true;
|
|
1823
1836
|
|
|
1824
|
-
try options.log.start(battle.active(player.foe()), .LeechSeed);
|
|
1837
|
+
try options.log.start(.{ battle.active(player.foe()), .LeechSeed });
|
|
1825
1838
|
}
|
|
1826
1839
|
|
|
1827
1840
|
pub fn lockOn(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1829,20 +1842,20 @@ pub const Effects = struct {
|
|
|
1829
1842
|
const foe_ident = battle.active(player.foe());
|
|
1830
1843
|
|
|
1831
1844
|
if (foe.active.volatiles.Substitute) {
|
|
1832
|
-
return options.log.
|
|
1845
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1833
1846
|
} else if (state.miss) {
|
|
1834
|
-
return options.log.fail(foe_ident, .None);
|
|
1847
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1835
1848
|
}
|
|
1836
1849
|
|
|
1837
1850
|
foe.active.volatiles.LockOn = true;
|
|
1838
|
-
try options.log.activate(battle.active(player), .LockOn);
|
|
1851
|
+
try options.log.activate(.{ battle.active(player), .LockOn });
|
|
1839
1852
|
}
|
|
1840
1853
|
|
|
1841
1854
|
const MAGNITUDE_POWER = [_]u8{ 10, 30, 50, 70, 90, 110, 150 };
|
|
1842
1855
|
|
|
1843
1856
|
pub fn magnitude(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1844
1857
|
const num = try Rolls.magnitude(battle, player, options);
|
|
1845
|
-
try options.log.
|
|
1858
|
+
try options.log.activate(.{ battle.active(player), .Magnitude, num });
|
|
1846
1859
|
state.bp = MAGNITUDE_POWER[num - 4];
|
|
1847
1860
|
}
|
|
1848
1861
|
|
|
@@ -1850,11 +1863,11 @@ pub const Effects = struct {
|
|
|
1850
1863
|
var foe = battle.foe(player);
|
|
1851
1864
|
|
|
1852
1865
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1853
|
-
if (!showdown) return options.log.fail(battle.active(player.foe()), .None);
|
|
1854
|
-
try options.log.lastmiss();
|
|
1855
|
-
return options.log.miss(battle.active(player));
|
|
1866
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1867
|
+
try options.log.lastmiss(.{});
|
|
1868
|
+
return options.log.miss(.{battle.active(player)});
|
|
1856
1869
|
} else if (foe.active.volatiles.Trapped) {
|
|
1857
|
-
return options.log.fail(battle.active(player.foe()), .None);
|
|
1870
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1858
1871
|
}
|
|
1859
1872
|
|
|
1860
1873
|
foe.active.volatiles.Trapped = true;
|
|
@@ -1881,7 +1894,29 @@ pub const Effects = struct {
|
|
|
1881
1894
|
}
|
|
1882
1895
|
|
|
1883
1896
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1884
|
-
|
|
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 });
|
|
1885
1920
|
}
|
|
1886
1921
|
|
|
1887
1922
|
pub fn mirrorMove(
|
|
@@ -1898,11 +1933,11 @@ pub const Effects = struct {
|
|
|
1898
1933
|
|
|
1899
1934
|
const last = foe.lastMove(false);
|
|
1900
1935
|
|
|
1901
|
-
if (last == .None) return options.log.fail(battle.active(player), .None);
|
|
1936
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1902
1937
|
const knows = knows: for (side.active.moves) |m| {
|
|
1903
1938
|
if (last == m.id) break :knows true;
|
|
1904
1939
|
} else false;
|
|
1905
|
-
if (knows) return options.log.fail(battle.active(player), .None);
|
|
1940
|
+
if (knows) return options.log.fail(.{ battle.active(player), .None });
|
|
1906
1941
|
|
|
1907
1942
|
// if (wPlayerCharging) ???
|
|
1908
1943
|
// wPlayerCharging = 1
|
|
@@ -1916,17 +1951,17 @@ pub const Effects = struct {
|
|
|
1916
1951
|
pub fn mist(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1917
1952
|
var side = battle.side(player);
|
|
1918
1953
|
|
|
1919
|
-
if (side.active.volatiles.Mist) return options.log.fail(battle.active(player), .None);
|
|
1954
|
+
if (side.active.volatiles.Mist) return options.log.fail(.{ battle.active(player), .None });
|
|
1920
1955
|
side.active.volatiles.Mist = true;
|
|
1921
1956
|
|
|
1922
|
-
try options.log.start(battle.active(player), .Mist);
|
|
1957
|
+
try options.log.start(.{ battle.active(player), .Mist });
|
|
1923
1958
|
}
|
|
1924
1959
|
|
|
1925
1960
|
pub fn weatherHeal(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1926
1961
|
var stored = battle.side(player).stored();
|
|
1927
1962
|
const ident = battle.active(player);
|
|
1928
1963
|
|
|
1929
|
-
if (stored.hp == stored.stats.hp) return options.log.fail(ident, .None);
|
|
1964
|
+
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident, .None });
|
|
1930
1965
|
|
|
1931
1966
|
stored.hp = switch (battle.field.weather) {
|
|
1932
1967
|
.Sun => stored.stats.hp,
|
|
@@ -1934,7 +1969,7 @@ pub const Effects = struct {
|
|
|
1934
1969
|
else => @min(stored.stats.hp, stored.hp + (stored.stats.hp / 4)),
|
|
1935
1970
|
};
|
|
1936
1971
|
|
|
1937
|
-
try options.log.heal(ident, stored,
|
|
1972
|
+
try options.log.heal(.{ ident, stored, .None });
|
|
1938
1973
|
}
|
|
1939
1974
|
|
|
1940
1975
|
pub fn multiHit(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1947,17 +1982,17 @@ pub const Effects = struct {
|
|
|
1947
1982
|
const foe_ident = battle.active(player.foe());
|
|
1948
1983
|
|
|
1949
1984
|
if (foe_volatiles.Flying or foe_volatiles.Underground) {
|
|
1950
|
-
if (!showdown) return options.log.fail(battle.active(player.foe()), .None);
|
|
1951
|
-
try options.log.lastmiss();
|
|
1952
|
-
return options.log.miss(battle.active(player));
|
|
1985
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1986
|
+
try options.log.lastmiss(.{});
|
|
1987
|
+
return options.log.miss(.{battle.active(player)});
|
|
1953
1988
|
} else if (foe_volatiles.Substitute) {
|
|
1954
|
-
return options.log.
|
|
1989
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1955
1990
|
} else if (!Status.is(foe.stored().status, .SLP) or foe_volatiles.Nightmare) {
|
|
1956
|
-
return options.log.fail(foe_ident, .None);
|
|
1991
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1957
1992
|
}
|
|
1958
1993
|
|
|
1959
1994
|
foe_volatiles.Nightmare = true;
|
|
1960
|
-
try options.log.start(battle.active(player.foe()), .Nightmare);
|
|
1995
|
+
try options.log.start(.{ battle.active(player.foe()), .Nightmare });
|
|
1961
1996
|
}
|
|
1962
1997
|
|
|
1963
1998
|
pub const ohko = struct {
|
|
@@ -1981,9 +2016,9 @@ pub const Effects = struct {
|
|
|
1981
2016
|
const foe_ident = battle.active(player.foe());
|
|
1982
2017
|
|
|
1983
2018
|
state.damage = 0;
|
|
1984
|
-
if (state.immune()) return options.log.immune(foe_ident, .None);
|
|
2019
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
1985
2020
|
const delta = side.stored().level -| foe.stored().level;
|
|
1986
|
-
if (delta == 0) return options.log.immune(foe_ident, .OHKO);
|
|
2021
|
+
if (delta == 0) return options.log.immune(.{ foe_ident, .OHKO });
|
|
1987
2022
|
|
|
1988
2023
|
state.accuracy = 2 * delta +| Gen12.percent(30);
|
|
1989
2024
|
try checkHit(battle, player, state, options);
|
|
@@ -1999,37 +2034,39 @@ pub const Effects = struct {
|
|
|
1999
2034
|
const ident = battle.active(player);
|
|
2000
2035
|
const foe_ident = battle.active(player.foe());
|
|
2001
2036
|
|
|
2002
|
-
if (state.miss) return options.log.fail(foe_ident, .None);
|
|
2037
|
+
if (state.miss) return options.log.fail(.{ foe_ident, .None });
|
|
2003
2038
|
if (foe.active.volatiles.Substitute) {
|
|
2004
|
-
return options.log.
|
|
2039
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2005
2040
|
}
|
|
2006
2041
|
|
|
2007
2042
|
const avg = @max((foe.stored().hp + side.stored().hp) / 2, 1);
|
|
2008
2043
|
foe.stored().hp -= avg;
|
|
2009
2044
|
side.stored().hp = avg;
|
|
2010
2045
|
|
|
2011
|
-
try options.log.sethp(foe_ident, foe.stored(), .Silent);
|
|
2012
|
-
try options.log.sethp(ident, side.stored(), .None);
|
|
2046
|
+
try options.log.sethp(.{ foe_ident, foe.stored(), .Silent });
|
|
2047
|
+
try options.log.sethp(.{ ident, side.stored(), .None });
|
|
2013
2048
|
}
|
|
2014
2049
|
|
|
2015
2050
|
pub fn paralyze(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2016
2051
|
var foe = battle.foe(player);
|
|
2017
2052
|
const foe_ident = battle.active(player.foe());
|
|
2018
2053
|
|
|
2019
|
-
if (Status.is(foe.stored().status, .PAR))
|
|
2020
|
-
|
|
2021
|
-
|
|
2054
|
+
if (Status.is(foe.stored().status, .PAR)) {
|
|
2055
|
+
return options.log.fail(.{ foe_ident, .Paralysis });
|
|
2056
|
+
}
|
|
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 });
|
|
2022
2059
|
if (state.miss) {
|
|
2023
|
-
try options.log.lastmiss();
|
|
2024
|
-
return options.log.miss(battle.active(player));
|
|
2060
|
+
try options.log.lastmiss(.{});
|
|
2061
|
+
return options.log.miss(.{battle.active(player)});
|
|
2025
2062
|
} else if (foe.active.volatiles.Substitute) {
|
|
2026
|
-
return options.log.
|
|
2063
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2027
2064
|
}
|
|
2028
2065
|
|
|
2029
2066
|
foe.stored().status = Status.init(.PAR);
|
|
2030
2067
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2031
2068
|
|
|
2032
|
-
try options.log.status(foe_ident, foe.stored().status, .None);
|
|
2069
|
+
try options.log.status(.{ foe_ident, foe.stored().status, .None });
|
|
2033
2070
|
}
|
|
2034
2071
|
|
|
2035
2072
|
pub fn paralyzeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2044,14 +2081,15 @@ pub const Effects = struct {
|
|
|
2044
2081
|
|
|
2045
2082
|
foe_stored.status = Status.init(.PAR);
|
|
2046
2083
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2047
|
-
|
|
2084
|
+
|
|
2085
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2048
2086
|
|
|
2049
2087
|
// TODO: check for status berry
|
|
2050
2088
|
}
|
|
2051
2089
|
|
|
2052
2090
|
pub fn payDay(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2053
2091
|
if (!showdown or !battle.foe(player).active.volatiles.Substitute) {
|
|
2054
|
-
try options.log.fieldactivate(); // FIXME .PayDay
|
|
2092
|
+
try options.log.fieldactivate(.{}); // FIXME .PayDay
|
|
2055
2093
|
}
|
|
2056
2094
|
}
|
|
2057
2095
|
|
|
@@ -2060,17 +2098,17 @@ pub const Effects = struct {
|
|
|
2060
2098
|
var foe = battle.foe(player);
|
|
2061
2099
|
|
|
2062
2100
|
if (foe.active.volatiles.PerishSong) {
|
|
2063
|
-
return options.log.fail(battle.active(player.foe()), .None);
|
|
2101
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2064
2102
|
} else if (!side.active.volatiles.PerishSong) {
|
|
2065
2103
|
side.active.volatiles.PerishSong = true;
|
|
2066
2104
|
side.active.volatiles.perish_song = 4;
|
|
2067
|
-
try options.log.start(battle.active(player), .PerishSong3Silent);
|
|
2105
|
+
try options.log.start(.{ battle.active(player), .PerishSong3Silent });
|
|
2068
2106
|
}
|
|
2069
2107
|
foe.active.volatiles.PerishSong = true;
|
|
2070
2108
|
foe.active.volatiles.perish_song = 4;
|
|
2071
|
-
try options.log.start(battle.active(player.foe()), .PerishSong3Silent);
|
|
2109
|
+
try options.log.start(.{ battle.active(player.foe()), .PerishSong3Silent });
|
|
2072
2110
|
|
|
2073
|
-
try options.log.fieldactivate(); // FIXME .PerishSong
|
|
2111
|
+
try options.log.fieldactivate(.{}); // FIXME .PerishSong
|
|
2074
2112
|
}
|
|
2075
2113
|
|
|
2076
2114
|
pub fn poison(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2080,24 +2118,24 @@ pub const Effects = struct {
|
|
|
2080
2118
|
|
|
2081
2119
|
const toxic = state.move == .Toxic;
|
|
2082
2120
|
if (state.immune() or foe.active.types.includes(.Poison)) {
|
|
2083
|
-
return options.log.immune(foe_ident, .None);
|
|
2121
|
+
return options.log.immune(.{ foe_ident, .None });
|
|
2084
2122
|
} else if (Status.any(foe_stored.status)) {
|
|
2085
2123
|
// Pokémon Showdown considers Toxic to be a status even in Generation II and so
|
|
2086
2124
|
// will not include a fail reason for Toxic vs. Poison or vice-versa...
|
|
2087
|
-
return options.log.fail(foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2125
|
+
return options.log.fail(.{ foe_ident, if (Status.is(foe_stored.status, .PSN))
|
|
2088
2126
|
if (!showdown)
|
|
2089
|
-
.Poison
|
|
2127
|
+
Fail.Poison
|
|
2090
2128
|
else if (toxic == (foe_stored.status == Status.TOX))
|
|
2091
|
-
if (toxic) .Toxic else .Poison
|
|
2129
|
+
if (toxic) Fail.Toxic else Fail.Poison
|
|
2092
2130
|
else
|
|
2093
|
-
.None
|
|
2131
|
+
Fail.None
|
|
2094
2132
|
else
|
|
2095
|
-
.None);
|
|
2133
|
+
Fail.None });
|
|
2096
2134
|
} else if (foe.active.volatiles.Substitute) {
|
|
2097
|
-
return options.log.
|
|
2135
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2098
2136
|
} else if (state.miss) {
|
|
2099
|
-
try options.log.lastmiss();
|
|
2100
|
-
return options.log.miss(battle.active(player));
|
|
2137
|
+
try options.log.lastmiss(.{});
|
|
2138
|
+
return options.log.miss(.{battle.active(player)});
|
|
2101
2139
|
}
|
|
2102
2140
|
|
|
2103
2141
|
foe_stored.status = Status.init(.PSN);
|
|
@@ -2107,7 +2145,7 @@ pub const Effects = struct {
|
|
|
2107
2145
|
foe.active.volatiles.toxic = 0;
|
|
2108
2146
|
}
|
|
2109
2147
|
|
|
2110
|
-
try options.log.status(foe_ident, foe_stored.status, .None);
|
|
2148
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2111
2149
|
|
|
2112
2150
|
// TODO: check for status berry
|
|
2113
2151
|
}
|
|
@@ -2123,7 +2161,8 @@ pub const Effects = struct {
|
|
|
2123
2161
|
if (foe.conditions.Safeguard) return;
|
|
2124
2162
|
|
|
2125
2163
|
foe_stored.status = Status.init(.PSN);
|
|
2126
|
-
|
|
2164
|
+
|
|
2165
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2127
2166
|
|
|
2128
2167
|
// TODO: check for status berry
|
|
2129
2168
|
}
|
|
@@ -2138,9 +2177,7 @@ pub const Effects = struct {
|
|
|
2138
2177
|
|
|
2139
2178
|
const failed = !state.first or volatiles.Substitute or
|
|
2140
2179
|
!try Rolls.protect(battle, player, volatiles.protect, options);
|
|
2141
|
-
if (failed) {
|
|
2142
|
-
return options.log.fail(battle.active(player), .None);
|
|
2143
|
-
}
|
|
2180
|
+
if (failed) return options.log.fail(.{ battle.active(player), .None });
|
|
2144
2181
|
|
|
2145
2182
|
volatiles.protect = @max(8, volatiles.protect + 1);
|
|
2146
2183
|
if (state.move == .Endure) {
|
|
@@ -2148,7 +2185,7 @@ pub const Effects = struct {
|
|
|
2148
2185
|
} else {
|
|
2149
2186
|
volatiles.Protect = true;
|
|
2150
2187
|
}
|
|
2151
|
-
try options.log.singleturn(battle.active(player), state.move);
|
|
2188
|
+
try options.log.singleturn(.{ battle.active(player), state.move });
|
|
2152
2189
|
}
|
|
2153
2190
|
|
|
2154
2191
|
pub fn psychUp(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2157,30 +2194,29 @@ pub const Effects = struct {
|
|
|
2157
2194
|
|
|
2158
2195
|
// Pokémon Showdown should fail when the enemy has no boosts/unboosts but doesn't
|
|
2159
2196
|
if (!showdown and @as(u32, @bitCast(foe.active.boosts)) == 0) {
|
|
2160
|
-
return options.log.fail(battle.active(player), .None);
|
|
2197
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2161
2198
|
}
|
|
2162
2199
|
|
|
2163
2200
|
side.active.boosts = foe.active.boosts;
|
|
2164
|
-
try options.log.copyboost(battle.active(player), battle.active(player.foe()));
|
|
2201
|
+
try options.log.copyboost(.{ battle.active(player), battle.active(player.foe()) });
|
|
2165
2202
|
}
|
|
2166
2203
|
|
|
2167
2204
|
pub fn pursuit(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2168
2205
|
// TODO: remove switching variable and just look at Choice saved in Battle struct?
|
|
2169
|
-
if (battle.foe(player).active.volatiles.switching) state.damage
|
|
2206
|
+
if (battle.foe(player).active.volatiles.switching) state.damage = smul(state.damage, 2);
|
|
2170
2207
|
}
|
|
2171
2208
|
|
|
2172
2209
|
pub const rage = struct {
|
|
2173
2210
|
pub fn start(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2174
2211
|
battle.side(player).active.volatiles.Rage = true;
|
|
2175
|
-
try options.log.singlemove(battle.active(player), Move.Rage);
|
|
2212
|
+
try options.log.singlemove(.{ battle.active(player), Move.Rage });
|
|
2176
2213
|
}
|
|
2177
2214
|
|
|
2178
2215
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2179
|
-
|
|
2180
|
-
var volatiles = &side.active.volatiles;
|
|
2216
|
+
const side = battle.side(player);
|
|
2181
2217
|
|
|
2182
|
-
assert(volatiles.Rage);
|
|
2183
|
-
state.damage
|
|
2218
|
+
assert(side.active.volatiles.Rage);
|
|
2219
|
+
state.damage = smul(state.damage, (side.active.volatiles.rage +| 1)); // TODO: |+
|
|
2184
2220
|
}
|
|
2185
2221
|
};
|
|
2186
2222
|
|
|
@@ -2192,18 +2228,18 @@ pub const Effects = struct {
|
|
|
2192
2228
|
var side = battle.side(player);
|
|
2193
2229
|
|
|
2194
2230
|
if (side.active.volatiles.LeechSeed) {
|
|
2195
|
-
try options.log.end(battle.active(player), .LeechSeedFrom);
|
|
2231
|
+
try options.log.end(.{ battle.active(player), .LeechSeedFrom });
|
|
2196
2232
|
side.active.volatiles.LeechSeed = false;
|
|
2197
2233
|
}
|
|
2198
2234
|
|
|
2199
2235
|
if (side.conditions.Spikes) {
|
|
2200
|
-
try options.log.
|
|
2236
|
+
try options.log.sideend(.{ player, protocol.Side.Spikes, battle.active(player) });
|
|
2201
2237
|
side.conditions.Spikes = false;
|
|
2202
2238
|
}
|
|
2203
2239
|
|
|
2204
2240
|
if (side.active.volatiles.bind.duration > 0) {
|
|
2205
2241
|
const reason = @intFromEnum(End.Bind) + side.active.volatiles.bind.reason - 1;
|
|
2206
|
-
try options.log.end(battle.active(player), @enumFromInt(reason));
|
|
2242
|
+
try options.log.end(.{ battle.active(player), @as(End, @enumFromInt(reason)) });
|
|
2207
2243
|
side.active.volatiles.bind = .{};
|
|
2208
2244
|
}
|
|
2209
2245
|
}
|
|
@@ -2211,18 +2247,14 @@ pub const Effects = struct {
|
|
|
2211
2247
|
pub fn recoil(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2212
2248
|
var side = battle.side(player);
|
|
2213
2249
|
var stored = side.stored();
|
|
2250
|
+
const ident = battle.active(player);
|
|
2214
2251
|
|
|
2215
2252
|
assert(state.damage > 0);
|
|
2216
2253
|
|
|
2217
2254
|
const damage: i16 = @intCast(@max(state.damage / 4, 1));
|
|
2218
2255
|
stored.hp = @intCast(@max(@as(i16, @intCast(stored.hp)) - damage, 0));
|
|
2219
2256
|
|
|
2220
|
-
try options.log.
|
|
2221
|
-
battle.active(player),
|
|
2222
|
-
stored,
|
|
2223
|
-
.RecoilOf,
|
|
2224
|
-
battle.active(player.foe()),
|
|
2225
|
-
);
|
|
2257
|
+
try options.log.damage(.{ ident, stored, .RecoilOf, battle.active(player.foe()) });
|
|
2226
2258
|
}
|
|
2227
2259
|
|
|
2228
2260
|
pub fn reversal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2279,13 +2311,13 @@ pub const Effects = struct {
|
|
|
2279
2311
|
side.active.volatiles.attacks += 1;
|
|
2280
2312
|
side.active.volatiles.Rollout = side.active.volatiles.attacks != 5;
|
|
2281
2313
|
|
|
2282
|
-
if (side.active.volatiles.DefenseCurl) state.damage
|
|
2314
|
+
if (side.active.volatiles.DefenseCurl) state.damage = smul(state.damage, 2);
|
|
2283
2315
|
}
|
|
2284
2316
|
};
|
|
2285
2317
|
|
|
2286
2318
|
pub fn sandstorm(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2287
2319
|
if (battle.field.weather != .Sandstorm) return weather(battle, .Sandstorm, options);
|
|
2288
|
-
return options.log.fail(battle.active(player), .None);
|
|
2320
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2289
2321
|
}
|
|
2290
2322
|
|
|
2291
2323
|
pub fn safeguard(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2309,11 +2341,13 @@ pub const Effects = struct {
|
|
|
2309
2341
|
) !void {
|
|
2310
2342
|
var side = battle.side(player);
|
|
2311
2343
|
|
|
2312
|
-
if (@field(side.conditions, key))
|
|
2344
|
+
if (@field(side.conditions, key)) {
|
|
2345
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2346
|
+
}
|
|
2313
2347
|
|
|
2314
2348
|
@field(side.conditions, key) = true;
|
|
2315
2349
|
@field(side.conditions, value) = 5;
|
|
2316
|
-
try options.log.sidestart(player, @field(protocol.Side, key));
|
|
2350
|
+
try options.log.sidestart(.{ player, @field(protocol.Side, key) });
|
|
2317
2351
|
}
|
|
2318
2352
|
|
|
2319
2353
|
pub fn sketch(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2324,9 +2358,9 @@ pub const Effects = struct {
|
|
|
2324
2358
|
|
|
2325
2359
|
if (showdown) {
|
|
2326
2360
|
// yes, actually "Splash". '|nothing' gets blindly mapped to '|-activate||move:Splash'
|
|
2327
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2361
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2328
2362
|
} else {
|
|
2329
|
-
try options.log.fail(battle.active(player), .None);
|
|
2363
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2330
2364
|
}
|
|
2331
2365
|
}
|
|
2332
2366
|
|
|
@@ -2336,40 +2370,39 @@ pub const Effects = struct {
|
|
|
2336
2370
|
const foe_ident = battle.active(player.foe());
|
|
2337
2371
|
|
|
2338
2372
|
if (Status.any(foe_stored.status)) {
|
|
2339
|
-
return options.log.fail(
|
|
2373
|
+
return options.log.fail(.{
|
|
2340
2374
|
foe_ident,
|
|
2341
|
-
if (Status.is(foe_stored.status, .SLP)) .Sleep else .None,
|
|
2342
|
-
);
|
|
2375
|
+
if (Status.is(foe_stored.status, .SLP)) Fail.Sleep else Fail.None,
|
|
2376
|
+
});
|
|
2343
2377
|
}
|
|
2344
2378
|
|
|
2345
2379
|
if (foe.active.volatiles.Substitute) {
|
|
2346
|
-
return options.log.
|
|
2380
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2347
2381
|
}
|
|
2348
2382
|
|
|
2349
2383
|
// Sleep Clause Mod
|
|
2350
|
-
if (
|
|
2384
|
+
if (pkmn.options.mod) {
|
|
2351
2385
|
for (foe.pokemon) |p| {
|
|
2352
2386
|
if (Status.is(p.status, .SLP) and !Status.is(p.status, .EXT)) return;
|
|
2353
2387
|
}
|
|
2354
2388
|
}
|
|
2355
2389
|
|
|
2356
2390
|
foe_stored.status = Status.slp(Rolls.sleepDuration(battle, player, options));
|
|
2357
|
-
try options.log.
|
|
2391
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .From, state.move });
|
|
2358
2392
|
}
|
|
2359
2393
|
|
|
2360
2394
|
pub fn sleepTalk(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2361
2395
|
const side = battle.side(player);
|
|
2362
2396
|
|
|
2363
2397
|
if (!Status.is(side.stored().status, .SLP)) {
|
|
2364
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
2365
|
-
return;
|
|
2398
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2366
2399
|
}
|
|
2367
2400
|
|
|
2368
2401
|
var n: u3 = 0;
|
|
2369
2402
|
for (side.active.moves, 0..) |m, i| {
|
|
2370
2403
|
if (state.mslot - 1 != i and Move.get(m.id).extra.sleep_talk) n += 1;
|
|
2371
2404
|
}
|
|
2372
|
-
if (n == 0) return options.log.fail(battle.active(player), .None);
|
|
2405
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
2373
2406
|
|
|
2374
2407
|
_ = try Rolls.sleepTalk(battle, player, n, state.mslot, options); // TODO
|
|
2375
2408
|
}
|
|
@@ -2378,55 +2411,75 @@ pub const Effects = struct {
|
|
|
2378
2411
|
if (Status.is(battle.side(player).stored().status, .SLP)) return true;
|
|
2379
2412
|
state.damage = 0;
|
|
2380
2413
|
state.miss = true; // TODO is this necessary?
|
|
2381
|
-
if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
2414
|
+
if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2382
2415
|
return false;
|
|
2383
2416
|
}
|
|
2384
2417
|
|
|
2385
2418
|
pub fn spikes(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2386
2419
|
var foe = battle.foe(player);
|
|
2387
|
-
if (foe.conditions.Spikes) return options.log.fail(battle.active(player), .None);
|
|
2420
|
+
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player), .None });
|
|
2388
2421
|
|
|
2389
2422
|
foe.conditions.Spikes = true;
|
|
2390
|
-
try options.log.sidestart(player.foe(), .Spikes);
|
|
2423
|
+
try options.log.sidestart(.{ player.foe(), protocol.Side.Spikes });
|
|
2391
2424
|
}
|
|
2392
2425
|
|
|
2393
2426
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2394
|
-
|
|
2395
|
-
// var foe = battle.foe(player);
|
|
2427
|
+
var foe = battle.foe(player);
|
|
2396
2428
|
|
|
2397
|
-
|
|
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
|
+
}
|
|
2398
2433
|
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
//
|
|
2404
|
-
|
|
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 });
|
|
2405
2458
|
}
|
|
2406
2459
|
|
|
2407
2460
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2408
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2461
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2409
2462
|
}
|
|
2410
2463
|
|
|
2411
2464
|
pub fn substitute(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2412
2465
|
var side = battle.side(player);
|
|
2413
2466
|
if (side.active.volatiles.Substitute) {
|
|
2414
|
-
return options.log.fail(battle.active(player), .Substitute);
|
|
2467
|
+
return options.log.fail(.{ battle.active(player), .Substitute });
|
|
2415
2468
|
}
|
|
2416
2469
|
|
|
2417
2470
|
assert(side.stored().stats.hp <= 1023);
|
|
2418
2471
|
const hp: u8 = @intCast(side.stored().stats.hp / 4);
|
|
2419
2472
|
|
|
2420
2473
|
if (hp == 0 or side.stored().hp <= hp) {
|
|
2421
|
-
return options.log.fail(battle.active(player), .Weak);
|
|
2474
|
+
return options.log.fail(.{ battle.active(player), .Weak });
|
|
2422
2475
|
}
|
|
2423
2476
|
|
|
2424
2477
|
side.stored().hp -= hp;
|
|
2425
2478
|
side.active.volatiles.substitute = hp;
|
|
2426
2479
|
side.active.volatiles.Substitute = true;
|
|
2427
2480
|
side.active.volatiles.bind = .{};
|
|
2428
|
-
try options.log.start(battle.active(player), .Substitute);
|
|
2429
|
-
try options.log.damage(battle.active(player), side.stored(), .None);
|
|
2481
|
+
try options.log.start(.{ battle.active(player), .Substitute });
|
|
2482
|
+
try options.log.damage(.{ battle.active(player), side.stored(), .None });
|
|
2430
2483
|
}
|
|
2431
2484
|
|
|
2432
2485
|
pub fn sunnyDay(battle: anytype, _: Player, _: *State, options: anytype) !void {
|
|
@@ -2436,14 +2489,14 @@ pub const Effects = struct {
|
|
|
2436
2489
|
fn weather(battle: anytype, w: Weather, options: anytype) !void {
|
|
2437
2490
|
battle.field.weather = w;
|
|
2438
2491
|
battle.field.weather_duration = 5;
|
|
2439
|
-
try options.log.weather(w, .None);
|
|
2492
|
+
try options.log.weather(.{ w, protocol.Weather.None });
|
|
2440
2493
|
}
|
|
2441
2494
|
|
|
2442
2495
|
pub fn teleport(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2443
2496
|
if (showdown) return;
|
|
2444
2497
|
|
|
2445
|
-
try options.log.fail(battle.active(player), .None);
|
|
2446
|
-
try options.log.laststill();
|
|
2498
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2499
|
+
try options.log.laststill(.{});
|
|
2447
2500
|
}
|
|
2448
2501
|
|
|
2449
2502
|
pub fn thief(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2458,11 +2511,11 @@ pub const Effects = struct {
|
|
|
2458
2511
|
side.stored().item = foe.stored().item;
|
|
2459
2512
|
foe.stored().item = .None;
|
|
2460
2513
|
|
|
2461
|
-
try options.log.item(
|
|
2514
|
+
try options.log.item(.{
|
|
2462
2515
|
battle.active(player),
|
|
2463
2516
|
side.stored().item,
|
|
2464
2517
|
battle.active(player.foe()),
|
|
2465
|
-
);
|
|
2518
|
+
});
|
|
2466
2519
|
}
|
|
2467
2520
|
|
|
2468
2521
|
pub fn thrashing(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2484,11 +2537,11 @@ pub const Effects = struct {
|
|
|
2484
2537
|
side.active.volatiles.dirty = false;
|
|
2485
2538
|
|
|
2486
2539
|
if (foe.active.volatiles.Transform) {
|
|
2487
|
-
return options.log.fail(battle.active(player), .None);
|
|
2540
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2488
2541
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2489
|
-
if (!showdown) return options.log.fail(battle.active(player), .None);
|
|
2490
|
-
try options.log.lastmiss();
|
|
2491
|
-
return options.log.miss(battle.active(player));
|
|
2542
|
+
if (!showdown) return options.log.fail(.{ battle.active(player), .None });
|
|
2543
|
+
try options.log.lastmiss(.{});
|
|
2544
|
+
return options.log.miss(.{battle.active(player)});
|
|
2492
2545
|
}
|
|
2493
2546
|
|
|
2494
2547
|
side.active.volatiles.Transform = true;
|
|
@@ -2510,7 +2563,7 @@ pub const Effects = struct {
|
|
|
2510
2563
|
side.active.moves[i].pp = if (m.id != .None) if (m.id == .Sketch) 1 else 5 else 0;
|
|
2511
2564
|
}
|
|
2512
2565
|
|
|
2513
|
-
try options.log.transform(battle.active(player), foe_ident);
|
|
2566
|
+
try options.log.transform(.{ battle.active(player), foe_ident });
|
|
2514
2567
|
}
|
|
2515
2568
|
|
|
2516
2569
|
pub fn triAttack(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2535,17 +2588,17 @@ pub const Effects = struct {
|
|
|
2535
2588
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2536
2589
|
var side = battle.side(player);
|
|
2537
2590
|
|
|
2538
|
-
|
|
2539
|
-
|
|
2591
|
+
const stats = &side.active.stats;
|
|
2592
|
+
const boosts = &side.active.boosts;
|
|
2540
2593
|
|
|
2541
2594
|
if (!state.proc) return;
|
|
2542
2595
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
2543
2596
|
assert(@field(boosts, s) >= -6 and @field(boosts, s) <= 6);
|
|
2544
2597
|
if (@field(boosts, s) == 6 or @field(stats, s) == MAX_STAT_VALUE) {
|
|
2545
|
-
try options.log.boost(battle.active(player), r, 0);
|
|
2598
|
+
try options.log.boost(.{ battle.active(player), r, 0 });
|
|
2546
2599
|
} else {
|
|
2547
2600
|
try boostStat(battle, side, stats, boosts, s, 1);
|
|
2548
|
-
try options.log.boost(battle.active(player), r, 1);
|
|
2601
|
+
try options.log.boost(.{ battle.active(player), r, 1 });
|
|
2549
2602
|
}
|
|
2550
2603
|
}
|
|
2551
2604
|
}
|
|
@@ -2555,51 +2608,61 @@ pub const Effects = struct {
|
|
|
2555
2608
|
var side = battle.side(player);
|
|
2556
2609
|
const ident = battle.active(player);
|
|
2557
2610
|
|
|
2558
|
-
if (state.miss) return log.fail(ident, .None);
|
|
2611
|
+
if (state.miss) return log.fail(.{ ident, .None });
|
|
2559
2612
|
|
|
2560
|
-
|
|
2613
|
+
const stats = &side.active.stats;
|
|
2561
2614
|
var boosts = &side.active.boosts;
|
|
2562
2615
|
|
|
2563
2616
|
const move = Move.get(state.move);
|
|
2564
2617
|
switch (move.effect) {
|
|
2565
2618
|
.AttackUp1, .AttackUp2, .Rage, .Swagger => {
|
|
2566
2619
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2567
|
-
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE)
|
|
2620
|
+
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE) {
|
|
2621
|
+
return log.fail(.{ ident, .None });
|
|
2622
|
+
}
|
|
2568
2623
|
const n: u2 = if (move.effect == .AttackUp2 or move.effect == .Swagger) 2 else 1;
|
|
2569
2624
|
try boostStat(battle, side, stats, boosts, "atk", n);
|
|
2570
2625
|
const reason = if (showdown and move.effect == .Rage) Boost.Rage else Boost.Attack;
|
|
2571
|
-
try log.boost(ident, reason, n);
|
|
2626
|
+
try log.boost(.{ ident, reason, n });
|
|
2572
2627
|
},
|
|
2573
2628
|
.DefenseUp1, .DefenseUp2, .SkullBash => {
|
|
2574
2629
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2575
|
-
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE)
|
|
2630
|
+
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE) {
|
|
2631
|
+
return log.fail(.{ ident, .None });
|
|
2632
|
+
}
|
|
2576
2633
|
const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
|
|
2577
2634
|
try boostStat(battle, side, stats, boosts, "def", n);
|
|
2578
|
-
try log.boost(ident, .Defense, n);
|
|
2635
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2579
2636
|
},
|
|
2580
2637
|
.SpeedUp2 => {
|
|
2581
2638
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2582
|
-
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE)
|
|
2639
|
+
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE) {
|
|
2640
|
+
return log.fail(.{ ident, .None });
|
|
2641
|
+
}
|
|
2583
2642
|
try boostStat(battle, side, stats, boosts, "spe", 2);
|
|
2584
|
-
try log.boost(ident, .Speed, 2);
|
|
2643
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2585
2644
|
},
|
|
2586
2645
|
.SpAtkUp1 => {
|
|
2587
2646
|
assert(boosts.spa >= -6 and boosts.spa <= 6);
|
|
2588
|
-
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE)
|
|
2647
|
+
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE) {
|
|
2648
|
+
return log.fail(.{ ident, .None });
|
|
2649
|
+
}
|
|
2589
2650
|
try boostStat(battle, side, stats, boosts, "spa", 1);
|
|
2590
|
-
try log.boost(ident, .SpecialAttack, 1);
|
|
2651
|
+
try log.boost(.{ ident, .SpecialAttack, 1 });
|
|
2591
2652
|
},
|
|
2592
2653
|
.SpDefUp2 => {
|
|
2593
2654
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2594
|
-
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE)
|
|
2655
|
+
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE) {
|
|
2656
|
+
return log.fail(.{ ident, .None });
|
|
2657
|
+
}
|
|
2595
2658
|
try boostStat(battle, side, stats, boosts, "spd", 2);
|
|
2596
|
-
try log.boost(ident, .SpecialDefense, 2);
|
|
2659
|
+
try log.boost(.{ ident, .SpecialDefense, 2 });
|
|
2597
2660
|
},
|
|
2598
2661
|
.EvasionUp1 => {
|
|
2599
2662
|
assert(boosts.evasion >= -6 and boosts.evasion <= 6);
|
|
2600
|
-
if (boosts.evasion == 6) return log.fail(ident, .None);
|
|
2663
|
+
if (boosts.evasion == 6) return log.fail(.{ ident, .None });
|
|
2601
2664
|
boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
|
|
2602
|
-
try log.boost(ident, .Evasion, 1);
|
|
2665
|
+
try log.boost(.{ ident, .Evasion, 1 });
|
|
2603
2666
|
if (state.move == .Minimize) side.active.volatiles.minimized = true;
|
|
2604
2667
|
},
|
|
2605
2668
|
else => unreachable,
|
|
@@ -2612,79 +2675,83 @@ pub const Effects = struct {
|
|
|
2612
2675
|
const foe_ident = battle.active(player.foe());
|
|
2613
2676
|
|
|
2614
2677
|
if (foe.active.volatiles.Mist) {
|
|
2615
|
-
try log.activate(foe_ident, .Mist);
|
|
2616
|
-
return log.fail(foe_ident, .None);
|
|
2678
|
+
try log.activate(.{ foe_ident, .Mist });
|
|
2679
|
+
return log.fail(.{ foe_ident, .None });
|
|
2617
2680
|
}
|
|
2618
2681
|
|
|
2619
2682
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2620
2683
|
|
|
2621
|
-
|
|
2684
|
+
const stats = &foe.active.stats;
|
|
2622
2685
|
var boosts = &foe.active.boosts;
|
|
2623
2686
|
|
|
2624
2687
|
const move = Move.get(state.move);
|
|
2688
|
+
const none: Fail = .None;
|
|
2625
2689
|
switch (move.effect) {
|
|
2626
2690
|
.AttackDown1, .AttackDown2, .AttackDownChance => {
|
|
2627
2691
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2628
2692
|
if (fail or boosts.atk == -6) {
|
|
2629
|
-
return if (move.effect != .AttackDownChance) try log.fail(foe_ident,
|
|
2693
|
+
return if (move.effect != .AttackDownChance) try log.fail(.{ foe_ident, none });
|
|
2630
2694
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2631
|
-
try options.log.lastmiss();
|
|
2632
|
-
return options.log.miss(battle.active(player));
|
|
2695
|
+
try options.log.lastmiss(.{});
|
|
2696
|
+
return options.log.miss(.{battle.active(player)});
|
|
2633
2697
|
} else if (stats.atk == 1) {
|
|
2634
|
-
return if (move.effect != .AttackDownChance) try log.fail(foe_ident,
|
|
2698
|
+
return if (move.effect != .AttackDownChance) try log.fail(.{ foe_ident, none });
|
|
2635
2699
|
}
|
|
2636
2700
|
const n: u2 = if (move.effect == .AttackDown2) 2 else 1;
|
|
2637
2701
|
try unboostStat(battle, foe, stats, boosts, "atk", n);
|
|
2638
|
-
try log.boost(foe_ident, .Attack, -@as(i8, n));
|
|
2702
|
+
try log.boost(.{ foe_ident, .Attack, -@as(i8, n) });
|
|
2639
2703
|
},
|
|
2640
2704
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2641
2705
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2642
2706
|
if (fail or boosts.def == -6) {
|
|
2643
|
-
|
|
2707
|
+
if (move.effect != .DefenseDownChance) try log.fail(.{ foe_ident, none });
|
|
2708
|
+
return;
|
|
2644
2709
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2645
|
-
try options.log.lastmiss();
|
|
2646
|
-
return options.log.miss(battle.active(player));
|
|
2710
|
+
try options.log.lastmiss(.{});
|
|
2711
|
+
return options.log.miss(.{battle.active(player)});
|
|
2647
2712
|
} else if (stats.def == 1) {
|
|
2648
|
-
|
|
2713
|
+
if (move.effect != .DefenseDownChance) try log.fail(.{ foe_ident, none });
|
|
2714
|
+
return;
|
|
2649
2715
|
}
|
|
2650
2716
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2651
2717
|
try unboostStat(battle, foe, stats, boosts, "def", n);
|
|
2652
|
-
try log.boost(foe_ident, .Defense, -@as(i8, n));
|
|
2718
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2653
2719
|
},
|
|
2654
2720
|
.SpeedDown1, .SpeedDown2, .SpeedDownChance => {
|
|
2655
2721
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2656
2722
|
if (fail or boosts.spe == -6) {
|
|
2657
|
-
return if (move.effect != .SpeedDownChance) try log.fail(foe_ident,
|
|
2723
|
+
return if (move.effect != .SpeedDownChance) try log.fail(.{ foe_ident, none });
|
|
2658
2724
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2659
|
-
try options.log.lastmiss();
|
|
2660
|
-
return options.log.miss(battle.active(player));
|
|
2725
|
+
try options.log.lastmiss(.{});
|
|
2726
|
+
return options.log.miss(.{battle.active(player)});
|
|
2661
2727
|
} else if (stats.spe == 1) {
|
|
2662
|
-
return if (move.effect != .SpeedDownChance) try log.fail(foe_ident,
|
|
2728
|
+
return if (move.effect != .SpeedDownChance) try log.fail(.{ foe_ident, none });
|
|
2663
2729
|
}
|
|
2664
2730
|
const n: u2 = if (move.effect == .SpeedDown2) 2 else 1;
|
|
2665
2731
|
try unboostStat(battle, foe, stats, boosts, "spe", n);
|
|
2666
|
-
try log.boost(foe_ident, .Speed, -@as(i8, n));
|
|
2732
|
+
try log.boost(.{ foe_ident, .Speed, -@as(i8, n) });
|
|
2667
2733
|
},
|
|
2668
2734
|
.SpDefDownChance => {
|
|
2669
2735
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2670
2736
|
if (fail or boosts.spd == -6) return;
|
|
2671
2737
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2672
|
-
try options.log.lastmiss();
|
|
2673
|
-
return options.log.miss(battle.active(player));
|
|
2738
|
+
try options.log.lastmiss(.{});
|
|
2739
|
+
return options.log.miss(.{battle.active(player)});
|
|
2674
2740
|
} else if (stats.spd == 1) return;
|
|
2675
2741
|
try unboostStat(battle, foe, stats, boosts, "spd", 1);
|
|
2676
|
-
try log.boost(foe_ident, .SpecialDefense, -1);
|
|
2742
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2677
2743
|
},
|
|
2678
2744
|
.AccuracyDown1, .AccuracyDownChance => {
|
|
2679
2745
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
2680
2746
|
if (fail or boosts.accuracy == -6) {
|
|
2681
|
-
|
|
2747
|
+
if (move.effect != .AccuracyDownChance) try log.fail(.{ foe_ident, none });
|
|
2748
|
+
return;
|
|
2682
2749
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2683
|
-
try options.log.lastmiss();
|
|
2684
|
-
return options.log.miss(battle.active(player));
|
|
2750
|
+
try options.log.lastmiss(.{});
|
|
2751
|
+
return options.log.miss(.{battle.active(player)});
|
|
2685
2752
|
}
|
|
2686
2753
|
boosts.accuracy = @intCast(@max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2687
|
-
try log.boost(foe_ident, .Accuracy, -1);
|
|
2754
|
+
try log.boost(.{ foe_ident, .Accuracy, -1 });
|
|
2688
2755
|
},
|
|
2689
2756
|
else => unreachable,
|
|
2690
2757
|
}
|
|
@@ -2698,9 +2765,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2698
2765
|
}
|
|
2699
2766
|
|
|
2700
2767
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2701
|
-
|
|
2768
|
+
const stats = unmodifiedStats(battle, side);
|
|
2702
2769
|
inline for (STATS) |s| {
|
|
2703
|
-
|
|
2770
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2704
2771
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2705
2772
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2706
2773
|
}
|
|
@@ -2723,7 +2790,7 @@ fn boostStat(
|
|
|
2723
2790
|
n: u2,
|
|
2724
2791
|
) !void {
|
|
2725
2792
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2726
|
-
|
|
2793
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2727
2794
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2728
2795
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2729
2796
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2746,7 +2813,7 @@ fn unboostStat(
|
|
|
2746
2813
|
n: u2,
|
|
2747
2814
|
) !void {
|
|
2748
2815
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2749
|
-
|
|
2816
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2750
2817
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2751
2818
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2752
2819
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2772,9 +2839,15 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2772
2839
|
return t != .@"???" and !active.types.includes(t);
|
|
2773
2840
|
}
|
|
2774
2841
|
|
|
2842
|
+
// NOTE: llvm/llvm-project#58557
|
|
2843
|
+
fn smul(a: anytype, b: @TypeOf(a)) @TypeOf(a) {
|
|
2844
|
+
if (!comptime builtin.target.cpu.arch.isWasm()) return a *| b;
|
|
2845
|
+
return @max(a *% b, std.math.maxInt(@TypeOf(a)));
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2775
2848
|
pub const Rolls = struct {
|
|
2776
2849
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2777
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2850
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2778
2851
|
player == .P1
|
|
2779
2852
|
else if (showdown)
|
|
2780
2853
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2786,7 +2859,7 @@ pub const Rolls = struct {
|
|
|
2786
2859
|
}
|
|
2787
2860
|
|
|
2788
2861
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2789
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2862
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2790
2863
|
val == .true
|
|
2791
2864
|
else if (showdown)
|
|
2792
2865
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2798,7 +2871,7 @@ pub const Rolls = struct {
|
|
|
2798
2871
|
}
|
|
2799
2872
|
|
|
2800
2873
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2801
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2874
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2802
2875
|
val == .true
|
|
2803
2876
|
else if (showdown)
|
|
2804
2877
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2809,20 +2882,22 @@ pub const Rolls = struct {
|
|
|
2809
2882
|
return ok;
|
|
2810
2883
|
}
|
|
2811
2884
|
|
|
2812
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2813
|
-
|
|
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|
|
|
2814
2888
|
val == .true
|
|
2815
2889
|
else if (showdown)
|
|
2816
2890
|
battle.rng.chance(u8, rate, 256)
|
|
2817
2891
|
else
|
|
2818
2892
|
battle.rng.next() < rate;
|
|
2819
2893
|
|
|
2820
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2894
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2821
2895
|
return crit;
|
|
2822
2896
|
}
|
|
2823
2897
|
|
|
2824
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2825
|
-
|
|
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|
|
|
2826
2901
|
val
|
|
2827
2902
|
else roll: {
|
|
2828
2903
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2833,12 +2908,12 @@ pub const Rolls = struct {
|
|
|
2833
2908
|
};
|
|
2834
2909
|
|
|
2835
2910
|
assert(roll >= 217 and roll <= 255);
|
|
2836
|
-
try options.chance.damage(player, roll);
|
|
2911
|
+
try options.chance.damage(player, i, roll);
|
|
2837
2912
|
return roll;
|
|
2838
2913
|
}
|
|
2839
2914
|
|
|
2840
2915
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2841
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2916
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2842
2917
|
val == .true
|
|
2843
2918
|
else if (showdown)
|
|
2844
2919
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2850,7 +2925,7 @@ pub const Rolls = struct {
|
|
|
2850
2925
|
}
|
|
2851
2926
|
|
|
2852
2927
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2853
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2928
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2854
2929
|
val == .true
|
|
2855
2930
|
else if (showdown)
|
|
2856
2931
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2862,7 +2937,7 @@ pub const Rolls = struct {
|
|
|
2862
2937
|
}
|
|
2863
2938
|
|
|
2864
2939
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2865
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2940
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2866
2941
|
val == .true
|
|
2867
2942
|
else if (showdown)
|
|
2868
2943
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2874,7 +2949,7 @@ pub const Rolls = struct {
|
|
|
2874
2949
|
}
|
|
2875
2950
|
|
|
2876
2951
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2877
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2952
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2878
2953
|
val == .true
|
|
2879
2954
|
else if (showdown)
|
|
2880
2955
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2887,7 +2962,7 @@ pub const Rolls = struct {
|
|
|
2887
2962
|
|
|
2888
2963
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2889
2964
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2890
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2965
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2891
2966
|
val == .true
|
|
2892
2967
|
else if (showdown)
|
|
2893
2968
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2899,7 +2974,7 @@ pub const Rolls = struct {
|
|
|
2899
2974
|
}
|
|
2900
2975
|
|
|
2901
2976
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2902
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2977
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2903
2978
|
val == .true
|
|
2904
2979
|
else if (showdown)
|
|
2905
2980
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2911,7 +2986,7 @@ pub const Rolls = struct {
|
|
|
2911
2986
|
}
|
|
2912
2987
|
|
|
2913
2988
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2914
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2989
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2915
2990
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2916
2991
|
else if (showdown)
|
|
2917
2992
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2927,7 +3002,7 @@ pub const Rolls = struct {
|
|
|
2927
3002
|
}
|
|
2928
3003
|
|
|
2929
3004
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2930
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3005
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2931
3006
|
(val - 1) * 40
|
|
2932
3007
|
else if (showdown) power: {
|
|
2933
3008
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2949,7 +3024,7 @@ pub const Rolls = struct {
|
|
|
2949
3024
|
}
|
|
2950
3025
|
|
|
2951
3026
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2952
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3027
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2953
3028
|
val + 3
|
|
2954
3029
|
else if (showdown) num: {
|
|
2955
3030
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -2977,7 +3052,7 @@ pub const Rolls = struct {
|
|
|
2977
3052
|
}
|
|
2978
3053
|
|
|
2979
3054
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
2980
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3055
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
2981
3056
|
val
|
|
2982
3057
|
else if (showdown)
|
|
2983
3058
|
battle.rng.range(u2, 1, 4)
|
|
@@ -2993,13 +3068,12 @@ pub const Rolls = struct {
|
|
|
2993
3068
|
return hits;
|
|
2994
3069
|
}
|
|
2995
3070
|
|
|
2996
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
2997
|
-
|
|
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|
|
|
2998
3074
|
val + 1
|
|
2999
|
-
else if (showdown)
|
|
3000
|
-
battle.rng.range(u8, 2, 6)
|
|
3001
3075
|
else
|
|
3002
|
-
(battle.rng.next() & 3) + 2;
|
|
3076
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3003
3077
|
|
|
3004
3078
|
assert(pp >= 2 and pp <= 5);
|
|
3005
3079
|
try options.chance.spite(player, pp);
|
|
@@ -3007,13 +3081,13 @@ pub const Rolls = struct {
|
|
|
3007
3081
|
}
|
|
3008
3082
|
|
|
3009
3083
|
// Conversion 2 can at most choose between 7 types (vs. a Grass-type attack)
|
|
3010
|
-
var CONVERSION_2: [7]Type =
|
|
3084
|
+
var CONVERSION_2: [7]Type = @splat(.@"???");
|
|
3011
3085
|
|
|
3012
3086
|
fn conversion2(battle: anytype, player: Player, mtype: Type, options: anytype) !Type {
|
|
3013
3087
|
var i: u8 = 0;
|
|
3014
3088
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3015
3089
|
assert(mtype != .@"???");
|
|
3016
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3090
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3017
3091
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3018
3092
|
else if (showdown) ty: {
|
|
3019
3093
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3047,7 +3121,7 @@ pub const Rolls = struct {
|
|
|
3047
3121
|
assert(count <= 8);
|
|
3048
3122
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3049
3123
|
|
|
3050
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3124
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3051
3125
|
val == .true
|
|
3052
3126
|
else if (showdown)
|
|
3053
3127
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3069,7 +3143,7 @@ pub const Rolls = struct {
|
|
|
3069
3143
|
const moves = active.moves;
|
|
3070
3144
|
|
|
3071
3145
|
// TODO: consider throwing error instead of rerolling?
|
|
3072
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3146
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3073
3147
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3074
3148
|
else
|
|
3075
3149
|
null;
|
|
@@ -3106,7 +3180,7 @@ pub const Rolls = struct {
|
|
|
3106
3180
|
const moves = active.moves;
|
|
3107
3181
|
|
|
3108
3182
|
// TODO: consider throwing error instead of rerolling?
|
|
3109
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3183
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3110
3184
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3111
3185
|
else
|
|
3112
3186
|
null;
|
|
@@ -3150,7 +3224,7 @@ pub const Rolls = struct {
|
|
|
3150
3224
|
assert(n > 0 and n <= 6);
|
|
3151
3225
|
|
|
3152
3226
|
// TODO: consider throwing error instead of rerolling?
|
|
3153
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3227
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3154
3228
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3155
3229
|
break :val null;
|
|
3156
3230
|
} else null;
|
|
@@ -3176,7 +3250,7 @@ pub const Rolls = struct {
|
|
|
3176
3250
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3177
3251
|
|
|
3178
3252
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3179
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3253
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3180
3254
|
@intCast(val)
|
|
3181
3255
|
else if (showdown)
|
|
3182
3256
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3191,7 +3265,7 @@ pub const Rolls = struct {
|
|
|
3191
3265
|
}
|
|
3192
3266
|
|
|
3193
3267
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3194
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3268
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3195
3269
|
val
|
|
3196
3270
|
else if (showdown)
|
|
3197
3271
|
battle.rng.range(u8, 1, max)
|
|
@@ -3217,14 +3291,14 @@ pub const Rolls = struct {
|
|
|
3217
3291
|
}
|
|
3218
3292
|
|
|
3219
3293
|
// TODO: consider throwing error instead of rerolling?
|
|
3220
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3294
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3221
3295
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3222
3296
|
break :val val;
|
|
3223
3297
|
} else null;
|
|
3224
3298
|
|
|
3225
3299
|
const move: Move = overridden orelse move: {
|
|
3226
3300
|
if (showdown) {
|
|
3227
|
-
var r = battle.rng.range(u8, 0, @intCast(Move.METRONOME.len - n));
|
|
3301
|
+
var r = battle.rng.range(u8, 0, @as(u8, @intCast(Move.METRONOME.len - n)));
|
|
3228
3302
|
for (moves) |m| {
|
|
3229
3303
|
if (m.id == .None) break;
|
|
3230
3304
|
if (@intFromEnum(m.id) > @intFromEnum(Move.METRONOME[r])) break;
|
|
@@ -3249,7 +3323,7 @@ pub const Rolls = struct {
|
|
|
3249
3323
|
}
|
|
3250
3324
|
|
|
3251
3325
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3252
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3326
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3253
3327
|
@intCast(val)
|
|
3254
3328
|
else if (showdown)
|
|
3255
3329
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3261,12 +3335,12 @@ pub const Rolls = struct {
|
|
|
3261
3335
|
};
|
|
3262
3336
|
|
|
3263
3337
|
assert(duration >= 1 and duration <= 6);
|
|
3264
|
-
options.chance.duration(
|
|
3338
|
+
options.chance.duration(player, duration);
|
|
3265
3339
|
return duration + 1;
|
|
3266
3340
|
}
|
|
3267
3341
|
|
|
3268
3342
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3269
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3343
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3270
3344
|
@intCast(val)
|
|
3271
3345
|
else if (showdown)
|
|
3272
3346
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3275,16 +3349,16 @@ pub const Rolls = struct {
|
|
|
3275
3349
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3276
3350
|
|
|
3277
3351
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3278
|
-
options.chance.duration(
|
|
3352
|
+
options.chance.duration(player, duration);
|
|
3279
3353
|
return duration;
|
|
3280
3354
|
}
|
|
3281
3355
|
|
|
3282
3356
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3283
|
-
const duration:
|
|
3357
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3284
3358
|
@intCast(val)
|
|
3285
3359
|
else if (showdown)
|
|
3286
3360
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3287
|
-
battle.rng.range(
|
|
3361
|
+
battle.rng.range(u3, 2, 6)
|
|
3288
3362
|
else duration: {
|
|
3289
3363
|
while (true) {
|
|
3290
3364
|
const r = battle.rng.next() & 7;
|
|
@@ -3293,12 +3367,12 @@ pub const Rolls = struct {
|
|
|
3293
3367
|
};
|
|
3294
3368
|
|
|
3295
3369
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3296
|
-
options.chance.duration(
|
|
3370
|
+
options.chance.duration(player, duration);
|
|
3297
3371
|
return duration;
|
|
3298
3372
|
}
|
|
3299
3373
|
|
|
3300
3374
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3301
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3375
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3302
3376
|
@intCast(val)
|
|
3303
3377
|
else if (showdown)
|
|
3304
3378
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3306,12 +3380,12 @@ pub const Rolls = struct {
|
|
|
3306
3380
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3307
3381
|
|
|
3308
3382
|
assert(duration >= 2 and duration <= 3);
|
|
3309
|
-
options.chance.duration(
|
|
3383
|
+
options.chance.duration(player, duration);
|
|
3310
3384
|
return duration;
|
|
3311
3385
|
}
|
|
3312
3386
|
|
|
3313
3387
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3314
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3388
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3315
3389
|
@intCast(val)
|
|
3316
3390
|
else if (showdown)
|
|
3317
3391
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3320,13 +3394,13 @@ pub const Rolls = struct {
|
|
|
3320
3394
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3321
3395
|
|
|
3322
3396
|
assert(duration >= 2 and duration <= 5);
|
|
3323
|
-
options.chance.duration(
|
|
3397
|
+
options.chance.duration(player, duration);
|
|
3324
3398
|
return duration + 1;
|
|
3325
3399
|
}
|
|
3326
3400
|
|
|
3327
3401
|
// TODO: consider sharing implementation with bindingDuration
|
|
3328
3402
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3329
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3403
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3330
3404
|
@intCast(val)
|
|
3331
3405
|
else if (showdown)
|
|
3332
3406
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3334,7 +3408,7 @@ pub const Rolls = struct {
|
|
|
3334
3408
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3335
3409
|
|
|
3336
3410
|
assert(duration >= 2 and duration <= 5);
|
|
3337
|
-
options.chance.duration(
|
|
3411
|
+
options.chance.duration(player, duration);
|
|
3338
3412
|
return duration + 1;
|
|
3339
3413
|
}
|
|
3340
3414
|
};
|
|
@@ -3432,11 +3506,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3432
3506
|
},
|
|
3433
3507
|
.Switch => {
|
|
3434
3508
|
const side = battle.side(player);
|
|
3435
|
-
var slot:
|
|
3509
|
+
var slot: usize = 2;
|
|
3436
3510
|
while (slot <= 6) : (slot += 1) {
|
|
3437
3511
|
const id = side.order[slot - 1];
|
|
3438
3512
|
if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
|
|
3439
|
-
out[n] = .{ .type = .Switch, .data = slot };
|
|
3513
|
+
out[n] = .{ .type = .Switch, .data = @intCast(slot) };
|
|
3440
3514
|
n += 1;
|
|
3441
3515
|
}
|
|
3442
3516
|
if (n == 0) {
|
|
@@ -3447,7 +3521,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3447
3521
|
.Move => {
|
|
3448
3522
|
const side = battle.side(player);
|
|
3449
3523
|
|
|
3450
|
-
|
|
3524
|
+
const active = &side.active;
|
|
3451
3525
|
const stored = side.stored();
|
|
3452
3526
|
|
|
3453
3527
|
// While players are not given any input options on the cartridge in these cases,
|
|
@@ -3466,11 +3540,11 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3466
3540
|
return n;
|
|
3467
3541
|
}
|
|
3468
3542
|
|
|
3469
|
-
var slot:
|
|
3543
|
+
var slot: usize = 2;
|
|
3470
3544
|
while (!active.volatiles.Trapped and slot <= 6) : (slot += 1) {
|
|
3471
3545
|
const id = side.order[slot - 1];
|
|
3472
3546
|
if (id == 0 or side.pokemon[id - 1].hp == 0) continue;
|
|
3473
|
-
out[n] = .{ .type = .Switch, .data = slot };
|
|
3547
|
+
out[n] = .{ .type = .Switch, .data = @intCast(slot) };
|
|
3474
3548
|
n += 1;
|
|
3475
3549
|
}
|
|
3476
3550
|
|
|
@@ -3503,7 +3577,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3503
3577
|
const struggle =
|
|
3504
3578
|
m.id == .Bide and (m.pp == 0 or active.volatiles.disable.move == slot);
|
|
3505
3579
|
const s = if (struggle) 0 else slot;
|
|
3506
|
-
out[n] = .{ .type = .Move, .data = s };
|
|
3580
|
+
out[n] = .{ .type = .Move, .data = @intCast(s) };
|
|
3507
3581
|
n += 1;
|
|
3508
3582
|
return n;
|
|
3509
3583
|
}
|
|
@@ -3517,7 +3591,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3517
3591
|
if (m.id == .None) break;
|
|
3518
3592
|
if (m.pp == 0) continue;
|
|
3519
3593
|
if (active.volatiles.disable.move == slot) continue;
|
|
3520
|
-
out[n] = .{ .type = .Move, .data = slot };
|
|
3594
|
+
out[n] = .{ .type = .Move, .data = @intCast(slot) };
|
|
3521
3595
|
n += 1;
|
|
3522
3596
|
}
|
|
3523
3597
|
// Struggle (Pokémon Showdown would use 'move 1' here)
|