@pkmn/engine 0.1.0-dev.bcb16a6c → 0.1.0-dev.c42deb42
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/package.json +1 -1
- package/src/lib/common/optional.zig +4 -6
- package/src/lib/common/protocol.zig +23 -18
- package/src/lib/gen1/chance.zig +43 -41
- package/src/lib/gen1/data/moves.zig +14 -0
- package/src/lib/gen1/helpers.zig +1 -16
- package/src/lib/gen1/mechanics.zig +29 -62
- package/src/lib/gen2/data/moves.zig +27 -10
- package/src/lib/gen2/data.zig +6 -2
- package/src/lib/gen2/mechanics.zig +1143 -81
|
@@ -89,8 +89,6 @@ const CRITICAL_HITS = &[_]u8{
|
|
|
89
89
|
|
|
90
90
|
const MAX_STAT_VALUE = 999;
|
|
91
91
|
|
|
92
|
-
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
93
|
-
|
|
94
92
|
pub fn update(battle: anytype, c1: Choice, c2: Choice, options: anytype) !Result {
|
|
95
93
|
assert(c1.type != .Pass or c2.type != .Pass or battle.turn == 0);
|
|
96
94
|
if (battle.turn == 0) return start(battle, options);
|
|
@@ -317,13 +315,13 @@ fn doTurn(
|
|
|
317
315
|
const foe_volatiles = &battle.foe(player).active.volatiles;
|
|
318
316
|
|
|
319
317
|
volatiles.DestinyBond = false;
|
|
320
|
-
if (try executeMove(battle, player, player_choice, options)) |r| return r;
|
|
318
|
+
if (try executeMove(battle, player, player_choice, true, options)) |r| return r;
|
|
321
319
|
foe_volatiles.Protect = false;
|
|
322
320
|
foe_volatiles.Endure = false;
|
|
323
321
|
foe_volatiles.DestinyBond = false;
|
|
324
322
|
|
|
325
323
|
// foe_volatiles.DestinyBond = false;
|
|
326
|
-
if (try executeMove(battle, foe_player, foe_choice, options)) |r| return r;
|
|
324
|
+
if (try executeMove(battle, foe_player, foe_choice, false, options)) |r| return r;
|
|
327
325
|
volatiles.Protect = false;
|
|
328
326
|
volatiles.Endure = false;
|
|
329
327
|
volatiles.DestinyBond = false;
|
|
@@ -331,20 +329,26 @@ fn doTurn(
|
|
|
331
329
|
return null;
|
|
332
330
|
}
|
|
333
331
|
|
|
334
|
-
fn executeMove(
|
|
332
|
+
fn executeMove(
|
|
333
|
+
battle: anytype,
|
|
334
|
+
player: Player,
|
|
335
|
+
choice: Choice,
|
|
336
|
+
first: bool,
|
|
337
|
+
options: anytype,
|
|
338
|
+
) !?Result {
|
|
335
339
|
if (choice.type == .Switch) {
|
|
336
340
|
try switchIn(battle, player, choice.data, false, options);
|
|
337
341
|
return null;
|
|
338
342
|
}
|
|
339
343
|
|
|
340
344
|
assert(choice.type == .Move);
|
|
341
|
-
var mslot:
|
|
345
|
+
var mslot: u3 = @intCast(choice.data);
|
|
342
346
|
|
|
343
347
|
// const move = Move.get(side.last_selected_move);
|
|
344
348
|
|
|
345
349
|
// CheckTurn
|
|
346
350
|
|
|
347
|
-
return doMove(battle, player,
|
|
351
|
+
return doMove(battle, player, mslot, first, options);
|
|
348
352
|
}
|
|
349
353
|
|
|
350
354
|
fn beforeMove(battle: anytype, player: Player, options: anytype) !bool {
|
|
@@ -498,7 +502,7 @@ fn canMove(battle: anytype, player: Player, options: anytype) !bool {
|
|
|
498
502
|
return false;
|
|
499
503
|
}
|
|
500
504
|
|
|
501
|
-
fn decrementPP(side: *Side, mslot:
|
|
505
|
+
fn decrementPP(side: *Side, mslot: u3) void {
|
|
502
506
|
var active = &side.active;
|
|
503
507
|
const volatiles = &active.volatiles;
|
|
504
508
|
|
|
@@ -516,16 +520,319 @@ fn decrementPP(side: *Side, mslot: u4) void {
|
|
|
516
520
|
assert(active.move(mslot).pp == side.stored().move(mslot).pp);
|
|
517
521
|
}
|
|
518
522
|
|
|
519
|
-
fn doMove(battle: anytype, player: Player,
|
|
520
|
-
_ = options;
|
|
523
|
+
fn doMove(battle: anytype, player: Player, mslot: u3, first: bool, options: anytype) !?Result {
|
|
521
524
|
var side = battle.side(player);
|
|
522
|
-
|
|
525
|
+
var move = Move.get(.Teleport); // side.last_selected_move
|
|
526
|
+
_ = .{ side, mslot };
|
|
527
|
+
|
|
523
528
|
switch (move.effect) {
|
|
529
|
+
.None, .HighCritical, .AlwaysHit, .Priority, .Gust, .TriAttack, .Earthquake => {
|
|
530
|
+
// TODO Gust skips kingsrock and does double fly damage
|
|
531
|
+
// TODO triattack does own effect instead of kingsrock
|
|
532
|
+
// TODO double damage for earthquake
|
|
533
|
+
},
|
|
534
|
+
.Attract => {
|
|
535
|
+
try Effects.attract(battle, player, options); // TODO
|
|
536
|
+
},
|
|
537
|
+
.BatonPass => {
|
|
538
|
+
try Effects.batonPass(battle, player, options); // TODO
|
|
539
|
+
},
|
|
540
|
+
.BeatUp => {
|
|
541
|
+
try Effects.beatUp(battle, player, options); // TODO
|
|
542
|
+
},
|
|
543
|
+
.BellyDrum => {
|
|
544
|
+
try Effects.bellyDrum(battle, player, options); // TODO
|
|
545
|
+
},
|
|
546
|
+
.Bide => {
|
|
547
|
+
try Effects.bide(battle, player, options); // TODO
|
|
548
|
+
},
|
|
549
|
+
.Binding => {
|
|
550
|
+
try Effects.binding(battle, player, options); // TODO
|
|
551
|
+
},
|
|
552
|
+
.BurnChance => {
|
|
553
|
+
try Effects.burnChance(battle, player, move, options); // TODO
|
|
554
|
+
},
|
|
555
|
+
.Confusion, .ConfusionChance => {
|
|
556
|
+
try Effects.confusion(battle, player, move, options); // TODO
|
|
557
|
+
},
|
|
558
|
+
.Conversion => {
|
|
559
|
+
try Effects.conversion(battle, player, options); // TODO
|
|
560
|
+
},
|
|
561
|
+
.Conversion2 => {
|
|
562
|
+
try Effects.conversion2(battle, player, options); // TODO
|
|
563
|
+
},
|
|
564
|
+
.Counter => {
|
|
565
|
+
try Effects.counter(battle, player, move, options); // TODO
|
|
566
|
+
},
|
|
567
|
+
.Curse => {
|
|
568
|
+
try Effects.curse(battle, player, options); // TODO
|
|
569
|
+
},
|
|
570
|
+
.DefenseCurl => {
|
|
571
|
+
try Effects.defenseCurl(battle, player, options); // TODO
|
|
572
|
+
},
|
|
573
|
+
.DestinyBond => {
|
|
574
|
+
try Effects.destinyBond(battle, player, options); // TODO
|
|
575
|
+
},
|
|
576
|
+
.Disable => {
|
|
577
|
+
try Effects.disable(battle, player, options); // TODO
|
|
578
|
+
},
|
|
579
|
+
.DrainHP, .DreamEater => {
|
|
580
|
+
try Effects.drainHP(battle, player, options); // TODO
|
|
581
|
+
},
|
|
582
|
+
.Encore => {
|
|
583
|
+
try Effects.encore(battle, player, first, options); // TODO
|
|
584
|
+
},
|
|
585
|
+
.Endure => {
|
|
586
|
+
try Effects.protect(battle, player, Move.Endure, first, options); // TODO
|
|
587
|
+
},
|
|
588
|
+
.Explode => {
|
|
589
|
+
try Effects.explode(battle, player, options); // TODO
|
|
590
|
+
},
|
|
591
|
+
.FalseSwipe => {
|
|
592
|
+
var damage: u16 = 0; // TODO
|
|
593
|
+
damage = try Effects.falseSwipe(battle, player, damage);
|
|
594
|
+
},
|
|
595
|
+
.FixedDamage, .Psywave, .SuperFang => {
|
|
596
|
+
try Effects.fixedDamage(battle, player, move, options); // TODO
|
|
597
|
+
},
|
|
598
|
+
.FlameWheel => {
|
|
599
|
+
try Effects.flameWheel(battle, player, options); // TODO
|
|
600
|
+
},
|
|
601
|
+
.FlinchChance => {
|
|
602
|
+
try Effects.flinchChance(battle, player, move, options); // TODO
|
|
603
|
+
},
|
|
604
|
+
.FlyDig => {
|
|
605
|
+
try Effects.flyDig(battle, player, move, options); // TODO
|
|
606
|
+
},
|
|
607
|
+
.FocusEnergy => {
|
|
608
|
+
try Effects.focusEnergy(battle, player, options); // TODO
|
|
609
|
+
},
|
|
610
|
+
.ForceSwitch => {
|
|
611
|
+
try Effects.forceSwitch(battle, player, options); // TODO
|
|
612
|
+
},
|
|
613
|
+
.Foresight => {
|
|
614
|
+
try Effects.foresight(battle, player, options); // TODO
|
|
615
|
+
},
|
|
616
|
+
.FreezeChance => {
|
|
617
|
+
try Effects.freezeChance(battle, player, move, options); // TODO
|
|
618
|
+
},
|
|
619
|
+
.FuryCutter => {
|
|
620
|
+
try Effects.furyCutter(battle, player, options); // TODO
|
|
621
|
+
},
|
|
622
|
+
.FutureSight => {
|
|
623
|
+
try Effects.futureSight(battle, player, options); // TODO
|
|
624
|
+
},
|
|
625
|
+
.Return, .Frustration => {
|
|
626
|
+
const bp = try Effects.happiness(battle, player, move); // TODO
|
|
627
|
+
_ = bp;
|
|
628
|
+
},
|
|
629
|
+
.Haze => {
|
|
630
|
+
try Effects.haze(battle, player, options); // TODO
|
|
631
|
+
},
|
|
632
|
+
.Heal => {
|
|
633
|
+
try Effects.heal(battle, player, options); // TODO
|
|
634
|
+
},
|
|
635
|
+
.HealBell => {
|
|
636
|
+
try Effects.healBell(battle, player, options); // TODO
|
|
637
|
+
},
|
|
638
|
+
.HiddenPower => {
|
|
639
|
+
try Effects.hiddenPower(battle, player, options); // TODO
|
|
640
|
+
},
|
|
641
|
+
.HyperBeam => {
|
|
642
|
+
try Effects.hyperBeam(battle, player, options); // TODO
|
|
643
|
+
},
|
|
644
|
+
.JumpKick => {
|
|
645
|
+
try Effects.jumpKick(battle, player, options); // TODO
|
|
646
|
+
},
|
|
647
|
+
.LeechSeed => {
|
|
648
|
+
try Effects.leechSeed(battle, player, options); // TODO
|
|
649
|
+
},
|
|
650
|
+
.LevelDamage => {
|
|
651
|
+
try Effects.levelDamage(battle, player, options); // TODO
|
|
652
|
+
},
|
|
653
|
+
.LightScreen, .Reflect => {
|
|
654
|
+
try Effects.screens(battle, player, move, options); // TODO
|
|
655
|
+
},
|
|
656
|
+
.LockOn => {
|
|
657
|
+
try Effects.lockOn(battle, player, options); // TODO
|
|
658
|
+
},
|
|
659
|
+
.Magnitude => {
|
|
660
|
+
try Effects.magnitude(battle, player, options); // TODO
|
|
661
|
+
},
|
|
662
|
+
.MeanLook => {
|
|
663
|
+
try Effects.meanLook(battle, player, options); // TODO
|
|
664
|
+
},
|
|
665
|
+
.Metronome => {
|
|
666
|
+
try Effects.metronome(battle, player, options); // TODO
|
|
667
|
+
},
|
|
668
|
+
.Mimic => {
|
|
669
|
+
try Effects.mimic(battle, player, options); // TODO
|
|
670
|
+
},
|
|
671
|
+
.MirrorCoat => {
|
|
672
|
+
try Effects.mirrorCoat(battle, player, options); // TODO
|
|
673
|
+
},
|
|
674
|
+
.MirrorMove => {
|
|
675
|
+
try Effects.mirrorMove(battle, player, options); // TODO
|
|
676
|
+
},
|
|
677
|
+
.Mist => {
|
|
678
|
+
try Effects.mist(battle, player, options); // TODO
|
|
679
|
+
},
|
|
680
|
+
.Moonlight, .MorningSun => {
|
|
681
|
+
try Effects.weatherHeal(battle, player, options); // TODO
|
|
682
|
+
},
|
|
683
|
+
.MultiHit, .DoubleHit, .Twineedle => {
|
|
684
|
+
try Effects.multiHit(battle, player, options); // TODO
|
|
685
|
+
},
|
|
686
|
+
.Nightmare => {
|
|
687
|
+
try Effects.nightmare(battle, player, options); // TODO
|
|
688
|
+
},
|
|
689
|
+
.OHKO => {
|
|
690
|
+
try Effects.ohko(battle, player, options); // TODO
|
|
691
|
+
},
|
|
692
|
+
.PainSplit => {
|
|
693
|
+
try Effects.painSplit(battle, player, options); // TODO
|
|
694
|
+
},
|
|
695
|
+
.Paralyze => {
|
|
696
|
+
try Effects.paralyze(battle, player, options); // TODO
|
|
697
|
+
},
|
|
698
|
+
.ParalyzeChance, .Thunder => {
|
|
699
|
+
try Effects.paralyzeChance(battle, player, options); // TODO
|
|
700
|
+
},
|
|
701
|
+
.PayDay => {
|
|
702
|
+
try Effects.payDay(battle, player, options); // TODO
|
|
703
|
+
},
|
|
704
|
+
.PerishSong => {
|
|
705
|
+
try Effects.perishSong(battle, player, options); // TODO
|
|
706
|
+
},
|
|
707
|
+
.Poison, .PoisonChance, .Toxic => {
|
|
708
|
+
try Effects.poison(battle, player, move, options); // TODO
|
|
709
|
+
},
|
|
710
|
+
.Present => {
|
|
711
|
+
try Effects.present(battle, player, options); // TODO
|
|
712
|
+
},
|
|
713
|
+
.Protect => {
|
|
714
|
+
try Effects.protect(battle, player, Move.Protect, first, options); // TODO Detect?
|
|
715
|
+
},
|
|
716
|
+
.PsychUp => {
|
|
717
|
+
try Effects.psychUp(battle, player, options); // TODO
|
|
718
|
+
},
|
|
719
|
+
.Pursuit => {
|
|
720
|
+
try Effects.pursuit(battle, player, options); // TODO
|
|
721
|
+
},
|
|
722
|
+
.Rage => {
|
|
723
|
+
try Effects.rage(battle, player, options); // TODO
|
|
724
|
+
},
|
|
725
|
+
.RainDance => {
|
|
726
|
+
try Effects.rainDance(battle, player, options); // TODOq
|
|
727
|
+
},
|
|
728
|
+
.RapidSpin => {
|
|
729
|
+
try Effects.rapidSpin(battle, player, options); // TODO
|
|
730
|
+
},
|
|
731
|
+
.RazorWind => {
|
|
732
|
+
try Effects.razorWind(battle, player, options); // TODO
|
|
733
|
+
},
|
|
734
|
+
.Recoil => {
|
|
735
|
+
try Effects.recoil(battle, player, options); // TODO
|
|
736
|
+
},
|
|
737
|
+
.Reversal => {
|
|
738
|
+
try Effects.reversal(battle, player, options); // TODO
|
|
739
|
+
},
|
|
740
|
+
.Rollout => {
|
|
741
|
+
try Effects.rollout(battle, player, options); // TODO
|
|
742
|
+
},
|
|
743
|
+
.SacredFire => {
|
|
744
|
+
try Effects.sacredFire(battle, player, options); // TODO
|
|
745
|
+
},
|
|
746
|
+
.Safeguard => {
|
|
747
|
+
try Effects.safeguard(battle, player, options); // TODO
|
|
748
|
+
},
|
|
749
|
+
.Sandstorm => {
|
|
750
|
+
try Effects.sandstorm(battle, player, options); // TODO
|
|
751
|
+
},
|
|
752
|
+
.Sketch => {
|
|
753
|
+
try Effects.sketch(battle, player, options); // TODO
|
|
754
|
+
},
|
|
755
|
+
.SkullBash => {
|
|
756
|
+
try Effects.skullBash(battle, player, options); // TODO
|
|
757
|
+
},
|
|
758
|
+
.SkyAttack => {
|
|
759
|
+
try Effects.skyAttack(battle, player, options); // TODO
|
|
760
|
+
},
|
|
761
|
+
.Sleep => {
|
|
762
|
+
try Effects.sleep(battle, player, options); // TODO
|
|
763
|
+
},
|
|
764
|
+
.SleepTalk => {
|
|
765
|
+
try Effects.sleepTalk(battle, player, mslot, options); // TODO
|
|
766
|
+
},
|
|
767
|
+
.Snore => {
|
|
768
|
+
try Effects.snore(battle, player, options); // TODO
|
|
769
|
+
},
|
|
770
|
+
.Solarbeam => {
|
|
771
|
+
try Effects.solarBeam(battle, player, options); // TODO
|
|
772
|
+
},
|
|
773
|
+
.Spikes => {
|
|
774
|
+
try Effects.spikes(battle, player, options); // TODO
|
|
775
|
+
},
|
|
776
|
+
.Spite => {
|
|
777
|
+
try Effects.spite(battle, player, options); // TODO
|
|
778
|
+
},
|
|
779
|
+
.Splash => {
|
|
780
|
+
try Effects.splash(battle, player, options); // TODO
|
|
781
|
+
},
|
|
782
|
+
.Stomp => {
|
|
783
|
+
try Effects.stomp(battle, player, options); // TODO
|
|
784
|
+
},
|
|
785
|
+
.Substitute => {
|
|
786
|
+
try Effects.substitute(battle, player, options); // TODO
|
|
787
|
+
},
|
|
788
|
+
.SunnyDay => {
|
|
789
|
+
try Effects.sunnyDay(battle, player, options); // TODO
|
|
790
|
+
},
|
|
791
|
+
.Swagger => {
|
|
792
|
+
try Effects.swagger(battle, player, options); // TODO
|
|
793
|
+
},
|
|
794
|
+
.Synthesis => {
|
|
795
|
+
try Effects.synthesis(battle, player, options); // TODO
|
|
796
|
+
},
|
|
524
797
|
.Teleport => {
|
|
525
|
-
|
|
526
|
-
|
|
798
|
+
try Effects.teleport(battle, player, options); // TODO
|
|
799
|
+
},
|
|
800
|
+
.Thief => {
|
|
801
|
+
try Effects.thief(battle, player, options); // TODO
|
|
802
|
+
},
|
|
803
|
+
.Thrashing => {
|
|
804
|
+
try Effects.thrashing(battle, player, options); // TODO
|
|
805
|
+
},
|
|
806
|
+
.Transform => {
|
|
807
|
+
try Effects.transform(battle, player, options); // TODO
|
|
808
|
+
},
|
|
809
|
+
.TripleKick => {
|
|
810
|
+
try Effects.tripleKick(battle, player, options); // TODO
|
|
811
|
+
},
|
|
812
|
+
.Twister => {
|
|
813
|
+
try Effects.twister(battle, player, options); // TODO
|
|
814
|
+
},
|
|
815
|
+
// zig fmt: off
|
|
816
|
+
.AllStatUpChance,
|
|
817
|
+
.AttackUp1, .AttackUp2, .AttackUpChance,
|
|
818
|
+
.DefenseUp1, .DefenseUp2, .DefenseUpChance,
|
|
819
|
+
.EvasionUp1,
|
|
820
|
+
.SpAtkUp1,
|
|
821
|
+
.SpDefUp2,
|
|
822
|
+
.SpeedUp2 => {
|
|
823
|
+
// zig fmt: on
|
|
824
|
+
try Effects.boost(battle, player, move, options); // TODO
|
|
825
|
+
},
|
|
826
|
+
// zig fmt: off
|
|
827
|
+
.AccuracyDown1, .AccuracyDownChance,
|
|
828
|
+
.AttackDown1, .AttackDown2, .AttackDownChance,
|
|
829
|
+
.DefenseDown1, .DefenseDown2, .DefenseDownChance,
|
|
830
|
+
.EvasionDown1,
|
|
831
|
+
.SpDefDownChance,
|
|
832
|
+
.SpeedDown1, .SpeedDown2, .SpeedDownChance => {
|
|
833
|
+
// zig fmt: on
|
|
834
|
+
try Effects.unboost(battle, player, move, options); // TODO
|
|
527
835
|
},
|
|
528
|
-
else => @panic("TODO"),
|
|
529
836
|
}
|
|
530
837
|
return null;
|
|
531
838
|
}
|
|
@@ -772,7 +1079,7 @@ fn handleResidual(battle: anytype, player: Player, options: anytype) !void {
|
|
|
772
1079
|
}
|
|
773
1080
|
}
|
|
774
1081
|
|
|
775
|
-
fn betweenTurns(battle: anytype, mslot:
|
|
1082
|
+
fn betweenTurns(battle: anytype, mslot: u3, options: anytype) !void {
|
|
776
1083
|
const players = std.enums.values(Player);
|
|
777
1084
|
|
|
778
1085
|
// TODO checkFaintThen(p1, p2);
|
|
@@ -1027,24 +1334,543 @@ fn endTurn(battle: anytype, options: anytype) @TypeOf(options.log).Error!Result
|
|
|
1027
1334
|
}
|
|
1028
1335
|
|
|
1029
1336
|
pub const Effects = struct {
|
|
1030
|
-
fn
|
|
1337
|
+
fn alwaysHit(battle: anytype, player: Player, options: anytype) !void {
|
|
1338
|
+
_ = .{ battle, player, options }; // TODO
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
fn attract(battle: anytype, player: Player, options: anytype) !void {
|
|
1342
|
+
const side = battle.side(player);
|
|
1343
|
+
var foe = battle.foe(player);
|
|
1344
|
+
|
|
1345
|
+
const gender: u8 = @intFromEnum(side.stored().dvs.gender);
|
|
1346
|
+
const foe_gender: u8 = @intFromEnum(foe.stored().dvs.gender);
|
|
1347
|
+
if (gender + foe_gender != 1) {
|
|
1348
|
+
return try options.log.immune(battle.active(player.foe()), .None);
|
|
1349
|
+
} else if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1350
|
+
try options.log.lastmiss();
|
|
1351
|
+
return try options.log.miss(battle.active(player));
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
foe.active.volatiles.Attract = true;
|
|
1355
|
+
try options.log.start(battle.active(player.foe()), .Attract);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
fn batonPass(battle: anytype, player: Player, options: anytype) !void {
|
|
1359
|
+
_ = .{ battle, player, options }; // TODO
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
fn beatUp(battle: anytype, player: Player, options: anytype) !void {
|
|
1363
|
+
_ = .{ battle, player, options }; // TODO
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
fn bellyDrum(battle: anytype, player: Player, options: anytype) !void {
|
|
1367
|
+
_ = .{ battle, player, options }; // TODO
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
fn bide(battle: anytype, player: Player, options: anytype) !void {
|
|
1371
|
+
_ = .{ battle, player, options }; // TODO
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
fn binding(battle: anytype, player: Player, options: anytype) !void {
|
|
1375
|
+
_ = .{ battle, player, options }; // TODO
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
fn burnChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1379
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
fn confusion(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1383
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
fn conversion(battle: anytype, player: Player, options: anytype) !void {
|
|
1031
1387
|
var side = battle.side(player);
|
|
1032
|
-
var volatiles = &side.active.volatiles;
|
|
1033
1388
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1389
|
+
var n: u3 = 0;
|
|
1390
|
+
for (side.active.moves) |m| {
|
|
1391
|
+
if (convertible(side.active, m.id)) n += 1;
|
|
1392
|
+
}
|
|
1393
|
+
if (n == 0) return try options.log.fail(battle.active(player), .None);
|
|
1394
|
+
|
|
1395
|
+
side.active.types.type1 = try Rolls.conversion(battle, player, n, options);
|
|
1396
|
+
side.active.types.type2 = side.active.types.type1;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
fn conversion2(battle: anytype, player: Player, options: anytype) !void {
|
|
1400
|
+
var side = battle.side(player);
|
|
1401
|
+
|
|
1402
|
+
const last = battle.foe(player).lastMove(false);
|
|
1403
|
+
if (last == .None) return try options.log.fail(battle.active(player), .None);
|
|
1404
|
+
const move = Move.get(last);
|
|
1405
|
+
if (move.type == .@"???") return try options.log.fail(battle.active(player), .None);
|
|
1406
|
+
|
|
1407
|
+
side.active.types.type1 = try Rolls.conversion2(battle, player, move.type, options);
|
|
1408
|
+
side.active.types.type2 = side.active.types.type1;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
fn counter(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1412
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
fn curse(battle: anytype, player: Player, options: anytype) !void {
|
|
1416
|
+
_ = .{ battle, player, options }; // TODO
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
fn defenseCurl(battle: anytype, player: Player, options: anytype) !void {
|
|
1420
|
+
_ = .{ battle, player, options }; // TODO
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
fn destinyBond(battle: anytype, player: Player, options: anytype) !void {
|
|
1424
|
+
battle.side(player).active.volatiles.DestinyBond = true;
|
|
1425
|
+
try options.log.singlemove(battle.active(player), Move.DestinyBond);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
fn disable(battle: anytype, player: Player, options: anytype) !void {
|
|
1429
|
+
var foe = battle.foe(player);
|
|
1430
|
+
var volatiles = &foe.active.volatiles;
|
|
1431
|
+
const foe_ident = battle.active(player.foe());
|
|
1432
|
+
|
|
1433
|
+
if (volatiles.disabled.move != 0) return try options.log.fail(foe_ident, .None);
|
|
1434
|
+
|
|
1435
|
+
const last = battle.foe(player).lastMove(false);
|
|
1436
|
+
if (last == .None or last == .Struggle) return try options.log.fail(foe_ident, .None);
|
|
1437
|
+
|
|
1438
|
+
var slot: u3 = 0;
|
|
1439
|
+
for (foe.active.moves) |m| {
|
|
1440
|
+
assert(m.id != .None);
|
|
1441
|
+
slot += 1;
|
|
1442
|
+
if (m.id == last) break;
|
|
1443
|
+
}
|
|
1444
|
+
const move = foe.active.move(slot);
|
|
1445
|
+
if (move.pp == 0) return try options.log.fail(foe_ident, .None);
|
|
1446
|
+
|
|
1447
|
+
volatiles.disabled.move = slot;
|
|
1448
|
+
volatiles.disabled.duration = Rolls.disableDuration(battle, player, options);
|
|
1449
|
+
|
|
1450
|
+
try options.log.startEffect(foe_ident, .Disable, move.id);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
fn doubleHit(battle: anytype, player: Player, options: anytype) !void {
|
|
1454
|
+
_ = .{ battle, player, options }; // TODO
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
fn drainHP(battle: anytype, player: Player, options: anytype) !void {
|
|
1458
|
+
_ = .{ battle, player, options }; // TODO
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
fn encore(battle: anytype, player: Player, first: bool, options: anytype) !void {
|
|
1462
|
+
var foe = battle.foe(player);
|
|
1463
|
+
var volatiles = &foe.active.volatiles;
|
|
1464
|
+
const foe_ident = battle.active(player.foe());
|
|
1465
|
+
|
|
1466
|
+
const last = battle.foe(player).lastMove(true);
|
|
1467
|
+
const failed = volatiles.Encore or
|
|
1468
|
+
(last == .None or last == .Struggle or
|
|
1469
|
+
last == .Encore or .last == .MirrorMove);
|
|
1470
|
+
if (failed) return try options.log.fail(foe_ident, .None);
|
|
1471
|
+
|
|
1472
|
+
var slot: u3 = 0;
|
|
1473
|
+
for (foe.active.moves) |m| {
|
|
1474
|
+
assert(m.id != .None);
|
|
1475
|
+
slot += 1;
|
|
1476
|
+
if (m.id == last) break;
|
|
1477
|
+
}
|
|
1478
|
+
const move = foe.active.move(slot);
|
|
1479
|
+
if (move.pp == 0) return try options.log.fail(foe_ident, .None);
|
|
1480
|
+
|
|
1481
|
+
volatiles.Encore = true;
|
|
1482
|
+
volatiles.encore = Rolls.encoreDuration(battle, player, options);
|
|
1483
|
+
|
|
1484
|
+
// TODO ???
|
|
1485
|
+
_ = first;
|
|
1486
|
+
|
|
1487
|
+
try options.log.startEffect(foe_ident, .Encore, move.id);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
fn explode(battle: anytype, player: Player, options: anytype) !void {
|
|
1491
|
+
_ = .{ battle, player, options }; // TODO
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
fn falseSwipe(battle: anytype, player: Player, damage: u16) !u16 {
|
|
1495
|
+
const foe = battle.foe(player);
|
|
1496
|
+
assert(foe.stored().hp > 0);
|
|
1497
|
+
// FIXME crit?
|
|
1498
|
+
return if (damage > foe.stored().hp) foe.stored().hp - 1 else damage;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
fn fixedDamage(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1502
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
fn flameWheel(battle: anytype, player: Player, options: anytype) !void {
|
|
1506
|
+
_ = .{ battle, player, options }; // TODO
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
fn flinchChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1510
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
fn flyDig(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1514
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
fn focusEnergy(battle: anytype, player: Player, options: anytype) !void {
|
|
1518
|
+
var side = battle.side(player);
|
|
1519
|
+
|
|
1520
|
+
if (side.active.volatiles.FocusEnergy) {
|
|
1521
|
+
return if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
side.active.volatiles.FocusEnergy = true;
|
|
1525
|
+
try options.log.start(battle.active(player), .FocusEnergy);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
var SLOTS: [5]u3 = .{ 0, 0, 0, 0, 0 };
|
|
1529
|
+
|
|
1530
|
+
fn forceSwitch(battle: anytype, player: Player, options: anytype) !void {
|
|
1531
|
+
const foe = battle.foe(player);
|
|
1532
|
+
|
|
1533
|
+
var n: u3 = 0;
|
|
1534
|
+
var i: u3 = 0;
|
|
1535
|
+
for (foe.pokemon, 0..) |pokemon, j| {
|
|
1536
|
+
if (pokemon.species == .None) break;
|
|
1537
|
+
n += 1;
|
|
1538
|
+
if (pokemon.position == 1) continue;
|
|
1539
|
+
if (pokemon.hp > 0) {
|
|
1540
|
+
SLOTS[i] = @intCast(foe.pokemon[j].position);
|
|
1541
|
+
i += 1;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
if (n == 0) try options.log.fail(battle.active(player), .None);
|
|
1546
|
+
|
|
1547
|
+
_ = try Rolls.forceSwitch(battle, player, SLOTS[0..i], n, options); // TODO
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
fn foresight(battle: anytype, player: Player, options: anytype) !void {
|
|
1551
|
+
var foe = battle.foe(player);
|
|
1552
|
+
|
|
1553
|
+
if (foe.active.volatiles.Flying or foe.active.volatiles.Underground) {
|
|
1554
|
+
try options.log.lastmiss();
|
|
1555
|
+
return try options.log.miss(battle.active(player));
|
|
1556
|
+
} else if (foe.active.volatiles.Foresight) {
|
|
1557
|
+
return try options.log.fail(battle.active(player), .None);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
foe.active.volatiles.Foresight = true;
|
|
1561
|
+
try options.log.start(battle.active(player.foe()), .FocusEnergy);
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
fn freezeChance(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1565
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
fn furyCutter(battle: anytype, player: Player, options: anytype) !void {
|
|
1569
|
+
_ = .{ battle, player, options }; // TODO
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
fn futureSight(battle: anytype, player: Player, options: anytype) !void {
|
|
1573
|
+
_ = .{ battle, player, options }; // TODO
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
fn happiness(battle: anytype, player: Player, move: Move.Data) !u8 {
|
|
1577
|
+
const val = battle.side(player).stored().happiness;
|
|
1578
|
+
return ((if (move.effect == .Frustration) 255 - val else val) * 10) / 25;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
fn haze(battle: anytype, player: Player, options: anytype) !void {
|
|
1582
|
+
_ = .{ battle, player, options }; // TODO
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
fn heal(battle: anytype, player: Player, options: anytype) !void {
|
|
1586
|
+
_ = .{ battle, player, options }; // TODO
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
fn healBell(battle: anytype, player: Player, options: anytype) !void {
|
|
1590
|
+
_ = .{ battle, player, options }; // TODO
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
fn hiddenPower(battle: anytype, player: Player, options: anytype) !void {
|
|
1594
|
+
_ = .{ battle, player, options }; // TODO
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
fn hyperBeam(battle: anytype, player: Player, options: anytype) !void {
|
|
1598
|
+
_ = .{ battle, player, options }; // TODO
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
fn jumpKick(battle: anytype, player: Player, options: anytype) !void {
|
|
1602
|
+
_ = .{ battle, player, options }; // TODO
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
fn leechSeed(battle: anytype, player: Player, options: anytype) !void {
|
|
1606
|
+
_ = .{ battle, player, options }; // TODO
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
fn levelDamage(battle: anytype, player: Player, options: anytype) !void {
|
|
1610
|
+
_ = .{ battle, player, options }; // TODO
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
fn lockOn(battle: anytype, player: Player, options: anytype) !void {
|
|
1614
|
+
_ = .{ battle, player, options }; // TODO
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
const MAGNITUDE_POWER = [_]u8{ 10, 30, 50, 70, 90, 110, 150 };
|
|
1618
|
+
|
|
1619
|
+
fn magnitude(battle: anytype, player: Player, options: anytype) !void {
|
|
1620
|
+
_ = .{ battle, player, options }; // TODO
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
fn meanLook(battle: anytype, player: Player, options: anytype) !void {
|
|
1624
|
+
_ = .{ battle, player, options }; // TODO
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
fn metronome(battle: anytype, player: Player, options: anytype) !void {
|
|
1628
|
+
_ = .{ battle, player, options }; // TODO
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
fn mimic(battle: anytype, player: Player, options: anytype) !void {
|
|
1632
|
+
_ = .{ battle, player, options }; // TODO
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
fn mirrorCoat(battle: anytype, player: Player, options: anytype) !void {
|
|
1636
|
+
_ = .{ battle, player, options }; // TODO
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
fn mirrorMove(battle: anytype, player: Player, options: anytype) !void {
|
|
1640
|
+
_ = .{ battle, player, options }; // TODO
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
fn mist(battle: anytype, player: Player, options: anytype) !void {
|
|
1644
|
+
_ = .{ battle, player, options }; // TODO
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
fn weatherHeal(battle: anytype, player: Player, options: anytype) !void {
|
|
1648
|
+
_ = .{ battle, player, options }; // TODO
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
fn multiHit(battle: anytype, player: Player, options: anytype) !void {
|
|
1652
|
+
_ = .{ battle, player, options }; // TODO
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
fn nightmare(battle: anytype, player: Player, options: anytype) !void {
|
|
1656
|
+
_ = .{ battle, player, options }; // TODO
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
fn ohko(battle: anytype, player: Player, options: anytype) !void {
|
|
1660
|
+
_ = .{ battle, player, options }; // TODO
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
fn painSplit(battle: anytype, player: Player, options: anytype) !void {
|
|
1664
|
+
_ = .{ battle, player, options }; // TODO
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
fn paralyze(battle: anytype, player: Player, options: anytype) !void {
|
|
1668
|
+
_ = .{ battle, player, options }; // TODO
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
fn paralyzeChance(battle: anytype, player: Player, options: anytype) !void {
|
|
1672
|
+
// TODO: Thunder does acc/checkHit/effectChance before stab + damage variation
|
|
1673
|
+
_ = .{ battle, player, options }; // TODO
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
fn payDay(battle: anytype, player: Player, options: anytype) !void {
|
|
1677
|
+
_ = .{ battle, player, options }; // TODO
|
|
1678
|
+
}
|
|
1036
1679
|
|
|
1037
|
-
|
|
1680
|
+
fn perishSong(battle: anytype, player: Player, options: anytype) !void {
|
|
1681
|
+
_ = .{ battle, player, options }; // TODO
|
|
1682
|
+
}
|
|
1038
1683
|
|
|
1039
|
-
|
|
1684
|
+
fn poison(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1685
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
fn present(battle: anytype, player: Player, options: anytype) !void {
|
|
1689
|
+
_ = .{ battle, player, options }; // TODO
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
fn protect(
|
|
1693
|
+
battle: anytype,
|
|
1694
|
+
player: Player,
|
|
1695
|
+
move: Move,
|
|
1696
|
+
first: bool,
|
|
1697
|
+
options: anytype,
|
|
1698
|
+
) !void {
|
|
1699
|
+
var side = battle.side(player);
|
|
1700
|
+
var volatiles = &side.active.volatiles;
|
|
1701
|
+
|
|
1702
|
+
const failed = !first or volatiles.Substitute or
|
|
1703
|
+
!try Rolls.protect(battle, player, volatiles.protect, options);
|
|
1704
|
+
if (failed) return try options.log.fail(battle.active(player), .None);
|
|
1705
|
+
|
|
1706
|
+
volatiles.protect = @max(8, volatiles.protect + 1);
|
|
1707
|
+
if (move == .Endure) {
|
|
1040
1708
|
volatiles.Endure = true;
|
|
1041
1709
|
} else {
|
|
1042
1710
|
volatiles.Protect = true;
|
|
1043
1711
|
}
|
|
1044
|
-
try options.log.singleturn(battle.active(player), move
|
|
1712
|
+
try options.log.singleturn(battle.active(player), move);
|
|
1045
1713
|
}
|
|
1046
1714
|
|
|
1047
|
-
|
|
1715
|
+
fn psychUp(battle: anytype, player: Player, options: anytype) !void {
|
|
1716
|
+
_ = .{ battle, player, options }; // TODO
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
fn pursuit(battle: anytype, player: Player, options: anytype) !void {
|
|
1720
|
+
_ = .{ battle, player, options }; // TODO
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
fn rage(battle: anytype, player: Player, options: anytype) !void {
|
|
1724
|
+
_ = .{ battle, player, options }; // TODO
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
fn rainDance(battle: anytype, player: Player, options: anytype) !void {
|
|
1728
|
+
_ = .{ battle, player, options }; // TODO
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
fn rapidSpin(battle: anytype, player: Player, options: anytype) !void {
|
|
1732
|
+
_ = .{ battle, player, options }; // TODO
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
fn razorWind(battle: anytype, player: Player, options: anytype) !void {
|
|
1736
|
+
_ = .{ battle, player, options }; // TODO
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
fn recoil(battle: anytype, player: Player, options: anytype) !void {
|
|
1740
|
+
_ = .{ battle, player, options }; // TODO
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
fn reversal(battle: anytype, player: Player, options: anytype) !void {
|
|
1744
|
+
_ = .{ battle, player, options }; // TODO
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
fn rollout(battle: anytype, player: Player, options: anytype) !void {
|
|
1748
|
+
_ = .{ battle, player, options }; // TODO
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
fn sacredFire(battle: anytype, player: Player, options: anytype) !void {
|
|
1752
|
+
_ = .{ battle, player, options }; // TODO
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
fn safeguard(battle: anytype, player: Player, options: anytype) !void {
|
|
1756
|
+
_ = .{ battle, player, options }; // TODO
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
fn sandstorm(battle: anytype, player: Player, options: anytype) !void {
|
|
1760
|
+
_ = .{ battle, player, options }; // TODO
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
fn screens(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1764
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
fn sketch(battle: anytype, player: Player, options: anytype) !void {
|
|
1768
|
+
_ = .{ battle, player, options }; // TODO
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
fn skullBash(battle: anytype, player: Player, options: anytype) !void {
|
|
1772
|
+
_ = .{ battle, player, options }; // TODO
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
fn skyAttack(battle: anytype, player: Player, options: anytype) !void {
|
|
1776
|
+
_ = .{ battle, player, options }; // TODO
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
fn sleep(battle: anytype, player: Player, options: anytype) !void {
|
|
1780
|
+
_ = .{ battle, player, options }; // TODO
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
fn sleepTalk(battle: anytype, player: Player, mslot: u3, options: anytype) !void {
|
|
1784
|
+
const side = battle.side(player);
|
|
1785
|
+
|
|
1786
|
+
if (!Status.is(side.stored().status, .SLP)) {
|
|
1787
|
+
return if (!showdown) try options.log.fail(battle.active(player), .None);
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
var n: u3 = 0;
|
|
1791
|
+
for (side.active.moves, 0..) |m, i| {
|
|
1792
|
+
if (mslot - 1 != i and Move.get(m.id).extra.sleep_talk) n += 1;
|
|
1793
|
+
}
|
|
1794
|
+
if (n == 0) return try options.log.fail(battle.active(player), .None);
|
|
1795
|
+
|
|
1796
|
+
_ = try Rolls.sleepTalk(battle, player, n, mslot, options); // TODO
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
fn snore(battle: anytype, player: Player, options: anytype) !void {
|
|
1800
|
+
_ = .{ battle, player, options }; // TODO
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
fn solarBeam(battle: anytype, player: Player, options: anytype) !void {
|
|
1804
|
+
_ = .{ battle, player, options }; // TODO
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
fn spikes(battle: anytype, player: Player, options: anytype) !void {
|
|
1808
|
+
_ = .{ battle, player, options }; // TODO
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
fn spite(battle: anytype, player: Player, options: anytype) !void {
|
|
1812
|
+
_ = .{ battle, player, options }; // TODO
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
fn splash(battle: anytype, player: Player, options: anytype) !void {
|
|
1816
|
+
_ = .{ battle, player, options }; // TODO
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
fn stomp(battle: anytype, player: Player, options: anytype) !void {
|
|
1820
|
+
_ = .{ battle, player, options }; // TODO
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
fn substitute(battle: anytype, player: Player, options: anytype) !void {
|
|
1824
|
+
_ = .{ battle, player, options }; // TODO
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
fn sunnyDay(battle: anytype, player: Player, options: anytype) !void {
|
|
1828
|
+
_ = .{ battle, player, options }; // TODO
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
fn swagger(battle: anytype, player: Player, options: anytype) !void {
|
|
1832
|
+
_ = .{ battle, player, options }; // TODO
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
fn synthesis(battle: anytype, player: Player, options: anytype) !void {
|
|
1836
|
+
_ = .{ battle, player, options }; // TODO
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
fn teleport(battle: anytype, player: Player, options: anytype) !void {
|
|
1840
|
+
_ = .{ battle, player, options }; // TODO
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
fn thief(battle: anytype, player: Player, options: anytype) !void {
|
|
1844
|
+
_ = .{ battle, player, options }; // TODO
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
fn thrashing(battle: anytype, player: Player, options: anytype) !void {
|
|
1848
|
+
_ = .{ battle, player, options }; // TODO
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
fn toxic(battle: anytype, player: Player, options: anytype) !void {
|
|
1852
|
+
_ = .{ battle, player, options }; // TODO
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
fn transform(battle: anytype, player: Player, options: anytype) !void {
|
|
1856
|
+
_ = .{ battle, player, options }; // TODO
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
fn tripleKick(battle: anytype, player: Player, options: anytype) !void {
|
|
1860
|
+
_ = .{ battle, player, options }; // TODO
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
fn twister(battle: anytype, player: Player, options: anytype) !void {
|
|
1864
|
+
_ = .{ battle, player, options }; // TODO
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
fn boost(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1868
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
fn unboost(battle: anytype, player: Player, move: Move.Data, options: anytype) !void {
|
|
1872
|
+
_ = .{ battle, player, move, options }; // TODO
|
|
1873
|
+
}
|
|
1048
1874
|
};
|
|
1049
1875
|
|
|
1050
1876
|
fn statusModify(status: u8, stats: *Stats(u16)) void {
|
|
@@ -1061,6 +1887,12 @@ inline fn isForced(active: anytype) bool {
|
|
|
1061
1887
|
active.volatiles.Rollout;
|
|
1062
1888
|
}
|
|
1063
1889
|
|
|
1890
|
+
inline fn convertible(active: ActivePokemon, m: Move) bool {
|
|
1891
|
+
if (m == .None) return false;
|
|
1892
|
+
const t = Move.get(m).type;
|
|
1893
|
+
return t != .@"???" and !active.types.includes(t);
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1064
1896
|
pub const Rolls = struct {
|
|
1065
1897
|
inline fn speedTie(battle: anytype, options: anytype) !bool {
|
|
1066
1898
|
const p1 = if (options.calc.overridden(.P1, .speed_tie)) |player|
|
|
@@ -1187,47 +2019,25 @@ pub const Rolls = struct {
|
|
|
1187
2019
|
return proc;
|
|
1188
2020
|
}
|
|
1189
2021
|
|
|
1190
|
-
inline fn
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
else if (showdown)
|
|
1194
|
-
battle.rng.chance(u8, 30, 256)
|
|
1195
|
-
else
|
|
1196
|
-
battle.rng.next() < 30;
|
|
1197
|
-
|
|
1198
|
-
try options.chance.focusBand(player, proc);
|
|
1199
|
-
return proc;
|
|
1200
|
-
}
|
|
2022
|
+
inline fn item(battle: anytype, player: Player, options: anytype, effect: Item) !bool {
|
|
2023
|
+
assert(effect == .FocusBand or effect == .KingsRock);
|
|
2024
|
+
const roll = if (effect == .FocusBand) .focus_band else .kings_rock;
|
|
1201
2025
|
|
|
1202
|
-
|
|
1203
|
-
const proc = if (options.calc.overridden(player, .kings_rock)) |val|
|
|
2026
|
+
const proc = if (options.calc.overridden(player, roll)) |val|
|
|
1204
2027
|
val == .true
|
|
1205
2028
|
else if (showdown)
|
|
1206
2029
|
battle.rng.chance(u8, 30, 256)
|
|
1207
2030
|
else
|
|
1208
2031
|
battle.rng.next() < 30;
|
|
1209
2032
|
|
|
1210
|
-
|
|
2033
|
+
if (effect == .FocusBand) {
|
|
2034
|
+
try options.chance.focusBand(player, proc);
|
|
2035
|
+
} else {
|
|
2036
|
+
try options.chance.kingsRock(player, proc);
|
|
2037
|
+
}
|
|
1211
2038
|
return proc;
|
|
1212
2039
|
}
|
|
1213
2040
|
|
|
1214
|
-
inline fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
1215
|
-
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
1216
|
-
@intCast(val)
|
|
1217
|
-
else if (showdown)
|
|
1218
|
-
@intCast(battle.rng.range(u8, 1, 7))
|
|
1219
|
-
else duration: {
|
|
1220
|
-
while (true) {
|
|
1221
|
-
const r = battle.rng.next() & 7;
|
|
1222
|
-
if (r != 0 and r != 7) break :duration @intCast(r);
|
|
1223
|
-
}
|
|
1224
|
-
};
|
|
1225
|
-
|
|
1226
|
-
assert(duration >= 1 and duration <= 6);
|
|
1227
|
-
options.chance.duration(.sleep, player, player.foe(), duration);
|
|
1228
|
-
return duration + 1;
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
2041
|
inline fn triAttack(battle: anytype, player: Player, options: anytype) !u8 {
|
|
1232
2042
|
const status: TriAttack = if (options.calc.overridden(player, .tri_attack)) |val|
|
|
1233
2043
|
val
|
|
@@ -1236,7 +2046,7 @@ pub const Rolls = struct {
|
|
|
1236
2046
|
else loop: {
|
|
1237
2047
|
while (true) {
|
|
1238
2048
|
const r = std.math.rotr(u8, std.battle.rng.next(), 4) & 3;
|
|
1239
|
-
if (r != 0) break :loop @
|
|
2049
|
+
if (r != 0) break :loop @enumFromInt(r - 1);
|
|
1240
2050
|
}
|
|
1241
2051
|
};
|
|
1242
2052
|
|
|
@@ -1294,11 +2104,11 @@ pub const Rolls = struct {
|
|
|
1294
2104
|
return num;
|
|
1295
2105
|
}
|
|
1296
2106
|
|
|
1297
|
-
inline fn tripleKick(battle: anytype, player: Player, options: anytype) !
|
|
2107
|
+
inline fn tripleKick(battle: anytype, player: Player, options: anytype) !u2 {
|
|
1298
2108
|
const hits = if (options.calc.overridden(player, .triple_kick)) |val|
|
|
1299
2109
|
val
|
|
1300
2110
|
else if (showdown)
|
|
1301
|
-
battle.rng.range(
|
|
2111
|
+
battle.rng.range(u2, 1, 4)
|
|
1302
2112
|
else power: {
|
|
1303
2113
|
while (true) {
|
|
1304
2114
|
const r = battle.rng.next() & 3;
|
|
@@ -1325,7 +2135,7 @@ pub const Rolls = struct {
|
|
|
1325
2135
|
}
|
|
1326
2136
|
|
|
1327
2137
|
// Conversion 2 can at most choose between 7 types (vs. a Grass-type attack)
|
|
1328
|
-
|
|
2138
|
+
var CONVERSION_2: [7]Type = [_]Type{.@"???"} ** 7;
|
|
1329
2139
|
|
|
1330
2140
|
inline fn conversion2(battle: anytype, player: Player, mtype: Type, options: anytype) !Type {
|
|
1331
2141
|
var i: u8 = 0;
|
|
@@ -1349,7 +2159,7 @@ pub const Rolls = struct {
|
|
|
1349
2159
|
if (r > 9 and r < 20) continue; // UNUSED_TYPES
|
|
1350
2160
|
if (r > 27) continue; // TYPES_END
|
|
1351
2161
|
|
|
1352
|
-
const t =
|
|
2162
|
+
const t = Type.conversion2(r);
|
|
1353
2163
|
if (@intFromEnum(mtype.effectiveness(t)) < neutral) {
|
|
1354
2164
|
break :ty t;
|
|
1355
2165
|
}
|
|
@@ -1361,17 +2171,152 @@ pub const Rolls = struct {
|
|
|
1361
2171
|
return ty;
|
|
1362
2172
|
}
|
|
1363
2173
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
2174
|
+
inline fn protect(battle: anytype, player: Player, count: u8, options: anytype) !bool {
|
|
2175
|
+
assert(count <= 8);
|
|
2176
|
+
const num = if (count == 8) 0 else @as(u8, 255) >> @intCast(count);
|
|
2177
|
+
|
|
2178
|
+
const ok = if (options.calc.overridden(player, .protect)) |val|
|
|
2179
|
+
val == .true
|
|
2180
|
+
else if (showdown)
|
|
2181
|
+
battle.rng.chance(u8, num, 255)
|
|
2182
|
+
else protected: {
|
|
2183
|
+
while (true) {
|
|
2184
|
+
const r = battle.rng.next();
|
|
2185
|
+
if (r != 0) break :protected !(num <= r - 1);
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
|
|
2189
|
+
try options.chance.protect(player, num, ok);
|
|
2190
|
+
return ok;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
fn conversion(battle: anytype, player: Player, n: u3, options: anytype) !Type {
|
|
2194
|
+
assert(n > 0);
|
|
2195
|
+
|
|
2196
|
+
const active = battle.side(player).active;
|
|
2197
|
+
const moves = active.moves;
|
|
2198
|
+
|
|
2199
|
+
// TODO: consider throwing error instead of rerolling?
|
|
2200
|
+
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
2201
|
+
if (convertible(active, moves[val - 1].id)) val else null
|
|
2202
|
+
else
|
|
2203
|
+
null;
|
|
2204
|
+
|
|
2205
|
+
const slot: u3 = overridden orelse slot: {
|
|
2206
|
+
if (showdown) {
|
|
2207
|
+
var r = battle.rng.range(u3, 0, n) + 1;
|
|
2208
|
+
var i: usize = 0;
|
|
2209
|
+
while (i < moves.len and r > 0) : (i += 1) {
|
|
2210
|
+
if (convertible(active, moves[i].id)) {
|
|
2211
|
+
r -= 1;
|
|
2212
|
+
if (r == 0) break :slot @intCast(i + 1);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
break :slot @intCast(i + 1);
|
|
2216
|
+
} else {
|
|
2217
|
+
while (true) {
|
|
2218
|
+
const r: u3 = @intCast(battle.rng.next() & 3);
|
|
2219
|
+
if (convertible(active, moves[r].id)) break :slot @intCast(r + 1);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
};
|
|
2223
|
+
|
|
2224
|
+
assert(n >= 1 and n <= 4);
|
|
2225
|
+
assert(slot >= 1 and slot <= 4);
|
|
2226
|
+
try options.chance.moveSlot(player, slot, n);
|
|
2227
|
+
return Move.get(active.move(slot).id).type;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
fn sleepTalk(battle: anytype, player: Player, n: u3, mslot: u3, options: anytype) !u3 {
|
|
2231
|
+
assert(n > 0);
|
|
2232
|
+
|
|
2233
|
+
const active = battle.side(player).active;
|
|
2234
|
+
const moves = active.moves;
|
|
2235
|
+
|
|
2236
|
+
// TODO: consider throwing error instead of rerolling?
|
|
2237
|
+
const overridden = if (options.calc.overridden(player, .move_slot)) |val|
|
|
2238
|
+
if (val != mslot and Move.get(moves[val - 1].id).extra.sleep_talk) val else null
|
|
2239
|
+
else
|
|
2240
|
+
null;
|
|
2241
|
+
|
|
2242
|
+
const slot: u3 = overridden orelse slot: {
|
|
2243
|
+
if (showdown) {
|
|
2244
|
+
var r = battle.rng.range(u3, 0, n) + 1;
|
|
2245
|
+
var i: usize = 0;
|
|
2246
|
+
while (i < moves.len and r > 0) : (i += 1) {
|
|
2247
|
+
if (i + 1 != mslot and Move.get(moves[i].id).extra.sleep_talk) {
|
|
2248
|
+
r -= 1;
|
|
2249
|
+
if (r == 0) break :slot @intCast(i + 1);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
break :slot @intCast(i + 1);
|
|
2253
|
+
} else {
|
|
2254
|
+
while (true) {
|
|
2255
|
+
const r: u3 = @intCast(battle.rng.next() & 3);
|
|
2256
|
+
if (r + 1 == mslot or !Move.get(moves[r].id).extra.sleep_talk) continue;
|
|
2257
|
+
break :slot @intCast(r + 1);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2261
|
+
|
|
2262
|
+
assert(n >= 1 and n <= 4);
|
|
2263
|
+
assert(slot >= 1 and slot <= 4);
|
|
2264
|
+
try options.chance.moveSlot(player, slot, n);
|
|
2265
|
+
return slot;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
inline fn forceSwitch(
|
|
2269
|
+
battle: anytype,
|
|
2270
|
+
player: Player,
|
|
2271
|
+
slots: []u3,
|
|
2272
|
+
n: u3,
|
|
2273
|
+
options: anytype,
|
|
2274
|
+
) !u3 {
|
|
2275
|
+
const foe = battle.foe(player);
|
|
2276
|
+
|
|
2277
|
+
assert(slots.len < 5);
|
|
2278
|
+
assert(n > 0 and n <= 6);
|
|
2279
|
+
|
|
2280
|
+
// TODO: consider throwing error instead of rerolling?
|
|
2281
|
+
const overridden = if (options.calc.overridden(player, .force_switch)) |val| val: {
|
|
2282
|
+
for (slots) |slot| if (slot == val) break :val if (foe.get(slot).hp > 0) val else null;
|
|
2283
|
+
break :val null;
|
|
2284
|
+
} else null;
|
|
2285
|
+
|
|
2286
|
+
const slot: u3 = overridden orelse slot: {
|
|
2287
|
+
if (showdown) {
|
|
2288
|
+
break :slot slots[battle.rng.range(u3, 0, @as(u3, @intCast(slots.len)))];
|
|
2289
|
+
} else {
|
|
2290
|
+
while (true) {
|
|
2291
|
+
const r = battle.rng.next() & 7;
|
|
2292
|
+
if (r > n or r == 1) continue;
|
|
2293
|
+
if (foe.get(r).hp > 0) break :slot @intCast(r);
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
};
|
|
2297
|
+
|
|
2298
|
+
assert(n >= 1 and n <= 6);
|
|
2299
|
+
assert(slot >= 1 and slot <= 6);
|
|
2300
|
+
try options.chance.forceSwitch(player, slot, n);
|
|
2301
|
+
return slot;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
const DISTRIBUTION = [_]u3{ 2, 2, 2, 3, 3, 3, 4, 5 };
|
|
2305
|
+
|
|
2306
|
+
inline fn multiHit(battle: anytype, player: Player, options: anytype) !u3 {
|
|
2307
|
+
const n: u3 = if (options.calc.overridden(player, .multi_hit)) |val|
|
|
2308
|
+
@intCast(val)
|
|
2309
|
+
else if (showdown)
|
|
2310
|
+
DISTRIBUTION[battle.rng.range(u3, 0, DISTRIBUTION.len)]
|
|
2311
|
+
else n: {
|
|
2312
|
+
const r = (battle.rng.next() & 3);
|
|
2313
|
+
break :n @intCast((if (r < 2) r else battle.rng.next() & 3) + 2);
|
|
2314
|
+
};
|
|
2315
|
+
|
|
2316
|
+
assert(n >= 2 and n <= 5);
|
|
2317
|
+
try options.chance.multiHit(player, n);
|
|
2318
|
+
return n;
|
|
2319
|
+
}
|
|
1375
2320
|
|
|
1376
2321
|
inline fn psywave(battle: anytype, player: Player, max: u8, options: anytype) !u8 {
|
|
1377
2322
|
const power = if (options.calc.overridden(player, .psywave)) |val|
|
|
@@ -1390,18 +2335,135 @@ pub const Rolls = struct {
|
|
|
1390
2335
|
return power;
|
|
1391
2336
|
}
|
|
1392
2337
|
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
}
|
|
2338
|
+
inline fn metronome(battle: anytype, player: Player, options: anytype) !Move {
|
|
2339
|
+
const moves = battle.get(player).active.moves;
|
|
2340
|
+
|
|
2341
|
+
var n: u2 = 0;
|
|
2342
|
+
for (moves) |m| {
|
|
2343
|
+
if (m == .None) break;
|
|
2344
|
+
if (Move.get(m).metronome) n += 1;
|
|
1401
2345
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
2346
|
+
|
|
2347
|
+
// TODO: consider throwing error instead of rerolling?
|
|
2348
|
+
const overridden = if (options.calc.overridden(player, .metronome)) |val| val: {
|
|
2349
|
+
for (moves) |m| if (m.id == val) break :val null;
|
|
2350
|
+
break :val val;
|
|
2351
|
+
} else null;
|
|
2352
|
+
|
|
2353
|
+
const move: Move = overridden orelse move: {
|
|
2354
|
+
if (showdown) {
|
|
2355
|
+
var r = battle.rng.range(u8, 0, Move.METRONOME.len - n);
|
|
2356
|
+
for (moves) |m| {
|
|
2357
|
+
if (m == .None or @intFromEnum(m) > @intFromEnum(Move.METRONOME[r])) break;
|
|
2358
|
+
if (Move.get(m).metronome) r -= 1;
|
|
2359
|
+
}
|
|
2360
|
+
break :move Move.METRONOME[r];
|
|
2361
|
+
} else {
|
|
2362
|
+
outer: while (true) {
|
|
2363
|
+
const r = battle.rng.next();
|
|
2364
|
+
if (r == 0 or r >= Move.size) continue;
|
|
2365
|
+
const selected: Move = @enumFromInt(r);
|
|
2366
|
+
if (!Move.get(selected).extra.metronome) continue;
|
|
2367
|
+
for (moves) |m| if (m.id == selected) continue :outer;
|
|
2368
|
+
break :move selected;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
|
|
2373
|
+
assert(Move.get(move).metronome);
|
|
2374
|
+
try options.chance.metronome(player, move, n);
|
|
2375
|
+
return move;
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
inline fn sleepDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2379
|
+
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2380
|
+
@intCast(val)
|
|
2381
|
+
else if (showdown)
|
|
2382
|
+
battle.rng.range(u3, 1, 7)
|
|
2383
|
+
else duration: {
|
|
2384
|
+
while (true) {
|
|
2385
|
+
const r = battle.rng.next() & 7;
|
|
2386
|
+
if (r != 0 and r != 7) break :duration @intCast(r);
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
assert(duration >= 1 and duration <= 6);
|
|
2391
|
+
options.chance.duration(.sleep, player, player.foe(), duration);
|
|
2392
|
+
return duration + 1;
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
inline fn confusionDuration(battle: anytype, player: Player, self: bool, options: anytype) u3 {
|
|
2396
|
+
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2397
|
+
@intCast(val)
|
|
2398
|
+
else if (showdown)
|
|
2399
|
+
// Pokémon Showdown incorrectly uses the same duration for self-confusion
|
|
2400
|
+
battle.rng.range(u3, 2, 6)
|
|
2401
|
+
else
|
|
2402
|
+
@intCast((battle.rng.next() & if (self) 1 else 3) + 2);
|
|
2403
|
+
|
|
2404
|
+
assert(duration >= 2 and duration <= if (!showdown and self) 3 else 5);
|
|
2405
|
+
options.chance.duration(.confusion, player, if (self) player else player.foe(), duration);
|
|
2406
|
+
return duration;
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
inline fn disableDuration(battle: anytype, player: Player, options: anytype) u4 {
|
|
2410
|
+
const duration: u4 = if (options.calc.overridden(player, .duration)) |val|
|
|
2411
|
+
@intCast(val)
|
|
2412
|
+
else if (showdown)
|
|
2413
|
+
// Pokémon Showdown incorrectly inherits Generation III's Disable duration
|
|
2414
|
+
battle.rng.range(u4, 2, 6)
|
|
2415
|
+
else duration: {
|
|
2416
|
+
while (true) {
|
|
2417
|
+
const r = battle.rng.next() & 7;
|
|
2418
|
+
if (r != 0) break :duration @intCast(r + 1);
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
|
|
2422
|
+
assert(duration >= 2 and duration <= (if (showdown) 5 else 7));
|
|
2423
|
+
options.chance.duration(.disable, player, player.foe(), duration);
|
|
2424
|
+
return duration;
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
inline fn attackingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2428
|
+
const duration: u3 = if (options.calc.overridden(player, .duration)) |val|
|
|
2429
|
+
@intCast(val)
|
|
2430
|
+
else if (showdown)
|
|
2431
|
+
battle.rng.range(u3, 2, 4)
|
|
2432
|
+
else
|
|
2433
|
+
@intCast((battle.rng.next() & 1) + 2); // BUG: thrash is + 1 not +2
|
|
2434
|
+
|
|
2435
|
+
assert(duration >= 2 and duration <= 3);
|
|
2436
|
+
options.chance.duration(.attacking, player, player, duration);
|
|
2437
|
+
return duration;
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
inline fn bindingDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2441
|
+
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
2442
|
+
@intCast(val)
|
|
2443
|
+
else if (showdown)
|
|
2444
|
+
// TODO: PS does range(3, 6) here which after subtracting 1 is actually 2-4 not 2-5!
|
|
2445
|
+
battle.rng.range(u3, 2, 5)
|
|
2446
|
+
else
|
|
2447
|
+
@intCast(battle.rng.next() & 3 + 2));
|
|
2448
|
+
|
|
2449
|
+
assert(duration >= 2 and duration <= 5);
|
|
2450
|
+
options.chance.duration(.binding, player, player.foe(), duration);
|
|
2451
|
+
return duration + 1;
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
// TODO: consider sharing implementation with bindingDuration
|
|
2455
|
+
inline fn encoreDuration(battle: anytype, player: Player, options: anytype) u3 {
|
|
2456
|
+
const duration: u3 = (if (options.calc.overridden(player, .duration)) |val|
|
|
2457
|
+
@intCast(val)
|
|
2458
|
+
else if (showdown)
|
|
2459
|
+
battle.rng.range(u3, 2, 6)
|
|
2460
|
+
else
|
|
2461
|
+
@intCast(battle.rng.next() & 3 + 2));
|
|
2462
|
+
|
|
2463
|
+
assert(duration >= 2 and duration <= 5);
|
|
2464
|
+
options.chance.duration(.encore, player, player.foe(), duration);
|
|
2465
|
+
return duration + 1;
|
|
2466
|
+
}
|
|
1405
2467
|
};
|
|
1406
2468
|
|
|
1407
2469
|
test "RNG agreement" {
|