@pkmn/engine 0.1.0-dev.30c63015 → 0.1.0-dev.3393edb3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -3
- package/README.md +41 -14
- package/build/pkg/addon/browser.d.ts +3 -0
- package/build/pkg/addon/browser.js +43 -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 +96 -0
- package/build/pkg/addon/node.js.map +1 -0
- package/build/pkg/addon.d.ts +2 -0
- package/build/pkg/addon.js +35 -58
- package/build/pkg/addon.js.map +1 -1
- package/build/pkg/data.d.ts +27 -5
- package/build/pkg/data.js.map +1 -1
- package/build/pkg/gen1.js +1 -1
- package/build/pkg/gen1.js.map +1 -1
- package/build/pkg/index.d.ts +24 -10
- package/build/pkg/index.js +15 -10
- package/build/pkg/index.js.map +1 -1
- package/build/pkg/protocol.d.ts +20 -1
- package/build/pkg/protocol.js +109 -107
- package/build/pkg/protocol.js.map +1 -1
- package/build/tools/debug.js +36 -70
- package/build/tools/debug.js.map +1 -1
- package/build/tools/display/debug.jsx +141 -0
- package/build/tools/display/debug.jsx.map +1 -0
- package/build/tools/display/index.js +132 -0
- package/build/tools/display/index.js.map +1 -0
- package/build/tools/display/ui.jsx +349 -0
- package/build/tools/display/ui.jsx.map +1 -0
- package/build/tools/display/util.js +18 -0
- package/build/tools/display/util.js.map +1 -0
- package/build.zig +206 -81
- package/package.json +54 -29
- package/src/bin/install-pkmn-engine +29 -19
- package/src/bin/pkmn-debug +1 -1
- package/src/lib/common/array.zig +77 -0
- package/src/lib/common/data.zig +4 -4
- package/src/lib/common/debug.zig +32 -6
- package/src/lib/common/js.zig +4 -2
- package/src/lib/common/optional.zig +14 -10
- package/src/lib/common/options.zig +4 -0
- package/src/lib/common/protocol.zig +222 -210
- package/src/lib/common/rational.zig +121 -27
- package/src/lib/common/rng.zig +7 -7
- package/src/lib/common/util.zig +50 -1
- package/src/lib/gen1/calc.zig +666 -133
- package/src/lib/gen1/chance.zig +375 -266
- package/src/lib/gen1/data/species.zig +1 -1
- package/src/lib/gen1/data.zig +17 -16
- package/src/lib/gen1/helpers.zig +89 -538
- package/src/lib/gen1/mechanics.zig +461 -372
- package/src/lib/gen2/calc.zig +34 -16
- package/src/lib/gen2/chance.zig +84 -496
- package/src/lib/gen2/data.zig +8 -7
- package/src/lib/gen2/generated.zig +1 -3
- package/src/lib/gen2/helpers.zig +41 -13
- package/src/lib/gen2/mechanics.zig +328 -300
- package/src/lib/{binding/node.zig → node.zig} +9 -12
- package/src/lib/pkmn.zig +32 -15
- package/src/lib/{binding/wasm.zig → wasm.zig} +2 -1
- package/src/tools/display/dom.ts +96 -0
- package/src/tools/display/ui.css +351 -0
- package/build/test/display.js +0 -400
- package/build/test/display.js.map +0 -1
- package/src/lib/gen1/README.md +0 -894
- package/src/lib/gen2/README.md +0 -329
- package/src/test/display/pkmn.html.tmpl +0 -368
|
@@ -6,7 +6,6 @@ const common = @import("../common/data.zig");
|
|
|
6
6
|
const DEBUG = @import("../common/debug.zig").print;
|
|
7
7
|
const protocol = @import("../common/protocol.zig");
|
|
8
8
|
const rng = @import("../common/rng.zig");
|
|
9
|
-
const util = @import("../common/util.zig");
|
|
10
9
|
|
|
11
10
|
const data = @import("data.zig");
|
|
12
11
|
const generated = @import("generated.zig");
|
|
@@ -26,30 +25,20 @@ const Result = common.Result;
|
|
|
26
25
|
|
|
27
26
|
const Activate = protocol.Activate;
|
|
28
27
|
const Boost = protocol.Boost;
|
|
29
|
-
const Cant = protocol.Cant;
|
|
30
|
-
const CureStatus = protocol.CureStatus;
|
|
31
28
|
const Damage = protocol.Damage;
|
|
32
29
|
const End = protocol.End;
|
|
33
|
-
const EndItem = protocol.EndItem;
|
|
34
30
|
const Fail = protocol.Fail;
|
|
35
31
|
const Heal = protocol.Heal;
|
|
36
|
-
const Immune = protocol.Immune;
|
|
37
|
-
const SetHP = protocol.SetHP;
|
|
38
32
|
const Start = protocol.Start;
|
|
39
33
|
|
|
40
34
|
const Gen12 = rng.Gen12;
|
|
41
35
|
|
|
42
|
-
const PointerType = util.PointerType;
|
|
43
|
-
|
|
44
36
|
const ActivePokemon = data.ActivePokemon;
|
|
45
37
|
const Boosts = data.Boosts;
|
|
46
38
|
const Effectiveness = data.Effectiveness;
|
|
47
39
|
const Item = data.Item;
|
|
48
40
|
const Move = data.Move;
|
|
49
|
-
const MoveSlot = data.MoveSlot;
|
|
50
|
-
const Pokemon = data.Pokemon;
|
|
51
41
|
const Side = data.Side;
|
|
52
|
-
const Species = data.Species;
|
|
53
42
|
const Stats = data.Stats;
|
|
54
43
|
const Status = data.Status;
|
|
55
44
|
const TriAttack = data.TriAttack;
|
|
@@ -111,6 +100,7 @@ pub const State = struct {
|
|
|
111
100
|
type: Type, // BATTLE_VARS_MOVE_TYPE
|
|
112
101
|
accuracy: u8, // wEnemyMoveStruct + MOVE_ACC
|
|
113
102
|
move: Move, // wCurPlayerMove
|
|
103
|
+
hit: u3 = 0,
|
|
114
104
|
from: Move = .None,
|
|
115
105
|
mslot: u4, // wCurMoveNum
|
|
116
106
|
first: bool, // wEnemyGoesFirst
|
|
@@ -145,11 +135,11 @@ fn start(battle: anytype, options: anytype) !Result {
|
|
|
145
135
|
const p1 = battle.side(.P1);
|
|
146
136
|
const p2 = battle.side(.P2);
|
|
147
137
|
|
|
148
|
-
|
|
138
|
+
const p1_slot = findFirstAlive(p1);
|
|
149
139
|
assert(!showdown or p1_slot == 1);
|
|
150
140
|
if (p1_slot == 0) return if (findFirstAlive(p2) == 0) Result.Tie else Result.Lose;
|
|
151
141
|
|
|
152
|
-
|
|
142
|
+
const p2_slot = findFirstAlive(p2);
|
|
153
143
|
assert(!showdown or p2_slot == 1);
|
|
154
144
|
if (p2_slot == 0) return Result.Win;
|
|
155
145
|
|
|
@@ -261,11 +251,10 @@ fn switchIn(battle: anytype, player: Player, slot: u8, kind: SwitchIn, options:
|
|
|
261
251
|
} else {
|
|
262
252
|
try options.log.switched(.{ battle.active(player), incoming });
|
|
263
253
|
}
|
|
264
|
-
options.chance.switched(player, side.pokemon[0].position, out);
|
|
265
254
|
|
|
266
255
|
if (side.conditions.Spikes and !active.types.includes(.Flying)) {
|
|
267
256
|
incoming.hp -|= @max(incoming.stats.hp / 8, 1);
|
|
268
|
-
try options.log.damage(.{ battle.active(player), incoming,
|
|
257
|
+
try options.log.damage(.{ battle.active(player), incoming, .Spikes });
|
|
269
258
|
}
|
|
270
259
|
}
|
|
271
260
|
|
|
@@ -408,7 +397,9 @@ fn executeMove(
|
|
|
408
397
|
|
|
409
398
|
assert(choice.type == .Move);
|
|
410
399
|
var mslot: u4 = @intCast(choice.data);
|
|
400
|
+
_ = &mslot;
|
|
411
401
|
var move = Move.Pound; // TODO
|
|
402
|
+
_ = &move;
|
|
412
403
|
|
|
413
404
|
if (!try beforeMove(battle, player, move, options)) return null;
|
|
414
405
|
|
|
@@ -438,31 +429,22 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
438
429
|
if (volatiles.Recharging) {
|
|
439
430
|
volatiles.Recharging = false;
|
|
440
431
|
resetCant(volatiles);
|
|
441
|
-
try log.cant(.{ ident,
|
|
432
|
+
try log.cant(.{ ident, .Recharge });
|
|
442
433
|
return false;
|
|
443
434
|
}
|
|
444
435
|
|
|
445
436
|
if (Status.is(stored.status, .SLP)) slp: {
|
|
446
437
|
const before = stored.status;
|
|
447
438
|
// Even if the EXT bit is set this will still correctly modify the sleep duration
|
|
448
|
-
// if (options.calc.modify(player, .sleep)) |extend| {
|
|
449
|
-
// if (!extend) stored.status = 0;
|
|
450
|
-
// } else {
|
|
451
439
|
stored.status -= 1;
|
|
452
|
-
// }
|
|
453
|
-
|
|
454
|
-
const duration = Status.duration(stored.status);
|
|
455
|
-
if (!Status.is(stored.status, .EXT)) {
|
|
456
|
-
try options.chance.sleep(player, duration);
|
|
457
|
-
}
|
|
458
440
|
|
|
459
|
-
if (duration == 0) {
|
|
460
|
-
try log.curestatus(.{ ident, before,
|
|
441
|
+
if (Status.duration(stored.status) == 0) {
|
|
442
|
+
try log.curestatus(.{ ident, before, .Message });
|
|
461
443
|
stored.status = 0; // clears EXT if present
|
|
462
444
|
resetCant(volatiles);
|
|
463
445
|
volatiles.Nightmare = false;
|
|
464
446
|
} else {
|
|
465
|
-
try log.cant(.{ ident,
|
|
447
|
+
try log.cant(.{ ident, .Sleep });
|
|
466
448
|
if (move == .Snore or move == .SleepTalk) break :slp;
|
|
467
449
|
resetCant(volatiles);
|
|
468
450
|
return false;
|
|
@@ -471,7 +453,7 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
471
453
|
|
|
472
454
|
if (Status.is(stored.status, .FRZ) and !(move == .FlameWheel or move == .SacredFire)) {
|
|
473
455
|
resetCant(volatiles);
|
|
474
|
-
try log.cant(.{ ident,
|
|
456
|
+
try log.cant(.{ ident, .Freeze });
|
|
475
457
|
return false;
|
|
476
458
|
}
|
|
477
459
|
|
|
@@ -481,39 +463,28 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
481
463
|
// if (!showdown) volatiles.Flinch = false;
|
|
482
464
|
volatiles.Flinch = false;
|
|
483
465
|
resetCant(volatiles);
|
|
484
|
-
try log.cant(.{ ident,
|
|
466
|
+
try log.cant(.{ ident, .Flinch });
|
|
485
467
|
return false;
|
|
486
468
|
}
|
|
487
469
|
|
|
488
470
|
if (volatiles.disable.duration > 0) {
|
|
489
|
-
// if (options.calc.modify(player, .disable)) |extend| {
|
|
490
|
-
// if (!extend) volatiles.disable.duration = 0;
|
|
491
|
-
// } else {
|
|
492
471
|
volatiles.disable.duration -= 1;
|
|
493
|
-
// }
|
|
494
|
-
try options.chance.disable(player, volatiles.disable.duration);
|
|
495
472
|
|
|
496
473
|
if (volatiles.disable.duration == 0) {
|
|
497
474
|
volatiles.disable.move = 0;
|
|
498
|
-
try log.end(.{ ident,
|
|
475
|
+
try log.end(.{ ident, .Disable });
|
|
499
476
|
}
|
|
500
477
|
}
|
|
501
478
|
|
|
502
479
|
if (volatiles.Confusion) {
|
|
503
480
|
assert(volatiles.confusion > 0);
|
|
504
|
-
|
|
505
|
-
// if (options.calc.modify(player, .confusion)) |extend| {
|
|
506
|
-
// if (!extend) volatiles.confusion = 0;
|
|
507
|
-
// } else {
|
|
508
481
|
volatiles.confusion -= 1;
|
|
509
|
-
// }
|
|
510
|
-
try options.chance.confusion(player, volatiles.confusion);
|
|
511
482
|
|
|
512
483
|
if (volatiles.confusion == 0) {
|
|
513
484
|
volatiles.Confusion = false;
|
|
514
|
-
try log.end(.{ ident,
|
|
485
|
+
try log.end(.{ ident, .Confusion });
|
|
515
486
|
} else {
|
|
516
|
-
try log.activate(ident, .Confusion);
|
|
487
|
+
try log.activate(.{ ident, .Confusion });
|
|
517
488
|
|
|
518
489
|
if (try Rolls.confused(battle, player, options)) {
|
|
519
490
|
volatiles.BeatUp = false;
|
|
@@ -528,11 +499,11 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
528
499
|
}
|
|
529
500
|
|
|
530
501
|
if (volatiles.Attract) {
|
|
531
|
-
try log.activate(ident, .Attract);
|
|
502
|
+
try log.activate(.{ ident, .Attract });
|
|
532
503
|
|
|
533
504
|
if (try Rolls.attract(battle, player, options)) {
|
|
534
505
|
resetCant(volatiles);
|
|
535
|
-
try log.cant(.{ ident,
|
|
506
|
+
try log.cant(.{ ident, .Attract });
|
|
536
507
|
return false;
|
|
537
508
|
}
|
|
538
509
|
}
|
|
@@ -543,14 +514,14 @@ fn beforeMove(battle: anytype, player: Player, move: Move, options: anytype) !bo
|
|
|
543
514
|
if (m != .None and m == move) {
|
|
544
515
|
side.active.volatiles.Charging = false;
|
|
545
516
|
resetCant(volatiles);
|
|
546
|
-
try options.log.cant(.{ ident,
|
|
517
|
+
try options.log.cant(.{ ident, .Disable, move });
|
|
547
518
|
return false;
|
|
548
519
|
}
|
|
549
520
|
}
|
|
550
521
|
|
|
551
522
|
if (Status.is(stored.status, .PAR) and try Rolls.paralyzed(battle, player, options)) {
|
|
552
523
|
resetCant(volatiles);
|
|
553
|
-
try log.cant(.{ ident,
|
|
524
|
+
try log.cant(.{ ident, .Paralysis });
|
|
554
525
|
return false;
|
|
555
526
|
}
|
|
556
527
|
|
|
@@ -570,7 +541,7 @@ fn resetCant(volatiles: *align(8) Volatiles) void {
|
|
|
570
541
|
|
|
571
542
|
pub fn canMove(battle: anytype, player: Player, state: *State, options: anytype) !bool {
|
|
572
543
|
var side = battle.side(player);
|
|
573
|
-
|
|
544
|
+
const volatiles = &side.active.volatiles;
|
|
574
545
|
|
|
575
546
|
// FIXME: pass in target to avoid lookup?
|
|
576
547
|
const target = if (Move.get(state.move).target == .Self) player else player.foe();
|
|
@@ -668,7 +639,7 @@ pub fn checkCriticalHit(battle: anytype, player: Player, state: *State, options:
|
|
|
668
639
|
}
|
|
669
640
|
assert(stage <= 4);
|
|
670
641
|
|
|
671
|
-
state.crit = try Rolls.criticalHit(battle, player, CRITICAL_HITS[stage], options);
|
|
642
|
+
state.crit = try Rolls.criticalHit(battle, player, state.hit, CRITICAL_HITS[stage], options);
|
|
672
643
|
}
|
|
673
644
|
|
|
674
645
|
pub fn calcDamage(
|
|
@@ -736,7 +707,7 @@ pub fn adjustDamage(battle: anytype, player: Player, state: *State, _: anytype)
|
|
|
736
707
|
|
|
737
708
|
pub fn randomizeDamage(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
738
709
|
if (state.damage <= 1) return;
|
|
739
|
-
const random = try Rolls.damage(battle, player, options);
|
|
710
|
+
const random = try Rolls.damage(battle, player, state.hit, options);
|
|
740
711
|
options.calc.base(player.foe(), state.damage);
|
|
741
712
|
state.damage = @intCast(@as(u32, state.damage) *% random / 255);
|
|
742
713
|
options.calc.final(player.foe(), state.damage);
|
|
@@ -953,26 +924,26 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
953
924
|
const damage = @max(stored.stats.hp / 8, 1);
|
|
954
925
|
stored.hp -|= damage;
|
|
955
926
|
// As above, Pokémon Showdown uses damage [of] but its not relevant
|
|
956
|
-
try options.log.damage(.{ ident, stored,
|
|
927
|
+
try options.log.damage(.{ ident, stored, .LeechSeed });
|
|
957
928
|
|
|
958
929
|
const before = foe_stored.hp;
|
|
959
930
|
foe_stored.hp = @min(foe_stored.hp + damage, foe_stored.stats.hp);
|
|
960
931
|
// Pokémon Showdown uses the less specific heal here instead of drain... because reasons?
|
|
961
|
-
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored,
|
|
932
|
+
if (foe_stored.hp > before) try options.log.heal(.{ foe_ident, foe_stored, .Silent });
|
|
962
933
|
if (stored.hp == 0) return true;
|
|
963
934
|
}
|
|
964
935
|
|
|
965
936
|
if (volatiles.Nightmare) {
|
|
966
937
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
967
938
|
// ibid
|
|
968
|
-
try options.log.damage(.{ ident, stored,
|
|
939
|
+
try options.log.damage(.{ ident, stored, .Nightmare });
|
|
969
940
|
if (stored.hp == 0) return true;
|
|
970
941
|
}
|
|
971
942
|
|
|
972
943
|
if (volatiles.Curse) {
|
|
973
944
|
stored.hp -= @max(stored.stats.hp / 4, 1);
|
|
974
945
|
// ibid
|
|
975
|
-
try options.log.damage(.{ ident, stored,
|
|
946
|
+
try options.log.damage(.{ ident, stored, .Curse });
|
|
976
947
|
if (stored.hp == 0) return true;
|
|
977
948
|
}
|
|
978
949
|
|
|
@@ -993,7 +964,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
993
964
|
future_sight.count -= 1;
|
|
994
965
|
assert(future_sight.count != 0);
|
|
995
966
|
if (future_sight.count == 1) {
|
|
996
|
-
try options.log.end(.{ battle.active(player),
|
|
967
|
+
try options.log.end(.{ battle.active(player), .FutureSight });
|
|
997
968
|
// TODO doMove
|
|
998
969
|
}
|
|
999
970
|
}
|
|
@@ -1002,7 +973,6 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1002
973
|
|
|
1003
974
|
// Weather
|
|
1004
975
|
if (battle.field.weather != .None) {
|
|
1005
|
-
assert(battle.field.weather_duration > 0);
|
|
1006
976
|
battle.field.weather_duration -= 1;
|
|
1007
977
|
|
|
1008
978
|
if (battle.field.weather_duration == 0) {
|
|
@@ -1019,7 +989,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1019
989
|
try options.log.damage(.{
|
|
1020
990
|
battle.active(player),
|
|
1021
991
|
side.stored(),
|
|
1022
|
-
|
|
992
|
+
.Sandstorm,
|
|
1023
993
|
});
|
|
1024
994
|
}
|
|
1025
995
|
}
|
|
@@ -1032,20 +1002,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1032
1002
|
inline for (players) |player| {
|
|
1033
1003
|
var side = battle.side(player);
|
|
1034
1004
|
var volatiles = &side.active.volatiles;
|
|
1005
|
+
const ident = battle.active(player);
|
|
1035
1006
|
|
|
1036
1007
|
if (volatiles.bind.duration > 0 and !volatiles.Substitute) {
|
|
1037
1008
|
volatiles.bind.duration -= 1;
|
|
1038
1009
|
if (volatiles.bind.duration == 0) {
|
|
1039
1010
|
const reason = @intFromEnum(End.Bind) + volatiles.bind.reason - 1;
|
|
1040
|
-
try options.log.end(.{
|
|
1011
|
+
try options.log.end(.{ ident, @as(End, @enumFromInt(reason)) });
|
|
1041
1012
|
} else {
|
|
1042
1013
|
var reason = @intFromEnum(Activate.Bind) + volatiles.bind.reason - 1;
|
|
1043
|
-
try options.log.activate(
|
|
1014
|
+
try options.log.activate(.{ ident, @as(Activate, @enumFromInt(reason)) });
|
|
1044
1015
|
|
|
1045
1016
|
reason = @intFromEnum(Damage.Bind) + volatiles.bind.reason - 1;
|
|
1046
1017
|
side.stored().hp -|= @max(side.stored().stats.hp / 16, 1);
|
|
1047
1018
|
try options.log.damage(.{
|
|
1048
|
-
|
|
1019
|
+
ident,
|
|
1049
1020
|
side.stored(),
|
|
1050
1021
|
@as(Damage, @enumFromInt(reason)),
|
|
1051
1022
|
});
|
|
@@ -1060,12 +1031,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1060
1031
|
var volatiles = &side.active.volatiles;
|
|
1061
1032
|
|
|
1062
1033
|
if (volatiles.PerishSong) {
|
|
1063
|
-
assert(volatiles.perish_song > 0);
|
|
1064
1034
|
volatiles.perish_song -= 1;
|
|
1065
1035
|
|
|
1066
1036
|
assert(volatiles.perish_song < 3);
|
|
1067
1037
|
const reason = @intFromEnum(Start.PerishSong0) + volatiles.perish_song;
|
|
1068
|
-
try options.log.start(battle.active(player), @enumFromInt(reason));
|
|
1038
|
+
try options.log.start(.{ battle.active(player), @as(Start, @enumFromInt(reason)) });
|
|
1069
1039
|
|
|
1070
1040
|
if (volatiles.perish_song == 0) {
|
|
1071
1041
|
volatiles.PerishSong = false;
|
|
@@ -1083,14 +1053,14 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1083
1053
|
const before = stored.hp;
|
|
1084
1054
|
stored.hp = @min(stored.hp + @max(stored.stats.hp / 16, 1), stored.stats.hp);
|
|
1085
1055
|
if (stored.hp > before) {
|
|
1086
|
-
try options.log.heal(.{ battle.active(player), stored,
|
|
1056
|
+
try options.log.heal(.{ battle.active(player), stored, .Leftovers });
|
|
1087
1057
|
}
|
|
1088
1058
|
}
|
|
1089
1059
|
}
|
|
1090
1060
|
|
|
1091
1061
|
// Mystery Berry
|
|
1092
1062
|
inline for (players) |player| {
|
|
1093
|
-
|
|
1063
|
+
const stored = battle.side(player).stored();
|
|
1094
1064
|
|
|
1095
1065
|
if (stored.item == .MysteryBerry) {
|
|
1096
1066
|
// TODO stored and active moves, transform, etc
|
|
@@ -1105,8 +1075,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1105
1075
|
!side.active.volatiles.frozen and
|
|
1106
1076
|
try Rolls.defrost(battle, player, options))
|
|
1107
1077
|
{
|
|
1108
|
-
|
|
1109
|
-
try options.log.curestatus(.{ battle.active(player), side.stored().status, reason });
|
|
1078
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1110
1079
|
side.stored().status = 0;
|
|
1111
1080
|
}
|
|
1112
1081
|
}
|
|
@@ -1147,7 +1116,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1147
1116
|
stored.hp < stored.stats.hp;
|
|
1148
1117
|
if (proc) {
|
|
1149
1118
|
assert(stored.hp != 0);
|
|
1150
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1119
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1151
1120
|
stored.item = .None;
|
|
1152
1121
|
|
|
1153
1122
|
const offset = num - @intFromEnum(Item.Berry);
|
|
@@ -1164,7 +1133,7 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1164
1133
|
|
|
1165
1134
|
if (proc) {
|
|
1166
1135
|
assert(Status.any(stored.status));
|
|
1167
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1136
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1168
1137
|
stored.item = .None;
|
|
1169
1138
|
|
|
1170
1139
|
// FIXME wtf?
|
|
@@ -1172,21 +1141,21 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1172
1141
|
// side.active.volatiles.Nightmare = false;
|
|
1173
1142
|
if (stored.item == .MiracleBerry) {
|
|
1174
1143
|
side.active.volatiles.Confusion = false;
|
|
1175
|
-
try options.log.end(.{ ident,
|
|
1144
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1176
1145
|
}
|
|
1177
1146
|
stored.status = 0;
|
|
1178
1147
|
|
|
1179
1148
|
// FIXME recalc stats
|
|
1180
1149
|
|
|
1181
|
-
try options.log.curestatus(.{ ident, stored.status,
|
|
1150
|
+
try options.log.curestatus(.{ ident, stored.status, .Message });
|
|
1182
1151
|
}
|
|
1183
1152
|
} else if (num == @intFromEnum(Item.BitterBerry)) {
|
|
1184
1153
|
if (side.active.volatiles.Confusion) {
|
|
1185
|
-
try options.log.enditem(.{ ident, stored.item,
|
|
1154
|
+
try options.log.enditem(.{ ident, stored.item, .Eat });
|
|
1186
1155
|
stored.item = .None;
|
|
1187
1156
|
|
|
1188
1157
|
side.active.volatiles.Confusion = false;
|
|
1189
|
-
try options.log.end(.{ ident,
|
|
1158
|
+
try options.log.end(.{ ident, .Confusion });
|
|
1190
1159
|
}
|
|
1191
1160
|
}
|
|
1192
1161
|
}
|
|
@@ -1198,12 +1167,11 @@ fn betweenTurns(battle: anytype, mslot: u4, options: anytype) !?Result {
|
|
|
1198
1167
|
var volatiles = &side.active.volatiles;
|
|
1199
1168
|
|
|
1200
1169
|
if (volatiles.Encore) {
|
|
1201
|
-
assert(volatiles.encore > 0);
|
|
1202
1170
|
volatiles.encore -= 1;
|
|
1203
1171
|
|
|
1204
1172
|
if (volatiles.encore == 0 or side.active.move(mslot).pp == 0) {
|
|
1205
1173
|
volatiles.Encore = false;
|
|
1206
|
-
try options.log.end(.{ battle.active(player),
|
|
1174
|
+
try options.log.end(.{ battle.active(player), .Encore });
|
|
1207
1175
|
}
|
|
1208
1176
|
}
|
|
1209
1177
|
}
|
|
@@ -1220,7 +1188,6 @@ fn updateSideCondition(
|
|
|
1220
1188
|
) !void {
|
|
1221
1189
|
var side = battle.side(player);
|
|
1222
1190
|
if (@field(side.conditions, key)) {
|
|
1223
|
-
assert(@field(side.conditions, value) > 0);
|
|
1224
1191
|
@field(side.conditions, value) -= 1;
|
|
1225
1192
|
|
|
1226
1193
|
if (@field(side.conditions, value) == 0) {
|
|
@@ -1256,7 +1223,7 @@ pub fn buildRage(battle: anytype, player: Player, state: *State, options: anytyp
|
|
|
1256
1223
|
}
|
|
1257
1224
|
} else {
|
|
1258
1225
|
foe.active.volatiles.rage +|= 1;
|
|
1259
|
-
try options.log.activate(battle.active(player), .Rage);
|
|
1226
|
+
try options.log.activate(.{ battle.active(player), .Rage });
|
|
1260
1227
|
}
|
|
1261
1228
|
}
|
|
1262
1229
|
}
|
|
@@ -1279,7 +1246,7 @@ pub fn destinyBond(battle: anytype, player: Player, _: *State, options: anytype)
|
|
|
1279
1246
|
|
|
1280
1247
|
if (foe.active.volatiles.DestinyBond) {
|
|
1281
1248
|
battle.side(player).stored().hp = 0;
|
|
1282
|
-
try options.log.activate(battle.active(player.foe()), .DestinyBond);
|
|
1249
|
+
try options.log.activate(.{ battle.active(player.foe()), .DestinyBond });
|
|
1283
1250
|
}
|
|
1284
1251
|
return true;
|
|
1285
1252
|
}
|
|
@@ -1292,14 +1259,14 @@ pub const Effects = struct {
|
|
|
1292
1259
|
const gender: u8 = @intFromEnum(side.stored().dvs.gender);
|
|
1293
1260
|
const foe_gender: u8 = @intFromEnum(foe.stored().dvs.gender);
|
|
1294
1261
|
if (gender + foe_gender != 1) {
|
|
1295
|
-
return options.log.immune(.{ battle.active(player.foe()),
|
|
1262
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1296
1263
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1297
1264
|
try options.log.lastmiss(.{});
|
|
1298
1265
|
return options.log.miss(.{battle.active(player)});
|
|
1299
1266
|
}
|
|
1300
1267
|
|
|
1301
1268
|
foe.active.volatiles.Attract = true;
|
|
1302
|
-
try options.log.start(battle.active(player.foe()), .Attract);
|
|
1269
|
+
try options.log.start(.{ battle.active(player.foe()), .Attract });
|
|
1303
1270
|
}
|
|
1304
1271
|
|
|
1305
1272
|
pub fn batonPass(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1311,14 +1278,32 @@ pub const Effects = struct {
|
|
|
1311
1278
|
}
|
|
1312
1279
|
|
|
1313
1280
|
pub fn bellyDrum(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1281
|
+
assert(!state.miss);
|
|
1282
|
+
var side = battle.side(player);
|
|
1283
|
+
|
|
1284
|
+
const ident = battle.active(player);
|
|
1285
|
+
|
|
1286
|
+
assert(side.active.boosts.atk >= -6 and side.active.boosts.atk <= 6);
|
|
1287
|
+
if (side.active.boosts.atk == 6 or side.active.boosts.atk == MAX_STAT_VALUE) {
|
|
1288
|
+
return options.log.fail(.{ ident, .None });
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const hp = @max(side.stored().stats.hp / 2, 1);
|
|
1292
|
+
if (side.stored().hp <= hp) {
|
|
1293
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1294
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 2), .Silent });
|
|
1295
|
+
return options.log.fail(.{ ident, .None });
|
|
1296
|
+
} else {
|
|
1297
|
+
side.stored().hp -= hp;
|
|
1298
|
+
try options.log.damage(.{ ident, side.stored(), .None });
|
|
1299
|
+
|
|
1300
|
+
const before = side.active.boosts.atk;
|
|
1301
|
+
var i: usize = 0;
|
|
1302
|
+
while (i < 6 and side.active.stats.atk < MAX_STAT_VALUE) : (i += 1) {
|
|
1303
|
+
try boostStat(battle, side, &side.active.stats, &side.active.boosts, "atk", 2);
|
|
1304
|
+
}
|
|
1305
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, side.active.boosts.atk - before) });
|
|
1306
|
+
}
|
|
1322
1307
|
}
|
|
1323
1308
|
|
|
1324
1309
|
pub const bide = struct {
|
|
@@ -1330,18 +1315,11 @@ pub const Effects = struct {
|
|
|
1330
1315
|
|
|
1331
1316
|
if (!volatiles.Bide) return;
|
|
1332
1317
|
|
|
1333
|
-
assert(volatiles.attacks > 0);
|
|
1334
|
-
// if (options.calc.modify(player, .attacking)) |extend| {
|
|
1335
|
-
// if (!extend) volatiles.attacks = 0;
|
|
1336
|
-
// } else {
|
|
1337
1318
|
volatiles.attacks -= 1;
|
|
1338
|
-
|
|
1339
|
-
try options.chance.attacking(player, volatiles.attacks);
|
|
1340
|
-
|
|
1341
|
-
if (volatiles.attacks != 0) return options.log.activate(ident, .Bide);
|
|
1319
|
+
if (volatiles.attacks != 0) return options.log.activate(.{ ident, .Bide });
|
|
1342
1320
|
|
|
1343
1321
|
volatiles.Bide = false;
|
|
1344
|
-
try options.log.end(.{ ident,
|
|
1322
|
+
try options.log.end(.{ ident, .Bide });
|
|
1345
1323
|
|
|
1346
1324
|
state.damage = volatiles.bide *% 2;
|
|
1347
1325
|
volatiles.bide = 0;
|
|
@@ -1378,7 +1356,7 @@ pub const Effects = struct {
|
|
|
1378
1356
|
try options.log.curestatus(.{
|
|
1379
1357
|
battle.active(player.foe()),
|
|
1380
1358
|
foe_stored.status,
|
|
1381
|
-
|
|
1359
|
+
.Message,
|
|
1382
1360
|
});
|
|
1383
1361
|
foe_stored.status = 0;
|
|
1384
1362
|
}
|
|
@@ -1395,8 +1373,7 @@ pub const Effects = struct {
|
|
|
1395
1373
|
foe_stored.status = Status.init(.BRN);
|
|
1396
1374
|
foe.active.stats.atk = @max(foe.active.stats.atk / 2, 1);
|
|
1397
1375
|
|
|
1398
|
-
|
|
1399
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
|
|
1376
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
1400
1377
|
|
|
1401
1378
|
// TODO: check for status berry
|
|
1402
1379
|
}
|
|
@@ -1416,7 +1393,7 @@ pub const Effects = struct {
|
|
|
1416
1393
|
for (side.active.moves) |m| {
|
|
1417
1394
|
if (convertible(side.active, m.id)) n += 1;
|
|
1418
1395
|
}
|
|
1419
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
1396
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1420
1397
|
|
|
1421
1398
|
side.active.types.type1 = try Rolls.conversion(battle, player, n, options);
|
|
1422
1399
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1426,9 +1403,9 @@ pub const Effects = struct {
|
|
|
1426
1403
|
var side = battle.side(player);
|
|
1427
1404
|
|
|
1428
1405
|
const last = battle.foe(player).lastMove(false);
|
|
1429
|
-
if (last == .None) return options.log.fail(.{ battle.active(player),
|
|
1406
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1430
1407
|
const move = Move.get(last);
|
|
1431
|
-
if (move.type == .@"???") return options.log.fail(.{ battle.active(player),
|
|
1408
|
+
if (move.type == .@"???") return options.log.fail(.{ battle.active(player), .None });
|
|
1432
1409
|
|
|
1433
1410
|
side.active.types.type1 = try Rolls.conversion2(battle, player, move.type, options);
|
|
1434
1411
|
side.active.types.type2 = side.active.types.type1;
|
|
@@ -1473,38 +1450,38 @@ pub const Effects = struct {
|
|
|
1473
1450
|
try options.log.lastmiss(.{});
|
|
1474
1451
|
return options.log.miss(.{battle.active(player)});
|
|
1475
1452
|
} else if (foe.active.volatiles.Substitute) {
|
|
1476
|
-
return options.log.
|
|
1453
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1477
1454
|
} else if (foe.active.volatiles.Curse) {
|
|
1478
|
-
return options.log.fail(.{ foe_ident,
|
|
1455
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1479
1456
|
}
|
|
1480
1457
|
|
|
1481
1458
|
foe.active.volatiles.Curse = true;
|
|
1482
|
-
try options.log.start(foe_ident, .Curse);
|
|
1459
|
+
try options.log.start(.{ foe_ident, .Curse });
|
|
1483
1460
|
|
|
1484
1461
|
side.stored().hp -= @max(side.stored().stats.hp / 2, 1);
|
|
1485
|
-
try options.log.damage(.{ ident, side.stored(),
|
|
1462
|
+
try options.log.damage(.{ ident, side.stored(), .Curse });
|
|
1486
1463
|
} else {
|
|
1487
|
-
|
|
1488
|
-
|
|
1464
|
+
const stats = &side.active.stats;
|
|
1465
|
+
const boosts = &side.active.boosts;
|
|
1489
1466
|
|
|
1490
1467
|
const max_atk = boosts.atk == 6 or stats.atk == MAX_STAT_VALUE;
|
|
1491
1468
|
const max_def = boosts.def == 6 or stats.def == MAX_STAT_VALUE;
|
|
1492
|
-
if (max_atk and max_def) return options.log.fail(.{ ident,
|
|
1469
|
+
if (max_atk and max_def) return options.log.fail(.{ ident, .None });
|
|
1493
1470
|
|
|
1494
1471
|
if (!(boosts.spe == -6 or stats.spe == 1)) {
|
|
1495
1472
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
1496
1473
|
try unboostStat(battle, side, stats, boosts, "spe", 1);
|
|
1497
|
-
try options.log.boost(.{ ident,
|
|
1474
|
+
try options.log.boost(.{ ident, .Speed, @as(i8, -1) });
|
|
1498
1475
|
}
|
|
1499
1476
|
if (!max_atk) {
|
|
1500
1477
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
1501
1478
|
try boostStat(battle, side, stats, boosts, "atk", 1);
|
|
1502
|
-
try options.log.boost(.{ ident,
|
|
1479
|
+
try options.log.boost(.{ ident, .Attack, @as(i8, 1) });
|
|
1503
1480
|
}
|
|
1504
1481
|
if (!max_def) {
|
|
1505
1482
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
1506
1483
|
try boostStat(battle, side, stats, boosts, "def", 1);
|
|
1507
|
-
try options.log.boost(.{ ident,
|
|
1484
|
+
try options.log.boost(.{ ident, .Defense, @as(i8, 1) });
|
|
1508
1485
|
}
|
|
1509
1486
|
}
|
|
1510
1487
|
}
|
|
@@ -1516,9 +1493,7 @@ pub const Effects = struct {
|
|
|
1516
1493
|
pub fn defrost(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1517
1494
|
var side = battle.side(player);
|
|
1518
1495
|
assert(Status.is(side.stored().status, .FRZ));
|
|
1519
|
-
|
|
1520
|
-
const reason: CureStatus = .Message;
|
|
1521
|
-
try options.log.curestatus(.{ battle.active(player), side.stored().status, reason });
|
|
1496
|
+
try options.log.curestatus(.{ battle.active(player), side.stored().status, .Message });
|
|
1522
1497
|
side.stored().status = 0;
|
|
1523
1498
|
}
|
|
1524
1499
|
|
|
@@ -1532,14 +1507,10 @@ pub const Effects = struct {
|
|
|
1532
1507
|
var volatiles = &foe.active.volatiles;
|
|
1533
1508
|
const foe_ident = battle.active(player.foe());
|
|
1534
1509
|
|
|
1535
|
-
if (volatiles.disable.move != 0) {
|
|
1536
|
-
return options.log.fail(.{ foe_ident, Fail.None });
|
|
1537
|
-
}
|
|
1510
|
+
if (volatiles.disable.move != 0) return options.log.fail(.{ foe_ident, .None });
|
|
1538
1511
|
|
|
1539
1512
|
const last = battle.foe(player).lastMove(false);
|
|
1540
|
-
if (last == .None or last == .Struggle) {
|
|
1541
|
-
return options.log.fail(.{ foe_ident, Fail.None });
|
|
1542
|
-
}
|
|
1513
|
+
if (last == .None or last == .Struggle) return options.log.fail(.{ foe_ident, .None });
|
|
1543
1514
|
|
|
1544
1515
|
var slot: u3 = 0;
|
|
1545
1516
|
for (foe.active.moves) |m| {
|
|
@@ -1548,12 +1519,12 @@ pub const Effects = struct {
|
|
|
1548
1519
|
if (m.id == last) break;
|
|
1549
1520
|
}
|
|
1550
1521
|
const move = foe.active.move(slot);
|
|
1551
|
-
if (move.pp == 0) return options.log.fail(.{ foe_ident,
|
|
1522
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1552
1523
|
|
|
1553
1524
|
volatiles.disable.move = slot;
|
|
1554
1525
|
volatiles.disable.duration = Rolls.disableDuration(battle, player, options);
|
|
1555
1526
|
|
|
1556
|
-
try options.log.
|
|
1527
|
+
try options.log.start(.{ foe_ident, .Disable, move.id });
|
|
1557
1528
|
}
|
|
1558
1529
|
|
|
1559
1530
|
pub const double = struct {
|
|
@@ -1579,7 +1550,7 @@ pub const Effects = struct {
|
|
|
1579
1550
|
if (stored.hp == stored.stats.hp) return;
|
|
1580
1551
|
stored.hp = @min(stored.stats.hp, stored.hp + @max(state.damage / 2, 1));
|
|
1581
1552
|
|
|
1582
|
-
try options.log.heal(.{ ident, stored,
|
|
1553
|
+
try options.log.heal(.{ ident, stored, .Drain, battle.active(player.foe()) });
|
|
1583
1554
|
}
|
|
1584
1555
|
|
|
1585
1556
|
pub fn encore(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1591,7 +1562,7 @@ pub const Effects = struct {
|
|
|
1591
1562
|
const failed = volatiles.Encore or
|
|
1592
1563
|
(last == .None or last == .Struggle or
|
|
1593
1564
|
last == .Encore or .last == .MirrorMove);
|
|
1594
|
-
if (failed) return options.log.fail(.{ foe_ident,
|
|
1565
|
+
if (failed) return options.log.fail(.{ foe_ident, .None });
|
|
1595
1566
|
|
|
1596
1567
|
var slot: u3 = 0;
|
|
1597
1568
|
for (foe.active.moves) |m| {
|
|
@@ -1600,7 +1571,7 @@ pub const Effects = struct {
|
|
|
1600
1571
|
if (m.id == last) break;
|
|
1601
1572
|
}
|
|
1602
1573
|
const move = foe.active.move(slot);
|
|
1603
|
-
if (move.pp == 0) return options.log.fail(.{ foe_ident,
|
|
1574
|
+
if (move.pp == 0) return options.log.fail(.{ foe_ident, .None });
|
|
1604
1575
|
|
|
1605
1576
|
volatiles.Encore = true;
|
|
1606
1577
|
volatiles.encore = Rolls.encoreDuration(battle, player, options);
|
|
@@ -1608,11 +1579,19 @@ pub const Effects = struct {
|
|
|
1608
1579
|
// TODO ???
|
|
1609
1580
|
_ = state;
|
|
1610
1581
|
|
|
1611
|
-
try options.log.
|
|
1582
|
+
try options.log.start(.{ foe_ident, .Encore, move.id });
|
|
1612
1583
|
}
|
|
1613
1584
|
|
|
1614
|
-
pub fn explode(battle: anytype, player: Player,
|
|
1615
|
-
|
|
1585
|
+
pub fn explode(battle: anytype, player: Player, _: *State, _: anytype) !void {
|
|
1586
|
+
var side = battle.side(player);
|
|
1587
|
+
var stored = side.stored();
|
|
1588
|
+
|
|
1589
|
+
stored.hp = 0;
|
|
1590
|
+
// Pokémon Showdown sets the status to 0 on faint(), and we need to do the same to be able
|
|
1591
|
+
// to correctly implement Pokémon Showdown's dumb speed-based switch mechanics
|
|
1592
|
+
if (!showdown) stored.status = 0;
|
|
1593
|
+
side.active.volatiles.LeechSeed = false;
|
|
1594
|
+
side.active.volatiles.DestinyBond = false;
|
|
1616
1595
|
}
|
|
1617
1596
|
|
|
1618
1597
|
pub fn falseSwipe(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
@@ -1662,12 +1641,11 @@ pub const Effects = struct {
|
|
|
1662
1641
|
var side = battle.side(player);
|
|
1663
1642
|
|
|
1664
1643
|
if (side.active.volatiles.FocusEnergy) {
|
|
1665
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
1666
|
-
return;
|
|
1644
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
1667
1645
|
}
|
|
1668
1646
|
|
|
1669
1647
|
side.active.volatiles.FocusEnergy = true;
|
|
1670
|
-
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1648
|
+
try options.log.start(.{ battle.active(player), .FocusEnergy });
|
|
1671
1649
|
}
|
|
1672
1650
|
|
|
1673
1651
|
var SLOTS: [5]u4 = .{ 0, 0, 0, 0, 0 };
|
|
@@ -1677,7 +1655,7 @@ pub const Effects = struct {
|
|
|
1677
1655
|
|
|
1678
1656
|
const foe = battle.foe(player);
|
|
1679
1657
|
|
|
1680
|
-
if (state.first) return options.log.fail(.{ battle.active(player),
|
|
1658
|
+
if (state.first) return options.log.fail(.{ battle.active(player), .None });
|
|
1681
1659
|
|
|
1682
1660
|
var n: u4 = 0;
|
|
1683
1661
|
var i: u4 = 0;
|
|
@@ -1691,7 +1669,7 @@ pub const Effects = struct {
|
|
|
1691
1669
|
}
|
|
1692
1670
|
}
|
|
1693
1671
|
|
|
1694
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
1672
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
1695
1673
|
|
|
1696
1674
|
const slot = try Rolls.forceSwitch(battle, player, SLOTS[0..i], n, options);
|
|
1697
1675
|
try switchIn(battle, player.foe(), slot, .Drag, options);
|
|
@@ -1704,11 +1682,11 @@ pub const Effects = struct {
|
|
|
1704
1682
|
try options.log.lastmiss(.{});
|
|
1705
1683
|
return options.log.miss(.{battle.active(player)});
|
|
1706
1684
|
} else if (foe.active.volatiles.Foresight) {
|
|
1707
|
-
return options.log.fail(.{ battle.active(player),
|
|
1685
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
1708
1686
|
}
|
|
1709
1687
|
|
|
1710
1688
|
foe.active.volatiles.Foresight = true;
|
|
1711
|
-
try options.log.start(battle.active(player.foe()), .FocusEnergy);
|
|
1689
|
+
try options.log.start(.{ battle.active(player.foe()), .FocusEnergy });
|
|
1712
1690
|
}
|
|
1713
1691
|
|
|
1714
1692
|
pub fn freezeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1731,7 +1709,7 @@ pub const Effects = struct {
|
|
|
1731
1709
|
if (showdown) for (foe.pokemon) |p| if (Status.is(p.status, .FRZ)) return;
|
|
1732
1710
|
|
|
1733
1711
|
foe_stored.status = Status.init(.FRZ);
|
|
1734
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
1712
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
1735
1713
|
|
|
1736
1714
|
// TODO: check for status berry
|
|
1737
1715
|
|
|
@@ -1739,8 +1717,26 @@ pub const Effects = struct {
|
|
|
1739
1717
|
foe.active.volatiles.frozen = true;
|
|
1740
1718
|
}
|
|
1741
1719
|
|
|
1742
|
-
|
|
1743
|
-
|
|
1720
|
+
// TODO FIXME showdown calls this before calcDamage (after checkHit) and modifies state.bp!
|
|
1721
|
+
pub fn furyCutter(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
1722
|
+
var side = battle.side(player);
|
|
1723
|
+
var volatiles = &side.active.volatiles;
|
|
1724
|
+
|
|
1725
|
+
if (state.miss) {
|
|
1726
|
+
volatiles.fury_cutter = 0;
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
assert(volatiles.fury_cutter >= 0 and volatiles.fury_cutter <= 5);
|
|
1731
|
+
if (volatiles.fury_cutter == 5) return;
|
|
1732
|
+
volatiles.fury_cutter += 1;
|
|
1733
|
+
|
|
1734
|
+
if (showdown) {
|
|
1735
|
+
state.bp *= 2;
|
|
1736
|
+
assert(state.bp <= 160);
|
|
1737
|
+
} else {
|
|
1738
|
+
state.damage *|= 2;
|
|
1739
|
+
}
|
|
1744
1740
|
}
|
|
1745
1741
|
|
|
1746
1742
|
pub fn futureSight(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1774,7 +1770,7 @@ pub const Effects = struct {
|
|
|
1774
1770
|
var stored = side.stored();
|
|
1775
1771
|
const ident = battle.active(player);
|
|
1776
1772
|
|
|
1777
|
-
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident,
|
|
1773
|
+
if (stored.stats.hp == stored.hp) return options.log.fail(.{ ident, .None });
|
|
1778
1774
|
|
|
1779
1775
|
const rest = state.move == .Rest;
|
|
1780
1776
|
if (rest) {
|
|
@@ -1783,7 +1779,7 @@ pub const Effects = struct {
|
|
|
1783
1779
|
side.active.volatiles.Toxic = false;
|
|
1784
1780
|
stored.status = Status.slf(2);
|
|
1785
1781
|
recomputeStats(battle, side);
|
|
1786
|
-
try options.log.status(.{ ident, stored.status,
|
|
1782
|
+
try options.log.status(.{ ident, stored.status, .From, Move.Rest });
|
|
1787
1783
|
stored.hp = stored.stats.hp;
|
|
1788
1784
|
} else {
|
|
1789
1785
|
stored.hp = @min(stored.stats.hp, stored.hp + (stored.stats.hp / 2));
|
|
@@ -1821,7 +1817,7 @@ pub const Effects = struct {
|
|
|
1821
1817
|
// Invulnerability trumps type immunity on Pokémon Showdown
|
|
1822
1818
|
const invulnerable = foe.active.volatiles.Underground or foe.active.volatiles.Flying;
|
|
1823
1819
|
if (!invulnerable and foe.active.types.includes(.Grass)) {
|
|
1824
|
-
return options.log.immune(.{ battle.active(player.foe()),
|
|
1820
|
+
return options.log.immune(.{ battle.active(player.foe()), .None });
|
|
1825
1821
|
}
|
|
1826
1822
|
try checkHit(battle, player, state, options);
|
|
1827
1823
|
if (state.miss) {
|
|
@@ -1841,7 +1837,7 @@ pub const Effects = struct {
|
|
|
1841
1837
|
|
|
1842
1838
|
foe.active.volatiles.LeechSeed = true;
|
|
1843
1839
|
|
|
1844
|
-
try options.log.start(battle.active(player.foe()), .LeechSeed);
|
|
1840
|
+
try options.log.start(.{ battle.active(player.foe()), .LeechSeed });
|
|
1845
1841
|
}
|
|
1846
1842
|
|
|
1847
1843
|
pub fn lockOn(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1849,20 +1845,20 @@ pub const Effects = struct {
|
|
|
1849
1845
|
const foe_ident = battle.active(player.foe());
|
|
1850
1846
|
|
|
1851
1847
|
if (foe.active.volatiles.Substitute) {
|
|
1852
|
-
return options.log.
|
|
1848
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1853
1849
|
} else if (state.miss) {
|
|
1854
|
-
return options.log.fail(.{ foe_ident,
|
|
1850
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1855
1851
|
}
|
|
1856
1852
|
|
|
1857
1853
|
foe.active.volatiles.LockOn = true;
|
|
1858
|
-
try options.log.activate(battle.active(player), .LockOn);
|
|
1854
|
+
try options.log.activate(.{ battle.active(player), .LockOn });
|
|
1859
1855
|
}
|
|
1860
1856
|
|
|
1861
1857
|
const MAGNITUDE_POWER = [_]u8{ 10, 30, 50, 70, 90, 110, 150 };
|
|
1862
1858
|
|
|
1863
1859
|
pub fn magnitude(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1864
1860
|
const num = try Rolls.magnitude(battle, player, options);
|
|
1865
|
-
try options.log.
|
|
1861
|
+
try options.log.activate(.{ battle.active(player), .Magnitude, num });
|
|
1866
1862
|
state.bp = MAGNITUDE_POWER[num - 4];
|
|
1867
1863
|
}
|
|
1868
1864
|
|
|
@@ -1870,11 +1866,11 @@ pub const Effects = struct {
|
|
|
1870
1866
|
var foe = battle.foe(player);
|
|
1871
1867
|
|
|
1872
1868
|
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1873
|
-
if (!showdown) return options.log.fail(.{ battle.active(player.foe()),
|
|
1869
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1874
1870
|
try options.log.lastmiss(.{});
|
|
1875
1871
|
return options.log.miss(.{battle.active(player)});
|
|
1876
1872
|
} else if (foe.active.volatiles.Trapped) {
|
|
1877
|
-
return options.log.fail(.{ battle.active(player.foe()),
|
|
1873
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1878
1874
|
}
|
|
1879
1875
|
|
|
1880
1876
|
foe.active.volatiles.Trapped = true;
|
|
@@ -1901,7 +1897,29 @@ pub const Effects = struct {
|
|
|
1901
1897
|
}
|
|
1902
1898
|
|
|
1903
1899
|
pub fn mimic(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
1904
|
-
|
|
1900
|
+
var side = battle.side(player);
|
|
1901
|
+
|
|
1902
|
+
if (state.miss) {
|
|
1903
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1904
|
+
try options.log.lastmiss(.{});
|
|
1905
|
+
return options.log.miss(.{battle.active(player)});
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
const move = battle.foe(player).lastMove(false);
|
|
1909
|
+
if (move == .None or move == .Struggle) {
|
|
1910
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
var slot: u4 = 0;
|
|
1914
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1915
|
+
if (m.id == move) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1916
|
+
if (m.id == .Mimic) slot = @intCast(i + 1);
|
|
1917
|
+
}
|
|
1918
|
+
assert(slot != 0 and slot == state.mslot);
|
|
1919
|
+
|
|
1920
|
+
side.active.move(slot).id = move;
|
|
1921
|
+
side.active.move(slot).pp = 5;
|
|
1922
|
+
try options.log.start(.{ battle.active(player), .Mimic, move });
|
|
1905
1923
|
}
|
|
1906
1924
|
|
|
1907
1925
|
pub fn mirrorMove(
|
|
@@ -1918,11 +1936,11 @@ pub const Effects = struct {
|
|
|
1918
1936
|
|
|
1919
1937
|
const last = foe.lastMove(false);
|
|
1920
1938
|
|
|
1921
|
-
if (last == .None) return options.log.fail(.{ battle.active(player),
|
|
1939
|
+
if (last == .None) return options.log.fail(.{ battle.active(player), .None });
|
|
1922
1940
|
const knows = knows: for (side.active.moves) |m| {
|
|
1923
1941
|
if (last == m.id) break :knows true;
|
|
1924
1942
|
} else false;
|
|
1925
|
-
if (knows) return options.log.fail(.{ battle.active(player),
|
|
1943
|
+
if (knows) return options.log.fail(.{ battle.active(player), .None });
|
|
1926
1944
|
|
|
1927
1945
|
// if (wPlayerCharging) ???
|
|
1928
1946
|
// wPlayerCharging = 1
|
|
@@ -1936,19 +1954,17 @@ pub const Effects = struct {
|
|
|
1936
1954
|
pub fn mist(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1937
1955
|
var side = battle.side(player);
|
|
1938
1956
|
|
|
1939
|
-
if (side.active.volatiles.Mist) {
|
|
1940
|
-
return options.log.fail(.{ battle.active(player), Fail.None });
|
|
1941
|
-
}
|
|
1957
|
+
if (side.active.volatiles.Mist) return options.log.fail(.{ battle.active(player), .None });
|
|
1942
1958
|
side.active.volatiles.Mist = true;
|
|
1943
1959
|
|
|
1944
|
-
try options.log.start(battle.active(player), .Mist);
|
|
1960
|
+
try options.log.start(.{ battle.active(player), .Mist });
|
|
1945
1961
|
}
|
|
1946
1962
|
|
|
1947
1963
|
pub fn weatherHeal(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
1948
1964
|
var stored = battle.side(player).stored();
|
|
1949
1965
|
const ident = battle.active(player);
|
|
1950
1966
|
|
|
1951
|
-
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident,
|
|
1967
|
+
if (stored.hp == stored.stats.hp) return options.log.fail(.{ ident, .None });
|
|
1952
1968
|
|
|
1953
1969
|
stored.hp = switch (battle.field.weather) {
|
|
1954
1970
|
.Sun => stored.stats.hp,
|
|
@@ -1956,7 +1972,7 @@ pub const Effects = struct {
|
|
|
1956
1972
|
else => @min(stored.stats.hp, stored.hp + (stored.stats.hp / 4)),
|
|
1957
1973
|
};
|
|
1958
1974
|
|
|
1959
|
-
try options.log.heal(.{ ident, stored,
|
|
1975
|
+
try options.log.heal(.{ ident, stored, .None });
|
|
1960
1976
|
}
|
|
1961
1977
|
|
|
1962
1978
|
pub fn multiHit(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -1969,17 +1985,17 @@ pub const Effects = struct {
|
|
|
1969
1985
|
const foe_ident = battle.active(player.foe());
|
|
1970
1986
|
|
|
1971
1987
|
if (foe_volatiles.Flying or foe_volatiles.Underground) {
|
|
1972
|
-
if (!showdown) return options.log.fail(.{ battle.active(player.foe()),
|
|
1988
|
+
if (!showdown) return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
1973
1989
|
try options.log.lastmiss(.{});
|
|
1974
1990
|
return options.log.miss(.{battle.active(player)});
|
|
1975
1991
|
} else if (foe_volatiles.Substitute) {
|
|
1976
|
-
return options.log.
|
|
1992
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
1977
1993
|
} else if (!Status.is(foe.stored().status, .SLP) or foe_volatiles.Nightmare) {
|
|
1978
|
-
return options.log.fail(.{ foe_ident,
|
|
1994
|
+
return options.log.fail(.{ foe_ident, .None });
|
|
1979
1995
|
}
|
|
1980
1996
|
|
|
1981
1997
|
foe_volatiles.Nightmare = true;
|
|
1982
|
-
try options.log.start(battle.active(player.foe()), .Nightmare);
|
|
1998
|
+
try options.log.start(.{ battle.active(player.foe()), .Nightmare });
|
|
1983
1999
|
}
|
|
1984
2000
|
|
|
1985
2001
|
pub const ohko = struct {
|
|
@@ -2003,9 +2019,9 @@ pub const Effects = struct {
|
|
|
2003
2019
|
const foe_ident = battle.active(player.foe());
|
|
2004
2020
|
|
|
2005
2021
|
state.damage = 0;
|
|
2006
|
-
if (state.immune()) return options.log.immune(.{ foe_ident,
|
|
2022
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
2007
2023
|
const delta = side.stored().level -| foe.stored().level;
|
|
2008
|
-
if (delta == 0) return options.log.immune(.{ foe_ident,
|
|
2024
|
+
if (delta == 0) return options.log.immune(.{ foe_ident, .OHKO });
|
|
2009
2025
|
|
|
2010
2026
|
state.accuracy = 2 * delta +| Gen12.percent(30);
|
|
2011
2027
|
try checkHit(battle, player, state, options);
|
|
@@ -2021,17 +2037,17 @@ pub const Effects = struct {
|
|
|
2021
2037
|
const ident = battle.active(player);
|
|
2022
2038
|
const foe_ident = battle.active(player.foe());
|
|
2023
2039
|
|
|
2024
|
-
if (state.miss) return options.log.fail(.{ foe_ident,
|
|
2040
|
+
if (state.miss) return options.log.fail(.{ foe_ident, .None });
|
|
2025
2041
|
if (foe.active.volatiles.Substitute) {
|
|
2026
|
-
return options.log.
|
|
2042
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2027
2043
|
}
|
|
2028
2044
|
|
|
2029
2045
|
const avg = @max((foe.stored().hp + side.stored().hp) / 2, 1);
|
|
2030
2046
|
foe.stored().hp -= avg;
|
|
2031
2047
|
side.stored().hp = avg;
|
|
2032
2048
|
|
|
2033
|
-
try options.log.sethp(.{ foe_ident, foe.stored(),
|
|
2034
|
-
try options.log.sethp(.{ ident, side.stored(),
|
|
2049
|
+
try options.log.sethp(.{ foe_ident, foe.stored(), .Silent });
|
|
2050
|
+
try options.log.sethp(.{ ident, side.stored(), .None });
|
|
2035
2051
|
}
|
|
2036
2052
|
|
|
2037
2053
|
pub fn paralyze(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2039,21 +2055,21 @@ pub const Effects = struct {
|
|
|
2039
2055
|
const foe_ident = battle.active(player.foe());
|
|
2040
2056
|
|
|
2041
2057
|
if (Status.is(foe.stored().status, .PAR)) {
|
|
2042
|
-
return options.log.fail(.{ foe_ident,
|
|
2058
|
+
return options.log.fail(.{ foe_ident, .Paralysis });
|
|
2043
2059
|
}
|
|
2044
|
-
if (state.immune()) return options.log.immune(.{ foe_ident,
|
|
2045
|
-
if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident,
|
|
2060
|
+
if (state.immune()) return options.log.immune(.{ foe_ident, .None });
|
|
2061
|
+
if (Status.any(foe.stored().status)) return options.log.fail(.{ foe_ident, .None });
|
|
2046
2062
|
if (state.miss) {
|
|
2047
2063
|
try options.log.lastmiss(.{});
|
|
2048
2064
|
return options.log.miss(.{battle.active(player)});
|
|
2049
2065
|
} else if (foe.active.volatiles.Substitute) {
|
|
2050
|
-
return options.log.
|
|
2066
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2051
2067
|
}
|
|
2052
2068
|
|
|
2053
2069
|
foe.stored().status = Status.init(.PAR);
|
|
2054
2070
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2055
2071
|
|
|
2056
|
-
try options.log.status(.{ foe_ident, foe.stored().status,
|
|
2072
|
+
try options.log.status(.{ foe_ident, foe.stored().status, .None });
|
|
2057
2073
|
}
|
|
2058
2074
|
|
|
2059
2075
|
pub fn paralyzeChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2069,8 +2085,7 @@ pub const Effects = struct {
|
|
|
2069
2085
|
foe_stored.status = Status.init(.PAR);
|
|
2070
2086
|
foe.active.stats.spe = @max(foe.active.stats.spe / 4, 1);
|
|
2071
2087
|
|
|
2072
|
-
|
|
2073
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, reason });
|
|
2088
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2074
2089
|
|
|
2075
2090
|
// TODO: check for status berry
|
|
2076
2091
|
}
|
|
@@ -2086,15 +2101,15 @@ pub const Effects = struct {
|
|
|
2086
2101
|
var foe = battle.foe(player);
|
|
2087
2102
|
|
|
2088
2103
|
if (foe.active.volatiles.PerishSong) {
|
|
2089
|
-
return options.log.fail(.{ battle.active(player.foe()),
|
|
2104
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2090
2105
|
} else if (!side.active.volatiles.PerishSong) {
|
|
2091
2106
|
side.active.volatiles.PerishSong = true;
|
|
2092
2107
|
side.active.volatiles.perish_song = 4;
|
|
2093
|
-
try options.log.start(battle.active(player), .PerishSong3Silent);
|
|
2108
|
+
try options.log.start(.{ battle.active(player), .PerishSong3Silent });
|
|
2094
2109
|
}
|
|
2095
2110
|
foe.active.volatiles.PerishSong = true;
|
|
2096
2111
|
foe.active.volatiles.perish_song = 4;
|
|
2097
|
-
try options.log.start(battle.active(player.foe()), .PerishSong3Silent);
|
|
2112
|
+
try options.log.start(.{ battle.active(player.foe()), .PerishSong3Silent });
|
|
2098
2113
|
|
|
2099
2114
|
try options.log.fieldactivate(.{}); // FIXME .PerishSong
|
|
2100
2115
|
}
|
|
@@ -2106,7 +2121,7 @@ pub const Effects = struct {
|
|
|
2106
2121
|
|
|
2107
2122
|
const toxic = state.move == .Toxic;
|
|
2108
2123
|
if (state.immune() or foe.active.types.includes(.Poison)) {
|
|
2109
|
-
return options.log.immune(.{ foe_ident,
|
|
2124
|
+
return options.log.immune(.{ foe_ident, .None });
|
|
2110
2125
|
} else if (Status.any(foe_stored.status)) {
|
|
2111
2126
|
// Pokémon Showdown considers Toxic to be a status even in Generation II and so
|
|
2112
2127
|
// will not include a fail reason for Toxic vs. Poison or vice-versa...
|
|
@@ -2120,7 +2135,7 @@ pub const Effects = struct {
|
|
|
2120
2135
|
else
|
|
2121
2136
|
Fail.None });
|
|
2122
2137
|
} else if (foe.active.volatiles.Substitute) {
|
|
2123
|
-
return options.log.
|
|
2138
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2124
2139
|
} else if (state.miss) {
|
|
2125
2140
|
try options.log.lastmiss(.{});
|
|
2126
2141
|
return options.log.miss(.{battle.active(player)});
|
|
@@ -2133,7 +2148,7 @@ pub const Effects = struct {
|
|
|
2133
2148
|
foe.active.volatiles.toxic = 0;
|
|
2134
2149
|
}
|
|
2135
2150
|
|
|
2136
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
2151
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .None });
|
|
2137
2152
|
|
|
2138
2153
|
// TODO: check for status berry
|
|
2139
2154
|
}
|
|
@@ -2149,8 +2164,8 @@ pub const Effects = struct {
|
|
|
2149
2164
|
if (foe.conditions.Safeguard) return;
|
|
2150
2165
|
|
|
2151
2166
|
foe_stored.status = Status.init(.PSN);
|
|
2152
|
-
|
|
2153
|
-
try options.log.status(.{ battle.active(player.foe()), foe_stored.status,
|
|
2167
|
+
|
|
2168
|
+
try options.log.status(.{ battle.active(player.foe()), foe_stored.status, .None });
|
|
2154
2169
|
|
|
2155
2170
|
// TODO: check for status berry
|
|
2156
2171
|
}
|
|
@@ -2165,9 +2180,7 @@ pub const Effects = struct {
|
|
|
2165
2180
|
|
|
2166
2181
|
const failed = !state.first or volatiles.Substitute or
|
|
2167
2182
|
!try Rolls.protect(battle, player, volatiles.protect, options);
|
|
2168
|
-
if (failed) {
|
|
2169
|
-
return options.log.fail(.{ battle.active(player), Fail.None });
|
|
2170
|
-
}
|
|
2183
|
+
if (failed) return options.log.fail(.{ battle.active(player), .None });
|
|
2171
2184
|
|
|
2172
2185
|
volatiles.protect = @max(8, volatiles.protect + 1);
|
|
2173
2186
|
if (state.move == .Endure) {
|
|
@@ -2184,7 +2197,7 @@ pub const Effects = struct {
|
|
|
2184
2197
|
|
|
2185
2198
|
// Pokémon Showdown should fail when the enemy has no boosts/unboosts but doesn't
|
|
2186
2199
|
if (!showdown and @as(u32, @bitCast(foe.active.boosts)) == 0) {
|
|
2187
|
-
return options.log.fail(.{ battle.active(player),
|
|
2200
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2188
2201
|
}
|
|
2189
2202
|
|
|
2190
2203
|
side.active.boosts = foe.active.boosts;
|
|
@@ -2203,11 +2216,10 @@ pub const Effects = struct {
|
|
|
2203
2216
|
}
|
|
2204
2217
|
|
|
2205
2218
|
pub fn damage(battle: anytype, player: Player, state: *State, _: anytype) !void {
|
|
2206
|
-
|
|
2207
|
-
var volatiles = &side.active.volatiles;
|
|
2219
|
+
const side = battle.side(player);
|
|
2208
2220
|
|
|
2209
|
-
assert(volatiles.Rage);
|
|
2210
|
-
state.damage *|= (volatiles.rage +| 1);
|
|
2221
|
+
assert(side.active.volatiles.Rage);
|
|
2222
|
+
state.damage *|= (side.active.volatiles.rage +| 1);
|
|
2211
2223
|
}
|
|
2212
2224
|
};
|
|
2213
2225
|
|
|
@@ -2219,7 +2231,7 @@ pub const Effects = struct {
|
|
|
2219
2231
|
var side = battle.side(player);
|
|
2220
2232
|
|
|
2221
2233
|
if (side.active.volatiles.LeechSeed) {
|
|
2222
|
-
try options.log.end(.{ battle.active(player),
|
|
2234
|
+
try options.log.end(.{ battle.active(player), .LeechSeedFrom });
|
|
2223
2235
|
side.active.volatiles.LeechSeed = false;
|
|
2224
2236
|
}
|
|
2225
2237
|
|
|
@@ -2238,18 +2250,14 @@ pub const Effects = struct {
|
|
|
2238
2250
|
pub fn recoil(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2239
2251
|
var side = battle.side(player);
|
|
2240
2252
|
var stored = side.stored();
|
|
2253
|
+
const ident = battle.active(player);
|
|
2241
2254
|
|
|
2242
2255
|
assert(state.damage > 0);
|
|
2243
2256
|
|
|
2244
2257
|
const damage: i16 = @intCast(@max(state.damage / 4, 1));
|
|
2245
2258
|
stored.hp = @intCast(@max(@as(i16, @intCast(stored.hp)) - damage, 0));
|
|
2246
2259
|
|
|
2247
|
-
try options.log.damage(.{
|
|
2248
|
-
battle.active(player),
|
|
2249
|
-
stored,
|
|
2250
|
-
Damage.RecoilOf,
|
|
2251
|
-
battle.active(player.foe()),
|
|
2252
|
-
});
|
|
2260
|
+
try options.log.damage(.{ ident, stored, .RecoilOf, battle.active(player.foe()) });
|
|
2253
2261
|
}
|
|
2254
2262
|
|
|
2255
2263
|
pub fn reversal(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
@@ -2312,7 +2320,7 @@ pub const Effects = struct {
|
|
|
2312
2320
|
|
|
2313
2321
|
pub fn sandstorm(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2314
2322
|
if (battle.field.weather != .Sandstorm) return weather(battle, .Sandstorm, options);
|
|
2315
|
-
return options.log.fail(.{ battle.active(player),
|
|
2323
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2316
2324
|
}
|
|
2317
2325
|
|
|
2318
2326
|
pub fn safeguard(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
@@ -2337,7 +2345,7 @@ pub const Effects = struct {
|
|
|
2337
2345
|
var side = battle.side(player);
|
|
2338
2346
|
|
|
2339
2347
|
if (@field(side.conditions, key)) {
|
|
2340
|
-
return options.log.fail(.{ battle.active(player),
|
|
2348
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2341
2349
|
}
|
|
2342
2350
|
|
|
2343
2351
|
@field(side.conditions, key) = true;
|
|
@@ -2353,9 +2361,9 @@ pub const Effects = struct {
|
|
|
2353
2361
|
|
|
2354
2362
|
if (showdown) {
|
|
2355
2363
|
// yes, actually "Splash". '|nothing' gets blindly mapped to '|-activate||move:Splash'
|
|
2356
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2364
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2357
2365
|
} else {
|
|
2358
|
-
try options.log.fail(.{ battle.active(player),
|
|
2366
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2359
2367
|
}
|
|
2360
2368
|
}
|
|
2361
2369
|
|
|
@@ -2372,7 +2380,7 @@ pub const Effects = struct {
|
|
|
2372
2380
|
}
|
|
2373
2381
|
|
|
2374
2382
|
if (foe.active.volatiles.Substitute) {
|
|
2375
|
-
return options.log.
|
|
2383
|
+
return options.log.activate(.{ foe_ident, .SubstituteBlock, state.move });
|
|
2376
2384
|
}
|
|
2377
2385
|
|
|
2378
2386
|
// Sleep Clause Mod
|
|
@@ -2383,22 +2391,21 @@ pub const Effects = struct {
|
|
|
2383
2391
|
}
|
|
2384
2392
|
|
|
2385
2393
|
foe_stored.status = Status.slp(Rolls.sleepDuration(battle, player, options));
|
|
2386
|
-
try options.log.status(.{ foe_ident, foe_stored.status,
|
|
2394
|
+
try options.log.status(.{ foe_ident, foe_stored.status, .From, state.move });
|
|
2387
2395
|
}
|
|
2388
2396
|
|
|
2389
2397
|
pub fn sleepTalk(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2390
2398
|
const side = battle.side(player);
|
|
2391
2399
|
|
|
2392
2400
|
if (!Status.is(side.stored().status, .SLP)) {
|
|
2393
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
2394
|
-
return;
|
|
2401
|
+
return if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2395
2402
|
}
|
|
2396
2403
|
|
|
2397
2404
|
var n: u3 = 0;
|
|
2398
2405
|
for (side.active.moves, 0..) |m, i| {
|
|
2399
2406
|
if (state.mslot - 1 != i and Move.get(m.id).extra.sleep_talk) n += 1;
|
|
2400
2407
|
}
|
|
2401
|
-
if (n == 0) return options.log.fail(.{ battle.active(player),
|
|
2408
|
+
if (n == 0) return options.log.fail(.{ battle.active(player), .None });
|
|
2402
2409
|
|
|
2403
2410
|
_ = try Rolls.sleepTalk(battle, player, n, state.mslot, options); // TODO
|
|
2404
2411
|
}
|
|
@@ -2407,55 +2414,75 @@ pub const Effects = struct {
|
|
|
2407
2414
|
if (Status.is(battle.side(player).stored().status, .SLP)) return true;
|
|
2408
2415
|
state.damage = 0;
|
|
2409
2416
|
state.miss = true; // TODO is this necessary?
|
|
2410
|
-
if (!showdown) try options.log.fail(.{ battle.active(player),
|
|
2417
|
+
if (!showdown) try options.log.fail(.{ battle.active(player), .None });
|
|
2411
2418
|
return false;
|
|
2412
2419
|
}
|
|
2413
2420
|
|
|
2414
2421
|
pub fn spikes(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2415
2422
|
var foe = battle.foe(player);
|
|
2416
|
-
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player),
|
|
2423
|
+
if (foe.conditions.Spikes) return options.log.fail(.{ battle.active(player), .None });
|
|
2417
2424
|
|
|
2418
2425
|
foe.conditions.Spikes = true;
|
|
2419
2426
|
try options.log.sidestart(.{ player.foe(), protocol.Side.Spikes });
|
|
2420
2427
|
}
|
|
2421
2428
|
|
|
2422
2429
|
pub fn spite(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2423
|
-
|
|
2424
|
-
// var foe = battle.foe(player);
|
|
2430
|
+
var foe = battle.foe(player);
|
|
2425
2431
|
|
|
2426
|
-
|
|
2432
|
+
const last = foe.lastMove(false);
|
|
2433
|
+
if (state.miss or last == .None or last == .Struggle) {
|
|
2434
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2435
|
+
}
|
|
2427
2436
|
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
//
|
|
2433
|
-
|
|
2437
|
+
var slot: u4 = 0;
|
|
2438
|
+
for (foe.active.moves, 0..) |m, i| {
|
|
2439
|
+
if (m.id == last) slot = @intCast(i + 1);
|
|
2440
|
+
}
|
|
2441
|
+
// If the move was called via Metronome it might not exist
|
|
2442
|
+
if (slot == 0 or foe.active.move(slot).pp == 0) {
|
|
2443
|
+
return options.log.fail(.{ battle.active(player.foe()), .None });
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
// Pokémon Showdown always deducts 4 instead of randomizing
|
|
2447
|
+
const pp = @min(
|
|
2448
|
+
foe.active.move(slot).pp,
|
|
2449
|
+
if (showdown) 4 else try Rolls.spite(battle, player, options),
|
|
2450
|
+
);
|
|
2451
|
+
foe.active.move(slot).pp -= pp;
|
|
2452
|
+
if (!foe.active.volatiles.Transform) {
|
|
2453
|
+
var move_slot = foe.stored().move(slot);
|
|
2454
|
+
assert(move_slot.pp > 0);
|
|
2455
|
+
|
|
2456
|
+
move_slot.pp = @as(u6, @intCast(move_slot.pp)) -% 1;
|
|
2457
|
+
assert(foe.active.move(slot).pp == foe.stored().move(slot).pp);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
try options.log.activate(.{ battle.active(player.foe()), .Spite, last, pp });
|
|
2434
2461
|
}
|
|
2435
2462
|
|
|
2436
2463
|
pub fn splash(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2437
|
-
try options.log.activate(battle.active(player), .Splash);
|
|
2464
|
+
try options.log.activate(.{ battle.active(player), .Splash });
|
|
2438
2465
|
}
|
|
2439
2466
|
|
|
2440
2467
|
pub fn substitute(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2441
2468
|
var side = battle.side(player);
|
|
2442
2469
|
if (side.active.volatiles.Substitute) {
|
|
2443
|
-
return options.log.fail(.{ battle.active(player),
|
|
2470
|
+
return options.log.fail(.{ battle.active(player), .Substitute });
|
|
2444
2471
|
}
|
|
2445
2472
|
|
|
2446
2473
|
assert(side.stored().stats.hp <= 1023);
|
|
2447
2474
|
const hp: u8 = @intCast(side.stored().stats.hp / 4);
|
|
2448
2475
|
|
|
2449
2476
|
if (hp == 0 or side.stored().hp <= hp) {
|
|
2450
|
-
return options.log.fail(.{ battle.active(player),
|
|
2477
|
+
return options.log.fail(.{ battle.active(player), .Weak });
|
|
2451
2478
|
}
|
|
2452
2479
|
|
|
2453
2480
|
side.stored().hp -= hp;
|
|
2454
2481
|
side.active.volatiles.substitute = hp;
|
|
2455
2482
|
side.active.volatiles.Substitute = true;
|
|
2456
2483
|
side.active.volatiles.bind = .{};
|
|
2457
|
-
try options.log.start(battle.active(player), .Substitute);
|
|
2458
|
-
try options.log.damage(.{ battle.active(player), side.stored(),
|
|
2484
|
+
try options.log.start(.{ battle.active(player), .Substitute });
|
|
2485
|
+
try options.log.damage(.{ battle.active(player), side.stored(), .None });
|
|
2459
2486
|
}
|
|
2460
2487
|
|
|
2461
2488
|
pub fn sunnyDay(battle: anytype, _: Player, _: *State, options: anytype) !void {
|
|
@@ -2471,7 +2498,7 @@ pub const Effects = struct {
|
|
|
2471
2498
|
pub fn teleport(battle: anytype, player: Player, _: *State, options: anytype) !void {
|
|
2472
2499
|
if (showdown) return;
|
|
2473
2500
|
|
|
2474
|
-
try options.log.fail(.{ battle.active(player),
|
|
2501
|
+
try options.log.fail(.{ battle.active(player), .None });
|
|
2475
2502
|
try options.log.laststill(.{});
|
|
2476
2503
|
}
|
|
2477
2504
|
|
|
@@ -2513,9 +2540,9 @@ pub const Effects = struct {
|
|
|
2513
2540
|
side.active.volatiles.dirty = false;
|
|
2514
2541
|
|
|
2515
2542
|
if (foe.active.volatiles.Transform) {
|
|
2516
|
-
return options.log.fail(.{ battle.active(player),
|
|
2543
|
+
return options.log.fail(.{ battle.active(player), .None });
|
|
2517
2544
|
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
2518
|
-
if (!showdown) return options.log.fail(.{ battle.active(player),
|
|
2545
|
+
if (!showdown) return options.log.fail(.{ battle.active(player), .None });
|
|
2519
2546
|
try options.log.lastmiss(.{});
|
|
2520
2547
|
return options.log.miss(.{battle.active(player)});
|
|
2521
2548
|
}
|
|
@@ -2564,8 +2591,8 @@ pub const Effects = struct {
|
|
|
2564
2591
|
pub fn allStatUpChance(battle: anytype, player: Player, state: *State, options: anytype) !void {
|
|
2565
2592
|
var side = battle.side(player);
|
|
2566
2593
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2594
|
+
const stats = &side.active.stats;
|
|
2595
|
+
const boosts = &side.active.boosts;
|
|
2569
2596
|
|
|
2570
2597
|
if (!state.proc) return;
|
|
2571
2598
|
inline for (STATS, STAT_UP_REASONS) |s, r| {
|
|
@@ -2584,9 +2611,9 @@ pub const Effects = struct {
|
|
|
2584
2611
|
var side = battle.side(player);
|
|
2585
2612
|
const ident = battle.active(player);
|
|
2586
2613
|
|
|
2587
|
-
if (state.miss) return log.fail(.{ ident,
|
|
2614
|
+
if (state.miss) return log.fail(.{ ident, .None });
|
|
2588
2615
|
|
|
2589
|
-
|
|
2616
|
+
const stats = &side.active.stats;
|
|
2590
2617
|
var boosts = &side.active.boosts;
|
|
2591
2618
|
|
|
2592
2619
|
const move = Move.get(state.move);
|
|
@@ -2594,7 +2621,7 @@ pub const Effects = struct {
|
|
|
2594
2621
|
.AttackUp1, .AttackUp2, .Rage, .Swagger => {
|
|
2595
2622
|
assert(boosts.atk >= -6 and boosts.atk <= 6);
|
|
2596
2623
|
if (boosts.atk == 6 or stats.atk == MAX_STAT_VALUE) {
|
|
2597
|
-
return log.fail(.{ ident,
|
|
2624
|
+
return log.fail(.{ ident, .None });
|
|
2598
2625
|
}
|
|
2599
2626
|
const n: u2 = if (move.effect == .AttackUp2 or move.effect == .Swagger) 2 else 1;
|
|
2600
2627
|
try boostStat(battle, side, stats, boosts, "atk", n);
|
|
@@ -2604,41 +2631,41 @@ pub const Effects = struct {
|
|
|
2604
2631
|
.DefenseUp1, .DefenseUp2, .SkullBash => {
|
|
2605
2632
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
2606
2633
|
if (boosts.def == 6 or stats.def == MAX_STAT_VALUE) {
|
|
2607
|
-
return log.fail(.{ ident,
|
|
2634
|
+
return log.fail(.{ ident, .None });
|
|
2608
2635
|
}
|
|
2609
2636
|
const n: u2 = if (move.effect == .DefenseUp2) 2 else 1;
|
|
2610
2637
|
try boostStat(battle, side, stats, boosts, "def", n);
|
|
2611
|
-
try log.boost(.{ ident,
|
|
2638
|
+
try log.boost(.{ ident, .Defense, n });
|
|
2612
2639
|
},
|
|
2613
2640
|
.SpeedUp2 => {
|
|
2614
2641
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
2615
2642
|
if (boosts.spe == 6 or stats.spe == MAX_STAT_VALUE) {
|
|
2616
|
-
return log.fail(.{ ident,
|
|
2643
|
+
return log.fail(.{ ident, .None });
|
|
2617
2644
|
}
|
|
2618
2645
|
try boostStat(battle, side, stats, boosts, "spe", 2);
|
|
2619
|
-
try log.boost(.{ ident,
|
|
2646
|
+
try log.boost(.{ ident, .Speed, 2 });
|
|
2620
2647
|
},
|
|
2621
2648
|
.SpAtkUp1 => {
|
|
2622
2649
|
assert(boosts.spa >= -6 and boosts.spa <= 6);
|
|
2623
2650
|
if (boosts.spa == 6 or stats.spa == MAX_STAT_VALUE) {
|
|
2624
|
-
return log.fail(.{ ident,
|
|
2651
|
+
return log.fail(.{ ident, .None });
|
|
2625
2652
|
}
|
|
2626
2653
|
try boostStat(battle, side, stats, boosts, "spa", 1);
|
|
2627
|
-
try log.boost(.{ ident,
|
|
2654
|
+
try log.boost(.{ ident, .SpecialAttack, 1 });
|
|
2628
2655
|
},
|
|
2629
2656
|
.SpDefUp2 => {
|
|
2630
2657
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
2631
2658
|
if (boosts.spd == 6 or stats.spd == MAX_STAT_VALUE) {
|
|
2632
|
-
return log.fail(.{ ident,
|
|
2659
|
+
return log.fail(.{ ident, .None });
|
|
2633
2660
|
}
|
|
2634
2661
|
try boostStat(battle, side, stats, boosts, "spd", 2);
|
|
2635
|
-
try log.boost(.{ ident,
|
|
2662
|
+
try log.boost(.{ ident, .SpecialDefense, 2 });
|
|
2636
2663
|
},
|
|
2637
2664
|
.EvasionUp1 => {
|
|
2638
2665
|
assert(boosts.evasion >= -6 and boosts.evasion <= 6);
|
|
2639
|
-
if (boosts.evasion == 6) return log.fail(.{ ident,
|
|
2666
|
+
if (boosts.evasion == 6) return log.fail(.{ ident, .None });
|
|
2640
2667
|
boosts.evasion = @intCast(@min(6, @as(i8, boosts.evasion) + 1));
|
|
2641
|
-
try log.boost(.{ ident,
|
|
2668
|
+
try log.boost(.{ ident, .Evasion, 1 });
|
|
2642
2669
|
if (state.move == .Minimize) side.active.volatiles.minimized = true;
|
|
2643
2670
|
},
|
|
2644
2671
|
else => unreachable,
|
|
@@ -2651,13 +2678,13 @@ pub const Effects = struct {
|
|
|
2651
2678
|
const foe_ident = battle.active(player.foe());
|
|
2652
2679
|
|
|
2653
2680
|
if (foe.active.volatiles.Mist) {
|
|
2654
|
-
try log.activate(foe_ident, .Mist);
|
|
2655
|
-
return log.fail(.{ foe_ident,
|
|
2681
|
+
try log.activate(.{ foe_ident, .Mist });
|
|
2682
|
+
return log.fail(.{ foe_ident, .None });
|
|
2656
2683
|
}
|
|
2657
2684
|
|
|
2658
2685
|
const fail = foe.active.volatiles.Substitute or state.miss or !state.proc;
|
|
2659
2686
|
|
|
2660
|
-
|
|
2687
|
+
const stats = &foe.active.stats;
|
|
2661
2688
|
var boosts = &foe.active.boosts;
|
|
2662
2689
|
|
|
2663
2690
|
const move = Move.get(state.move);
|
|
@@ -2675,7 +2702,7 @@ pub const Effects = struct {
|
|
|
2675
2702
|
}
|
|
2676
2703
|
const n: u2 = if (move.effect == .AttackDown2) 2 else 1;
|
|
2677
2704
|
try unboostStat(battle, foe, stats, boosts, "atk", n);
|
|
2678
|
-
try log.boost(.{ foe_ident,
|
|
2705
|
+
try log.boost(.{ foe_ident, .Attack, -@as(i8, n) });
|
|
2679
2706
|
},
|
|
2680
2707
|
.DefenseDown1, .DefenseDown2, .DefenseDownChance => {
|
|
2681
2708
|
assert(boosts.def >= -6 and boosts.def <= 6);
|
|
@@ -2691,7 +2718,7 @@ pub const Effects = struct {
|
|
|
2691
2718
|
}
|
|
2692
2719
|
const n: u2 = if (move.effect == .DefenseDown2) 2 else 1;
|
|
2693
2720
|
try unboostStat(battle, foe, stats, boosts, "def", n);
|
|
2694
|
-
try log.boost(.{ foe_ident,
|
|
2721
|
+
try log.boost(.{ foe_ident, .Defense, -@as(i8, n) });
|
|
2695
2722
|
},
|
|
2696
2723
|
.SpeedDown1, .SpeedDown2, .SpeedDownChance => {
|
|
2697
2724
|
assert(boosts.spe >= -6 and boosts.spe <= 6);
|
|
@@ -2705,7 +2732,7 @@ pub const Effects = struct {
|
|
|
2705
2732
|
}
|
|
2706
2733
|
const n: u2 = if (move.effect == .SpeedDown2) 2 else 1;
|
|
2707
2734
|
try unboostStat(battle, foe, stats, boosts, "spe", n);
|
|
2708
|
-
try log.boost(.{ foe_ident,
|
|
2735
|
+
try log.boost(.{ foe_ident, .Speed, -@as(i8, n) });
|
|
2709
2736
|
},
|
|
2710
2737
|
.SpDefDownChance => {
|
|
2711
2738
|
assert(boosts.spd >= -6 and boosts.spd <= 6);
|
|
@@ -2715,7 +2742,7 @@ pub const Effects = struct {
|
|
|
2715
2742
|
return options.log.miss(.{battle.active(player)});
|
|
2716
2743
|
} else if (stats.spd == 1) return;
|
|
2717
2744
|
try unboostStat(battle, foe, stats, boosts, "spd", 1);
|
|
2718
|
-
try log.boost(.{ foe_ident,
|
|
2745
|
+
try log.boost(.{ foe_ident, .SpecialDefense, -1 });
|
|
2719
2746
|
},
|
|
2720
2747
|
.AccuracyDown1, .AccuracyDownChance => {
|
|
2721
2748
|
assert(boosts.accuracy >= -6 and boosts.accuracy <= 6);
|
|
@@ -2727,7 +2754,7 @@ pub const Effects = struct {
|
|
|
2727
2754
|
return options.log.miss(.{battle.active(player)});
|
|
2728
2755
|
}
|
|
2729
2756
|
boosts.accuracy = @intCast(@max(-6, @as(i8, boosts.accuracy) - 1));
|
|
2730
|
-
try log.boost(.{ foe_ident,
|
|
2757
|
+
try log.boost(.{ foe_ident, .Accuracy, -1 });
|
|
2731
2758
|
},
|
|
2732
2759
|
else => unreachable,
|
|
2733
2760
|
}
|
|
@@ -2741,9 +2768,9 @@ fn unmodifiedStats(battle: anytype, side: *Side) *Stats(u16) {
|
|
|
2741
2768
|
}
|
|
2742
2769
|
|
|
2743
2770
|
fn recomputeStats(battle: anytype, side: *Side) void {
|
|
2744
|
-
|
|
2771
|
+
const stats = unmodifiedStats(battle, side);
|
|
2745
2772
|
inline for (STATS) |s| {
|
|
2746
|
-
|
|
2773
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(side.active.boosts, s)) + 6))];
|
|
2747
2774
|
const val = @field(stats, s) * mod[0] / mod[1];
|
|
2748
2775
|
@field(side.active.stats, s) = @max(1, @min(MAX_STAT_VALUE, val));
|
|
2749
2776
|
}
|
|
@@ -2766,7 +2793,7 @@ fn boostStat(
|
|
|
2766
2793
|
n: u2,
|
|
2767
2794
|
) !void {
|
|
2768
2795
|
@field(boosts, s) = @intCast(@min(6, @as(i8, @field(boosts, s)) + n));
|
|
2769
|
-
|
|
2796
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2770
2797
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2771
2798
|
@field(stats, s) = @min(MAX_STAT_VALUE, stat * mod[0] / mod[1]);
|
|
2772
2799
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2789,7 +2816,7 @@ fn unboostStat(
|
|
|
2789
2816
|
n: u2,
|
|
2790
2817
|
) !void {
|
|
2791
2818
|
@field(boosts, s) = @intCast(@max(-6, @as(i8, @field(boosts, s)) - n));
|
|
2792
|
-
|
|
2819
|
+
const mod = STAT_BOOSTS[@as(u4, @intCast(@as(i8, @field(boosts, s)) + 6))];
|
|
2793
2820
|
const stat = @field(unmodifiedStats(battle, side), s);
|
|
2794
2821
|
@field(stats, s) = @max(1, stat * mod[0] / mod[1]);
|
|
2795
2822
|
if (comptime std.mem.eql(u8, s, "atk")) {
|
|
@@ -2817,7 +2844,7 @@ fn convertible(active: ActivePokemon, m: Move) bool {
|
|
|
2817
2844
|
|
|
2818
2845
|
pub const Rolls = struct {
|
|
2819
2846
|
fn speedTie(battle: anytype, options: anytype) !bool {
|
|
2820
|
-
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
2847
|
+
const p1 = if (options.calc.overridden(.P1, 0, .speed_tie)) |player|
|
|
2821
2848
|
player == .P1
|
|
2822
2849
|
else if (showdown)
|
|
2823
2850
|
battle.rng.range(u8, 0, 2) == 0
|
|
@@ -2829,7 +2856,7 @@ pub const Rolls = struct {
|
|
|
2829
2856
|
}
|
|
2830
2857
|
|
|
2831
2858
|
fn quickClaw(battle: anytype, player: Player, options: anytype) !bool {
|
|
2832
|
-
const qkc = if (options.calc.overridden(player, .quick_claw)) |val|
|
|
2859
|
+
const qkc = if (options.calc.overridden(player, 0, .quick_claw)) |val|
|
|
2833
2860
|
val == .true
|
|
2834
2861
|
else if (showdown)
|
|
2835
2862
|
battle.rng.chance(u8, 60, 256)
|
|
@@ -2841,7 +2868,7 @@ pub const Rolls = struct {
|
|
|
2841
2868
|
}
|
|
2842
2869
|
|
|
2843
2870
|
fn hit(battle: anytype, player: Player, accuracy: u8, options: anytype) !bool {
|
|
2844
|
-
const ok = if (options.calc.overridden(player, .hit)) |val|
|
|
2871
|
+
const ok = if (options.calc.overridden(player, 0, .hit)) |val|
|
|
2845
2872
|
val == .true
|
|
2846
2873
|
else if (showdown)
|
|
2847
2874
|
battle.rng.chance(u8, accuracy, 256)
|
|
@@ -2852,20 +2879,22 @@ pub const Rolls = struct {
|
|
|
2852
2879
|
return ok;
|
|
2853
2880
|
}
|
|
2854
2881
|
|
|
2855
|
-
fn criticalHit(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2856
|
-
|
|
2882
|
+
fn criticalHit(battle: anytype, player: Player, i: u3, rate: u8, options: anytype) !bool {
|
|
2883
|
+
_ = .{ player, options };
|
|
2884
|
+
const crit = if (options.calc.overridden(player, i, .critical_hits)) |val|
|
|
2857
2885
|
val == .true
|
|
2858
2886
|
else if (showdown)
|
|
2859
2887
|
battle.rng.chance(u8, rate, 256)
|
|
2860
2888
|
else
|
|
2861
2889
|
battle.rng.next() < rate;
|
|
2862
2890
|
|
|
2863
|
-
try options.chance.criticalHit(player, crit, rate);
|
|
2891
|
+
try options.chance.criticalHit(player, i, crit, rate);
|
|
2864
2892
|
return crit;
|
|
2865
2893
|
}
|
|
2866
2894
|
|
|
2867
|
-
fn damage(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2868
|
-
|
|
2895
|
+
fn damage(battle: anytype, player: Player, i: u3, options: anytype) !u8 {
|
|
2896
|
+
_ = .{ player, options };
|
|
2897
|
+
const roll = if (options.calc.overridden(player, i, .damages)) |val|
|
|
2869
2898
|
val
|
|
2870
2899
|
else roll: {
|
|
2871
2900
|
if (showdown) break :roll battle.rng.range(u8, 217, 256);
|
|
@@ -2876,12 +2905,12 @@ pub const Rolls = struct {
|
|
|
2876
2905
|
};
|
|
2877
2906
|
|
|
2878
2907
|
assert(roll >= 217 and roll <= 255);
|
|
2879
|
-
try options.chance.damage(player, roll);
|
|
2908
|
+
try options.chance.damage(player, i, roll);
|
|
2880
2909
|
return roll;
|
|
2881
2910
|
}
|
|
2882
2911
|
|
|
2883
2912
|
fn confused(battle: anytype, player: Player, options: anytype) !bool {
|
|
2884
|
-
const cfz = if (options.calc.overridden(player, .confused)) |val|
|
|
2913
|
+
const cfz = if (options.calc.overridden(player, 0, .confused)) |val|
|
|
2885
2914
|
val == .true
|
|
2886
2915
|
else if (showdown)
|
|
2887
2916
|
!battle.rng.chance(u8, 128, 256)
|
|
@@ -2893,7 +2922,7 @@ pub const Rolls = struct {
|
|
|
2893
2922
|
}
|
|
2894
2923
|
|
|
2895
2924
|
fn attract(battle: anytype, player: Player, options: anytype) !bool {
|
|
2896
|
-
const cant = if (options.calc.overridden(player, .attract)) |val|
|
|
2925
|
+
const cant = if (options.calc.overridden(player, 0, .attract)) |val|
|
|
2897
2926
|
val == .true
|
|
2898
2927
|
else if (showdown)
|
|
2899
2928
|
battle.rng.chance(u8, 1, 2)
|
|
@@ -2905,7 +2934,7 @@ pub const Rolls = struct {
|
|
|
2905
2934
|
}
|
|
2906
2935
|
|
|
2907
2936
|
fn paralyzed(battle: anytype, player: Player, options: anytype) !bool {
|
|
2908
|
-
const par = if (options.calc.overridden(player, .paralyzed)) |val|
|
|
2937
|
+
const par = if (options.calc.overridden(player, 0, .paralyzed)) |val|
|
|
2909
2938
|
val == .true
|
|
2910
2939
|
else if (showdown)
|
|
2911
2940
|
battle.rng.chance(u8, 63, 256)
|
|
@@ -2917,7 +2946,7 @@ pub const Rolls = struct {
|
|
|
2917
2946
|
}
|
|
2918
2947
|
|
|
2919
2948
|
fn defrost(battle: anytype, player: Player, options: anytype) !bool {
|
|
2920
|
-
const thaw = if (options.calc.overridden(player, .defrost)) |val|
|
|
2949
|
+
const thaw = if (options.calc.overridden(player, 0, .defrost)) |val|
|
|
2921
2950
|
val == .true
|
|
2922
2951
|
else if (showdown)
|
|
2923
2952
|
battle.rng.chance(u8, 25, 256)
|
|
@@ -2930,7 +2959,7 @@ pub const Rolls = struct {
|
|
|
2930
2959
|
|
|
2931
2960
|
fn secondaryChance(battle: anytype, player: Player, rate: u8, options: anytype) !bool {
|
|
2932
2961
|
// GLITCH: 100% secondary chance will still fail 1/256 of the time
|
|
2933
|
-
const proc = if (options.calc.overridden(player, .secondary_chance)) |val|
|
|
2962
|
+
const proc = if (options.calc.overridden(player, 0, .secondary_chance)) |val|
|
|
2934
2963
|
val == .true
|
|
2935
2964
|
else if (showdown)
|
|
2936
2965
|
battle.rng.chance(u8, rate, 256)
|
|
@@ -2942,7 +2971,7 @@ pub const Rolls = struct {
|
|
|
2942
2971
|
}
|
|
2943
2972
|
|
|
2944
2973
|
fn item(battle: anytype, player: Player, options: anytype) !bool {
|
|
2945
|
-
const proc = if (options.calc.overridden(player, .item)) |val|
|
|
2974
|
+
const proc = if (options.calc.overridden(player, 0, .item)) |val|
|
|
2946
2975
|
val == .true
|
|
2947
2976
|
else if (showdown)
|
|
2948
2977
|
battle.rng.chance(u8, 30, 256)
|
|
@@ -2954,7 +2983,7 @@ pub const Rolls = struct {
|
|
|
2954
2983
|
}
|
|
2955
2984
|
|
|
2956
2985
|
fn triAttack(battle: anytype, player: Player, options: anytype) !TriAttack {
|
|
2957
|
-
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
2986
|
+
const status: TriAttack = if (options.calc.overridden(player, 0, .tri_attack)) |val|
|
|
2958
2987
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
2959
2988
|
else if (showdown)
|
|
2960
2989
|
@enumFromInt(battle.rng.range(u2, 0, 2))
|
|
@@ -2970,7 +2999,7 @@ pub const Rolls = struct {
|
|
|
2970
2999
|
}
|
|
2971
3000
|
|
|
2972
3001
|
fn present(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2973
|
-
const power = if (options.calc.overridden(player, .present)) |val|
|
|
3002
|
+
const power = if (options.calc.overridden(player, 0, .present)) |val|
|
|
2974
3003
|
(val - 1) * 40
|
|
2975
3004
|
else if (showdown) power: {
|
|
2976
3005
|
const r = battle.rng.range(u8, 0, 10);
|
|
@@ -2992,7 +3021,7 @@ pub const Rolls = struct {
|
|
|
2992
3021
|
}
|
|
2993
3022
|
|
|
2994
3023
|
fn magnitude(battle: anytype, player: Player, options: anytype) !u8 {
|
|
2995
|
-
const num: u8 = if (options.calc.overridden(player, .magnitude)) |val|
|
|
3024
|
+
const num: u8 = if (options.calc.overridden(player, 0, .magnitude)) |val|
|
|
2996
3025
|
val + 3
|
|
2997
3026
|
else if (showdown) num: {
|
|
2998
3027
|
const r = battle.rng.range(u8, 0, 100);
|
|
@@ -3020,7 +3049,7 @@ pub const Rolls = struct {
|
|
|
3020
3049
|
}
|
|
3021
3050
|
|
|
3022
3051
|
fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
3023
|
-
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
3052
|
+
const hits = if (options.calc.overridden(player, 0, .triple_kick)) |val|
|
|
3024
3053
|
val
|
|
3025
3054
|
else if (showdown)
|
|
3026
3055
|
battle.rng.range(u2, 1, 4)
|
|
@@ -3036,13 +3065,12 @@ pub const Rolls = struct {
|
|
|
3036
3065
|
return hits;
|
|
3037
3066
|
}
|
|
3038
3067
|
|
|
3039
|
-
fn spite(battle: anytype, player: Player, options: anytype) !
|
|
3040
|
-
|
|
3068
|
+
fn spite(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3069
|
+
assert(!showdown);
|
|
3070
|
+
const pp: u3 = if (options.calc.overridden(player, 0, .spite)) |val|
|
|
3041
3071
|
val + 1
|
|
3042
|
-
else if (showdown)
|
|
3043
|
-
battle.rng.range(u8, 2, 6)
|
|
3044
3072
|
else
|
|
3045
|
-
(battle.rng.next() & 3) + 2;
|
|
3073
|
+
@intCast((battle.rng.next() & 3) + 2);
|
|
3046
3074
|
|
|
3047
3075
|
assert(pp >= 2 and pp <= 5);
|
|
3048
3076
|
try options.chance.spite(player, pp);
|
|
@@ -3056,7 +3084,7 @@ pub const Rolls = struct {
|
|
|
3056
3084
|
var i: u8 = 0;
|
|
3057
3085
|
const neutral = @intFromEnum(Effectiveness.Neutral);
|
|
3058
3086
|
assert(mtype != .@"???");
|
|
3059
|
-
const ty: Type = if (options.calc.overridden(player, .conversion_2)) |val|
|
|
3087
|
+
const ty: Type = if (options.calc.overridden(player, 0, .conversion_2)) |val|
|
|
3060
3088
|
@enumFromInt(@intFromEnum(val) - 1)
|
|
3061
3089
|
else if (showdown) ty: {
|
|
3062
3090
|
for (Type.SHOWDOWN) |t| {
|
|
@@ -3090,7 +3118,7 @@ pub const Rolls = struct {
|
|
|
3090
3118
|
assert(count <= 8);
|
|
3091
3119
|
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
3092
3120
|
|
|
3093
|
-
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
3121
|
+
const ok = if (options.calc.overridden(player, 0, .protect)) |val|
|
|
3094
3122
|
val == .true
|
|
3095
3123
|
else if (showdown)
|
|
3096
3124
|
battle.rng.chance(u8, num, 255)
|
|
@@ -3112,7 +3140,7 @@ pub const Rolls = struct {
|
|
|
3112
3140
|
const moves = active.moves;
|
|
3113
3141
|
|
|
3114
3142
|
// TODO: consider throwing error instead of rerolling?
|
|
3115
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3143
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3116
3144
|
if (convertible(active, moves[val - 1].id)) val else null
|
|
3117
3145
|
else
|
|
3118
3146
|
null;
|
|
@@ -3149,7 +3177,7 @@ pub const Rolls = struct {
|
|
|
3149
3177
|
const moves = active.moves;
|
|
3150
3178
|
|
|
3151
3179
|
// TODO: consider throwing error instead of rerolling?
|
|
3152
|
-
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
3180
|
+
const overridden = if (options.calc.overridden(player, 0, .move_slot)) |val|
|
|
3153
3181
|
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
3154
3182
|
else
|
|
3155
3183
|
null;
|
|
@@ -3193,7 +3221,7 @@ pub const Rolls = struct {
|
|
|
3193
3221
|
assert(n > 0 and n <= 6);
|
|
3194
3222
|
|
|
3195
3223
|
// TODO: consider throwing error instead of rerolling?
|
|
3196
|
-
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
3224
|
+
const overridden = if (options.calc.overridden(player, 0, .force_switch)) |val| val: {
|
|
3197
3225
|
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
3198
3226
|
break :val null;
|
|
3199
3227
|
} else null;
|
|
@@ -3219,7 +3247,7 @@ pub const Rolls = struct {
|
|
|
3219
3247
|
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
3220
3248
|
|
|
3221
3249
|
fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
3222
|
-
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
3250
|
+
const n: u3 = if (options.calc.overridden(player, 0, .multi_hit)) |val|
|
|
3223
3251
|
@intCast(val)
|
|
3224
3252
|
else if (showdown)
|
|
3225
3253
|
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
@@ -3234,7 +3262,7 @@ pub const Rolls = struct {
|
|
|
3234
3262
|
}
|
|
3235
3263
|
|
|
3236
3264
|
fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
3237
|
-
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
3265
|
+
const power = if (options.calc.overridden(player, 0, .psywave)) |val|
|
|
3238
3266
|
val
|
|
3239
3267
|
else if (showdown)
|
|
3240
3268
|
battle.rng.range(u8, 1, max)
|
|
@@ -3260,7 +3288,7 @@ pub const Rolls = struct {
|
|
|
3260
3288
|
}
|
|
3261
3289
|
|
|
3262
3290
|
// TODO: consider throwing error instead of rerolling?
|
|
3263
|
-
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
3291
|
+
const overridden = if (options.calc.overridden(player, 0, .metronome)) |val| val: {
|
|
3264
3292
|
for (moves) |m| if (m.id == val) break :val null;
|
|
3265
3293
|
break :val val;
|
|
3266
3294
|
} else null;
|
|
@@ -3292,7 +3320,7 @@ pub const Rolls = struct {
|
|
|
3292
3320
|
}
|
|
3293
3321
|
|
|
3294
3322
|
fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3295
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3323
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3296
3324
|
@intCast(val)
|
|
3297
3325
|
else if (showdown)
|
|
3298
3326
|
battle.rng.range(u3, 1, 7)
|
|
@@ -3304,12 +3332,12 @@ pub const Rolls = struct {
|
|
|
3304
3332
|
};
|
|
3305
3333
|
|
|
3306
3334
|
assert(duration >= 1 and duration <= 6);
|
|
3307
|
-
options.chance.duration(
|
|
3335
|
+
options.chance.duration(player, duration);
|
|
3308
3336
|
return duration + 1;
|
|
3309
3337
|
}
|
|
3310
3338
|
|
|
3311
3339
|
fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
3312
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3340
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3313
3341
|
@intCast(val)
|
|
3314
3342
|
else if (showdown)
|
|
3315
3343
|
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
@@ -3318,16 +3346,16 @@ pub const Rolls = struct {
|
|
|
3318
3346
|
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
3319
3347
|
|
|
3320
3348
|
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
3321
|
-
options.chance.duration(
|
|
3349
|
+
options.chance.duration(player, duration);
|
|
3322
3350
|
return duration;
|
|
3323
3351
|
}
|
|
3324
3352
|
|
|
3325
3353
|
fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
3326
|
-
const duration:
|
|
3354
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3327
3355
|
@intCast(val)
|
|
3328
3356
|
else if (showdown)
|
|
3329
3357
|
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
3330
|
-
battle.rng.range(
|
|
3358
|
+
battle.rng.range(u3, 2, 6)
|
|
3331
3359
|
else duration: {
|
|
3332
3360
|
while (true) {
|
|
3333
3361
|
const r = battle.rng.next() & 7;
|
|
@@ -3336,12 +3364,12 @@ pub const Rolls = struct {
|
|
|
3336
3364
|
};
|
|
3337
3365
|
|
|
3338
3366
|
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
3339
|
-
options.chance.duration(
|
|
3367
|
+
options.chance.duration(player, duration);
|
|
3340
3368
|
return duration;
|
|
3341
3369
|
}
|
|
3342
3370
|
|
|
3343
3371
|
fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3344
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
3372
|
+
const duration: u3 = if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3345
3373
|
@intCast(val)
|
|
3346
3374
|
else if (showdown)
|
|
3347
3375
|
battle.rng.range(u3, 2, 4)
|
|
@@ -3349,12 +3377,12 @@ pub const Rolls = struct {
|
|
|
3349
3377
|
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
3350
3378
|
|
|
3351
3379
|
assert(duration >= 2 and duration <= 3);
|
|
3352
|
-
options.chance.duration(
|
|
3380
|
+
options.chance.duration(player, duration);
|
|
3353
3381
|
return duration;
|
|
3354
3382
|
}
|
|
3355
3383
|
|
|
3356
3384
|
fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3357
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3385
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3358
3386
|
@intCast(val)
|
|
3359
3387
|
else if (showdown)
|
|
3360
3388
|
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
@@ -3363,13 +3391,13 @@ pub const Rolls = struct {
|
|
|
3363
3391
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3364
3392
|
|
|
3365
3393
|
assert(duration >= 2 and duration <= 5);
|
|
3366
|
-
options.chance.duration(
|
|
3394
|
+
options.chance.duration(player, duration);
|
|
3367
3395
|
return duration + 1;
|
|
3368
3396
|
}
|
|
3369
3397
|
|
|
3370
3398
|
// TODO: consider sharing implementation with bindingDuration
|
|
3371
3399
|
fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
3372
|
-
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
3400
|
+
const duration: u3 = (if (options.calc.overridden(player, 0, .duration)) |val|
|
|
3373
3401
|
@intCast(val)
|
|
3374
3402
|
else if (showdown)
|
|
3375
3403
|
battle.rng.range(u3, 2, 6)
|
|
@@ -3377,7 +3405,7 @@ pub const Rolls = struct {
|
|
|
3377
3405
|
@intCast(battle.rng.next() & 3 + 2));
|
|
3378
3406
|
|
|
3379
3407
|
assert(duration >= 2 and duration <= 5);
|
|
3380
|
-
options.chance.duration(
|
|
3408
|
+
options.chance.duration(player, duration);
|
|
3381
3409
|
return duration + 1;
|
|
3382
3410
|
}
|
|
3383
3411
|
};
|
|
@@ -3490,7 +3518,7 @@ pub fn choices(battle: anytype, player: Player, request: Choice.Type, out: []Cho
|
|
|
3490
3518
|
.Move => {
|
|
3491
3519
|
const side = battle.side(player);
|
|
3492
3520
|
|
|
3493
|
-
|
|
3521
|
+
const active = &side.active;
|
|
3494
3522
|
const stored = side.stored();
|
|
3495
3523
|
|
|
3496
3524
|
// While players are not given any input options on the cartridge in these cases,
|