diff --git a/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs b/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs
index b9b663214..c75c6f5a8 100644
--- a/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs
+++ b/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs
@@ -20,16 +20,16 @@ public sealed class BallVerifier : Verifier
data.AddLine(Localize(result));
}
- private static byte IsReplacedBall(IVersion enc, PKM pk) => pk switch
+ private static Ball IsReplacedBall(IVersion enc, PKM pk) => pk switch
{
// Trading from PLA origin -> SW/SH will replace the Legends: Arceus ball with a regular Poké Ball
- PK8 when enc.Version == GameVersion.PLA => (int)Poke,
+ PK8 when enc.Version == GameVersion.PLA => Poke,
// No replacement done.
_ => NoBallReplace,
};
- private const int NoBallReplace = (int)None;
+ private const Ball NoBallReplace = None;
public static BallVerificationResult VerifyBall(LegalityAnalysis data)
{
@@ -37,73 +37,83 @@ public sealed class BallVerifier : Verifier
var enc = info.EncounterOriginal;
var pk = data.Entity;
- // Capture / Inherit cases -- can be one of many balls
- if (pk.Species == (int)Species.Shedinja && enc.Species != (int)Species.Shedinja) // Shedinja. For Gen3, copy the ball from Nincada
- {
- // Only a Gen3 origin Shedinja can copy the wild ball.
- // Evolution chains will indicate if it could have existed as Shedinja in Gen3.
- // The special move verifier has a similar check!
- if (enc is { Version: GameVersion.HG or GameVersion.SS, IsEgg: false } && pk is { Ball: (int)Sport }) // Can evolve in D/P to retain the HG/SS ball (separate byte) -- not able to be captured in any other ball
- return GetResult(true);
- if (enc.Generation != 3 || info.EvoChainsAllGens.Gen3.Length != 2) // not evolved in Gen3 Nincada->Shedinja
- return VerifyBallEquals(pk, (int)Poke); // Poké Ball Only
- }
+ Ball current = (Ball)pk.Ball;
+ if (enc.Species == (int)Species.Nincada && pk.Species == (int)Species.Shedinja)
+ return VerifyEvolvedShedinja(enc, current, pk, info);
- return VerifyBall(pk, enc);
+ return VerifyBall(enc, current, pk);
+ }
+
+ private static BallVerificationResult VerifyEvolvedShedinja(IEncounterable enc, Ball current, PKM pk, LegalInfo info)
+ {
+ // Nincada evolving into Shedinja normally reverts to Poké Ball.
+
+ // Gen3 Evolution: Copy current ball.
+ if (enc is EncounterSlot3 && info.EvoChainsAllGens.Gen3.Length == 2)
+ return VerifyBall(enc, current, pk);
+
+ // Gen4 D/P/Pt: Retain the HG/SS ball (stored in a separate byte) -- can only be caught in BCC with Sport Ball.
+ if (enc is EncounterSlot4 { Type: SlotType4.BugContest } && info.EvoChainsAllGens.Gen4.Length == 2)
+ return GetResult(current is Sport or Poke);
+
+ return VerifyBallEquals(current, Poke); // Poké Ball Only
}
///
/// Verifies the currently set ball for the .
///
+ /// Encounter template
+ /// Current ball
+ /// Misc details like Version and Ability
/// Call this directly instead of the overload if you've already ruled out the above cases needing Evolution chains.
- public static BallVerificationResult VerifyBall(PKM pk, IEncounterTemplate enc)
+ public static BallVerificationResult VerifyBall(IEncounterTemplate enc, Ball current, PKM pk)
{
+ // Capture / Inherit cases -- can be one of many balls
var ball = IsReplacedBall(enc, pk);
if (ball != NoBallReplace)
- return VerifyBallEquals(pk, ball);
+ return VerifyBallEquals(current, ball);
// Capturing with Heavy Ball is impossible in Sun/Moon for specific species.
- if (pk is { Ball: (int)Heavy, SM: true } && enc is not EncounterEgg && BallUseLegality.IsAlolanCaptureNoHeavyBall(enc.Species))
+ if (current is Heavy && enc is not EncounterEgg && pk is { SM: true } && BallUseLegality.IsAlolanCaptureNoHeavyBall(enc.Species))
return BadCaptureHeavy; // Heavy Ball, can inherit if from egg (US/UM fixed catch rate calc)
return enc switch
{
EncounterSlot8GO => GetResult(true), // Already a strict match
EncounterInvalid => GetResult(true), // ignore ball, pass whatever
- IFixedBall { FixedBall: not None } s => VerifyBallEquals(pk, (byte)s.FixedBall),
+ IFixedBall { FixedBall: not None } s => VerifyBallEquals(current, s.FixedBall),
- EncounterEgg => VerifyBallEgg(pk, enc), // Inheritance rules can vary.
- EncounterStatic5Entree => VerifyBallEquals((Ball)pk.Ball, BallUseLegality.DreamWorldBalls),
- _ => VerifyBallEquals((Ball)pk.Ball, BallUseLegality.GetWildBalls(enc.Generation, enc.Version)),
+ EncounterEgg => VerifyBallEgg(enc, current, pk), // Inheritance rules can vary.
+ EncounterStatic5Entree => VerifyBallEquals(current, BallUseLegality.DreamWorldBalls),
+ _ => VerifyBallEquals(current, BallUseLegality.GetWildBalls(enc.Generation, enc.Version)),
};
}
- private static BallVerificationResult VerifyBallEgg(PKM pk, IEncounterTemplate enc)
+ private static BallVerificationResult VerifyBallEgg(IEncounterTemplate enc, Ball ball, PKM pk)
{
if (enc.Generation < 6) // No inheriting Balls
- return VerifyBallEquals(pk, (int)Poke); // Must be Poké Ball -- no ball inheritance.
+ return VerifyBallEquals(ball, Poke); // Must be Poké Ball -- no ball inheritance.
- return pk.Ball switch
+ return ball switch
{
- (int)Master => BadInheritMaster,
- (int)Cherish => BadInheritCherish,
- _ => VerifyBallInherited(pk, enc),
+ Master => BadInheritMaster,
+ Cherish => BadInheritCherish,
+ _ => VerifyBallInherited(enc, ball, pk),
};
}
- private static BallVerificationResult VerifyBallInherited(PKM pk, IEncounterTemplate enc) => enc.Context switch
+ private static BallVerificationResult VerifyBallInherited(IEncounterTemplate enc, Ball ball, PKM pk) => enc.Context switch
{
- EntityContext.Gen6 => VerifyBallEggGen6(pk, enc), // Gen6 Inheritance Rules
- EntityContext.Gen7 => VerifyBallEggGen7(pk, enc), // Gen7 Inheritance Rules
- EntityContext.Gen8 => VerifyBallEggGen8(pk, enc),
- EntityContext.Gen8b => VerifyBallEggGen8BDSP(pk, enc),
- EntityContext.Gen9 => VerifyBallEggGen9(pk, enc),
+ EntityContext.Gen6 => VerifyBallEggGen6(enc, ball, pk), // Gen6 Inheritance Rules
+ EntityContext.Gen7 => VerifyBallEggGen7(enc, ball, pk), // Gen7 Inheritance Rules
+ EntityContext.Gen8 => VerifyBallEggGen8(enc, ball),
+ EntityContext.Gen8b => VerifyBallEggGen8BDSP(enc, ball),
+ EntityContext.Gen9 => VerifyBallEggGen9(enc, ball),
_ => BadEncounter,
};
- private static BallVerificationResult VerifyBallEggGen6(PKM pk, IEncounterTemplate enc)
+ private static BallVerificationResult VerifyBallEggGen6(IEncounterTemplate enc, Ball ball, PKM pk)
{
- var ball = (Ball)pk.Ball;
if (ball > Dream)
return BadOutOfRange;
@@ -111,9 +121,8 @@ public sealed class BallVerifier : Verifier
return GetResult(result);
}
- private static BallVerificationResult VerifyBallEggGen7(PKM pk, IEncounterTemplate enc)
+ private static BallVerificationResult VerifyBallEggGen7(IEncounterTemplate enc, Ball ball, PKM pk)
{
- var ball = (Ball)pk.Ball;
if (ball > Beast)
return BadOutOfRange;
@@ -121,9 +130,8 @@ public sealed class BallVerifier : Verifier
return GetResult(result);
}
- private static BallVerificationResult VerifyBallEggGen8BDSP(PKM pk, IEncounterTemplate enc)
+ private static BallVerificationResult VerifyBallEggGen8BDSP(IEncounterTemplate enc, Ball ball)
{
- var ball = (Ball)pk.Ball;
if (ball > Beast)
return BadOutOfRange;
@@ -135,9 +143,8 @@ public sealed class BallVerifier : Verifier
return GetResult(result);
}
- private static BallVerificationResult VerifyBallEggGen8(PKM pk, IEncounterTemplate enc)
+ private static BallVerificationResult VerifyBallEggGen8(IEncounterTemplate enc, Ball ball)
{
- var ball = (Ball)pk.Ball;
if (ball > Beast)
return BadOutOfRange;
@@ -145,9 +152,8 @@ public sealed class BallVerifier : Verifier
return GetResult(result);
}
- private static BallVerificationResult VerifyBallEggGen9(PKM pk, IEncounterTemplate enc)
+ private static BallVerificationResult VerifyBallEggGen9(IEncounterTemplate enc, Ball ball)
{
- var ball = (Ball)pk.Ball;
if (ball > Beast)
return BadOutOfRange;
@@ -160,7 +166,7 @@ public sealed class BallVerifier : Verifier
return GetResult(result);
}
- private static BallVerificationResult VerifyBallEquals(PKM pk, byte ball) => GetResult(ball == pk.Ball);
+ private static BallVerificationResult VerifyBallEquals(Ball ball, Ball permit) => GetResult(ball == permit);
private static BallVerificationResult VerifyBallEquals(Ball ball, ulong permit) => GetResult(BallUseLegality.IsBallPermitted(permit, (byte)ball));
private static BallVerificationResult GetResult(bool valid) => valid ? ValidEncounter : BadEncounter;