From f83a9bf8332c9e6ee2991bbe3e22fcc8404e4e56 Mon Sep 17 00:00:00 2001 From: Kurt Date: Wed, 5 Jan 2022 16:46:23 -0800 Subject: [PATCH] Expose shiny potential value Not really digging it currently as it doesn't cover multi-state like AlwaysStar-Or-Never, but that single edge case can be handled elsewhere --- .../Encounters/EncounterMisc/EncounterEgg.cs | 9 ++++- .../EncounterMisc/EncounterInvalid.cs | 5 +++ .../Encounters/EncounterSlot/EncounterSlot.cs | 12 ++---- .../EncounterSlot/EncounterSlot1.cs | 1 + .../EncounterSlot/EncounterSlot2.cs | 1 + .../EncounterSlot/EncounterSlot3.cs | 2 +- .../EncounterSlot/EncounterSlot4.cs | 3 +- .../EncounterSlot/EncounterSlot7.cs | 2 +- .../EncounterSlot/EncounterSlot8b.cs | 5 +-- .../EncounterSlot/GO/EncounterSlotGO.cs | 7 ++-- .../EncounterStatic/EncounterStatic.cs | 2 +- .../EncounterTrade/EncounterTrade.cs | 2 +- .../Legality/Encounters/IEncounterable.cs | 2 +- .../Legality/Structures/IResultantShiny.cs | 6 +++ PKHeX.Core/Legality/Tables/Locations.cs | 9 +++++ .../Verifiers/Ball/BallBreedLegality.cs | 4 +- .../Legality/Verifiers/Ball/BallVerifier.cs | 2 +- PKHeX.Core/MysteryGifts/MysteryGift.cs | 9 ++++- PKHeX.Core/MysteryGifts/PCD.cs | 1 + PKHeX.Core/MysteryGifts/PGF.cs | 7 ++++ PKHeX.Core/MysteryGifts/PGT.cs | 1 + PKHeX.Core/MysteryGifts/WB7.cs | 1 + PKHeX.Core/MysteryGifts/WB8.cs | 37 ++++++++++++------- PKHeX.Core/MysteryGifts/WC3.cs | 2 +- PKHeX.Core/MysteryGifts/WC6.cs | 31 +++++++++++++++- PKHeX.Core/MysteryGifts/WC7.cs | 30 ++++++++++++++- PKHeX.Core/MysteryGifts/WC8.cs | 37 ++++++++++++------- PKHeX.Core/MysteryGifts/WR7.cs | 1 + PKHeX.Core/PKM/Shared/IShinyPotential.cs | 9 +++++ 29 files changed, 182 insertions(+), 58 deletions(-) create mode 100644 PKHeX.Core/Legality/Structures/IResultantShiny.cs create mode 100644 PKHeX.Core/PKM/Shared/IShinyPotential.cs diff --git a/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterEgg.cs b/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterEgg.cs index 8914e53e0..874aa497a 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterEgg.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterEgg.cs @@ -14,6 +14,11 @@ public sealed record EncounterEgg(int Species, int Form, int Level, int Generati public int LevelMin => Level; public int LevelMax => Level; public bool IsShiny => false; + public int Location => 0; + public int EggLocation => Locations.GetDaycareLocation(Generation, Version); + public Ball FixedBall => BallBreedLegality.GetDefaultBall(Version, Species); + public Shiny Shiny => Shiny.Random; + public int Ability => 0; public bool CanHaveVoltTackle => Species is (int)Core.Species.Pichu && (Generation > 3 || Version is GameVersion.E); @@ -34,7 +39,9 @@ public PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, gen); pk.CurrentLevel = Level; pk.Version = (int)version; - pk.Ball = (int)BallBreedLegality.GetDefaultBall(Version, Species); + + var ball = FixedBall; + pk.Ball = ball is Ball.None ? (int)Ball.Poke : (int)ball; pk.OT_Friendship = pk.PersonalInfo.BaseFriendship; SetEncounterMoves(pk, version); diff --git a/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterInvalid.cs b/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterInvalid.cs index ab0c847cf..10502d352 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterInvalid.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterInvalid.cs @@ -17,9 +17,14 @@ public sealed record EncounterInvalid : IEncounterable public int Generation { get; } public GameVersion Version { get; } public bool IsShiny => false; + public Shiny Shiny => Shiny.Never; public string Name => "Invalid"; public string LongName => "Invalid"; + public int Location => 0; + public int EggLocation => 0; + public int Ability => 0; + public Ball FixedBall => Ball.None; private EncounterInvalid() { } diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot.cs index e12f9758d..f3dec9e3a 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot.cs @@ -7,7 +7,7 @@ namespace PKHeX.Core /// Wild Encounter Slot data /// /// Wild encounter slots are found as random encounters in-game. - public abstract record EncounterSlot(EncounterArea Area, int Species, int Form, int LevelMin, int LevelMax) : IEncounterable, ILocation, IEncounterMatch, IFixedAbilityNumber + public abstract record EncounterSlot(EncounterArea Area, int Species, int Form, int LevelMin, int LevelMax) : IEncounterable, IEncounterMatch { public abstract int Generation { get; } public bool EggEncounter => false; @@ -17,6 +17,8 @@ public abstract record EncounterSlot(EncounterArea Area, int Species, int Form, public GameVersion Version => Area.Version; public int Location => Area.Location; public int EggLocation => 0; + public virtual Ball FixedBall => Ball.None; + public virtual Shiny Shiny => Shiny.Random; public bool IsFixedLevel => LevelMin == LevelMax; public bool IsRandomLevel => LevelMin != LevelMax; @@ -68,12 +70,6 @@ public virtual string LongName } } - /// - /// Returns a required ball if the wild encounter can only be caught in certain scenarios. - /// - /// if unrestricted, otherwise, a specific ball value. - public virtual Ball GetRequiredBallValue() => Ball.None; - public PKM ConvertToPKM(ITrainerInfo sav) => ConvertToPKM(sav, EncounterCriteria.Unrestricted); public PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria) @@ -95,7 +91,7 @@ protected virtual void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria pk.Version = (int)version; pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation); - var ball = GetRequiredBallValue(); + var ball = FixedBall; pk.Ball = (int)(ball == Ball.None ? Ball.Poke : ball); pk.Language = lang; pk.Form = GetWildForm(pk, Form, sav); diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot1.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot1.cs index 18a5256e7..63cdd6c0a 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot1.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot1.cs @@ -8,6 +8,7 @@ public sealed record EncounterSlot1 : EncounterSlot, INumberedSlot { public override int Generation => 1; public int SlotNumber { get; } + public override Ball FixedBall => Ball.Poke; public EncounterSlot1(EncounterArea1 area, int species, int min, int max, int slot) : base(area, species, 0, min, max) { diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot2.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot2.cs index 362f40580..63dc9db35 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot2.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot2.cs @@ -13,6 +13,7 @@ public sealed record EncounterSlot2 : EncounterSlot, INumberedSlot { public override int Generation => 2; public int SlotNumber { get; } + public override Ball FixedBall => Ball.Poke; public EncounterSlot2(EncounterArea2 area, int species, int min, int max, int slot) : base(area, species, species == 201 ? FormRandom : 0, min, max) { diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot3.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot3.cs index ed476ad22..30b30a5e0 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot3.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot3.cs @@ -15,6 +15,7 @@ public record EncounterSlot3 : EncounterSlot, IMagnetStatic, INumberedSlot, ISlo public SlotType Type => Area.Type; public int SlotNumber { get; } + public override Ball FixedBall => Locations.IsSafariZoneLocation3(Location) ? Ball.Safari : Ball.None; public EncounterSlot3(EncounterArea3 area, int species, int form, int min, int max, int slot, int mpi, int mpc, int sti, int stc) : base(area, species, form, min, max) { @@ -36,6 +37,5 @@ public override EncounterMatchRating GetMatchRating(PKM pkm) private bool IsDeferredSafari3(bool IsSafariBall) => IsSafariBall != Locations.IsSafariZoneLocation3(Location); - public override Ball GetRequiredBallValue() => Locations.IsSafariZoneLocation3(Location) ? Ball.Safari : Ball.None; } } diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot4.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot4.cs index ee4684cea..492c32682 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot4.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot4.cs @@ -16,6 +16,7 @@ public sealed record EncounterSlot4 : EncounterSlot, IMagnetStatic, INumberedSlo public SlotType Type => Area.Type; public int SlotNumber { get; } + public override Ball FixedBall => GetRequiredBallValue(); public bool CanUseRadar => !GameVersion.HGSS.Contains(Version) && GroundTile.HasFlag(GroundTilePermission.Grass); public EncounterSlot4(EncounterArea4 area, int species, int form, int min, int max, int slot, int mpi, int mpc, int sti, int stc) : base(area, species, form, min, max) @@ -44,7 +45,7 @@ public override EncounterMatchRating GetMatchRating(PKM pkm) return base.GetMatchRating(pkm); } - public override Ball GetRequiredBallValue() + private Ball GetRequiredBallValue() { if (Type is SlotType.BugContest) return Ball.Sport; diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot7.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot7.cs index e037fab1f..7976e0762 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot7.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot7.cs @@ -31,6 +31,6 @@ protected override void SetPINGA(PKM pk, EncounterCriteria criteria) protected override HiddenAbilityPermission IsHiddenAbilitySlot() => IsSOS ? HiddenAbilityPermission.Possible : HiddenAbilityPermission.Never; - public override Ball GetRequiredBallValue() => Location == Locations.Pelago7 ? Ball.Poke : Ball.None; + public override Ball FixedBall => Location == Locations.Pelago7 ? Ball.Poke : Ball.None; } } diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot8b.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot8b.cs index c76d010e9..e5b43d45f 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot8b.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot8b.cs @@ -13,6 +13,7 @@ public sealed record EncounterSlot8b : EncounterSlot public bool IsUnderground => Area.Location is (>= 508 and <= 617); public bool IsMarsh => Area.Location is (>= 219 and <= 224); public readonly bool IsBCAT; + public override Ball FixedBall => IsMarsh ? Ball.Safari : Ball.None; public EncounterSlot8b(EncounterArea area, int species, int form, int min, int max, bool isBCAT = false) : base(area, species, form, min, max) { @@ -34,10 +35,6 @@ protected override void SetFormatSpecificData(PKM pk) if (GetBaseEggMove(out int move1)) pk.RelearnMove1 = move1; } - else if (IsMarsh) - { - pk.Ball = (int)Ball.Safari; - } pk.SetRandomEC(); } diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlotGO.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlotGO.cs index 95c173586..34309aed7 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlotGO.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlotGO.cs @@ -5,7 +5,7 @@ namespace PKHeX.Core /// /// Contains details about an encounter that can be found in . /// - public abstract record EncounterSlotGO : EncounterSlot, IPogoSlot, IFixedBall + public abstract record EncounterSlotGO : EncounterSlot, IPogoSlot { /// public int Start { get; } @@ -13,8 +13,7 @@ public abstract record EncounterSlotGO : EncounterSlot, IPogoSlot, IFixedBall /// public int End { get; } - /// - public Shiny Shiny { get; } + public override Shiny Shiny { get; } /// public PogoType Type { get; } @@ -24,7 +23,7 @@ public abstract record EncounterSlotGO : EncounterSlot, IPogoSlot, IFixedBall public override bool IsShiny => Shiny.IsShiny(); - public Ball FixedBall => Type.GetValidBall(); + public override Ball FixedBall => Type.GetValidBall(); protected EncounterSlotGO(EncounterArea area, int species, int form, int start, int end, Shiny shiny, Gender gender, PogoType type) : base(area, species, form, type.GetMinLevel(), EncountersGO.MAX_LEVEL) { diff --git a/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic.cs b/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic.cs index d077311df..542770238 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic.cs @@ -9,7 +9,7 @@ namespace PKHeX.Core /// /// Static Encounters are fixed position encounters with properties that are not subject to Wild Encounter conditions. /// - public abstract record EncounterStatic(GameVersion Version) : IEncounterable, IMoveset, ILocation, IEncounterMatch, IFixedBall, IFixedAbilityNumber + public abstract record EncounterStatic(GameVersion Version) : IEncounterable, IMoveset, IEncounterMatch { public int Species { get; init; } public int Form { get; init; } diff --git a/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs b/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs index 09d516743..52aad583a 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterTrade/EncounterTrade.cs @@ -10,7 +10,7 @@ namespace PKHeX.Core /// /// Trade data is fixed level in all cases except for the first few generations of games. /// - public abstract record EncounterTrade(GameVersion Version) : IEncounterable, IMoveset, ILocation, IEncounterMatch, IFixedBall, IFixedAbilityNumber + public abstract record EncounterTrade(GameVersion Version) : IEncounterable, IMoveset, IEncounterMatch { public int Species { get; init; } public int Form { get; init; } diff --git a/PKHeX.Core/Legality/Encounters/IEncounterable.cs b/PKHeX.Core/Legality/Encounters/IEncounterable.cs index 71418e547..ee86542a2 100644 --- a/PKHeX.Core/Legality/Encounters/IEncounterable.cs +++ b/PKHeX.Core/Legality/Encounters/IEncounterable.cs @@ -4,7 +4,7 @@ /// Common Encounter Properties base interface. /// /// - public interface IEncounterable : IEncounterInfo + public interface IEncounterable : IEncounterInfo, ILocation, IFixedAbilityNumber, IFixedBall, IShinyPotential { /// /// Short name to describe the encounter data, usually just indicating which of the main component encounter types the data is. diff --git a/PKHeX.Core/Legality/Structures/IResultantShiny.cs b/PKHeX.Core/Legality/Structures/IResultantShiny.cs new file mode 100644 index 000000000..5c4122a83 --- /dev/null +++ b/PKHeX.Core/Legality/Structures/IResultantShiny.cs @@ -0,0 +1,6 @@ +namespace PKHeX.Core; + +public interface IResultantShiny +{ + Shiny CanBeShiny { get; } +} diff --git a/PKHeX.Core/Legality/Tables/Locations.cs b/PKHeX.Core/Legality/Tables/Locations.cs index 33c19cefc..09e8ea678 100644 --- a/PKHeX.Core/Legality/Tables/Locations.cs +++ b/PKHeX.Core/Legality/Tables/Locations.cs @@ -153,5 +153,14 @@ public static bool IsEggLocationBred4(int loc, GameVersion ver) GameVersion.BD or GameVersion.SP => Default8bNone, _ => 0, }; + + public static int GetDaycareLocation(int generation, GameVersion version) => generation switch + { + 1 or 2 or 3 => 0, + 4 => Daycare4, + 5 => Daycare5, + 8 when version is GameVersion.BD or GameVersion.SP => Daycare8b, + _ => Daycare5, + }; } } diff --git a/PKHeX.Core/Legality/Verifiers/Ball/BallBreedLegality.cs b/PKHeX.Core/Legality/Verifiers/Ball/BallBreedLegality.cs index 4b4766848..77fb86f99 100644 --- a/PKHeX.Core/Legality/Verifiers/Ball/BallBreedLegality.cs +++ b/PKHeX.Core/Legality/Verifiers/Ball/BallBreedLegality.cs @@ -1047,13 +1047,13 @@ internal static class BallBreedLegality /// Not all things can hatch with a Poké Ball! public static Ball GetDefaultBall(GameVersion version, int species) { - if (GameVersion.BDSP.Contains(version)) + if (version is GameVersion.BD or GameVersion.SP) { if (BanInheritedExceptSafari_BDSP.Contains(species)) return Ball.Safari; } - return Ball.Poke; + return Ball.None; } } } diff --git a/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs b/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs index 16aa2d90d..94fd2393c 100644 --- a/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs @@ -82,7 +82,7 @@ private CheckResult VerifyBallStatic(LegalityAnalysis data, EncounterStatic s) private CheckResult VerifyBallWild(LegalityAnalysis data, EncounterSlot w) { - var req = w.GetRequiredBallValue(); + var req = w.FixedBall; if (req != None) return VerifyBallEquals(data, (int) req); diff --git a/PKHeX.Core/MysteryGifts/MysteryGift.cs b/PKHeX.Core/MysteryGifts/MysteryGift.cs index bb226c762..80a703adc 100644 --- a/PKHeX.Core/MysteryGifts/MysteryGift.cs +++ b/PKHeX.Core/MysteryGifts/MysteryGift.cs @@ -8,7 +8,7 @@ namespace PKHeX.Core /// /// Mystery Gift Template File /// - public abstract class MysteryGift : IEncounterable, IMoveset, IRelearn, ILocation, IFixedBall, IFixedAbilityNumber + public abstract class MysteryGift : IEncounterable, IMoveset, IRelearn { /// /// Determines whether or not the given length of bytes is valid for a mystery gift. @@ -133,6 +133,13 @@ public virtual GameVersion Version public virtual IReadOnlyList Relearn { get => Array.Empty(); set { } } public virtual int[] IVs { get => Array.Empty(); set { } } public virtual bool IsShiny => false; + + public virtual Shiny Shiny + { + get => Shiny.Never; + init => throw new InvalidOperationException(); + } + public virtual bool IsEgg { get => false; set { } } public virtual int HeldItem { get => -1; set { } } public virtual int AbilityType { get => -1; set { } } diff --git a/PKHeX.Core/MysteryGifts/PCD.cs b/PKHeX.Core/MysteryGifts/PCD.cs index 9ec37d8ab..64d4871b9 100644 --- a/PKHeX.Core/MysteryGifts/PCD.cs +++ b/PKHeX.Core/MysteryGifts/PCD.cs @@ -80,6 +80,7 @@ public override string CardTitle public override IReadOnlyList Moves { get => Gift.Moves; set => Gift.Moves = value; } public override int HeldItem { get => Gift.HeldItem; set => Gift.HeldItem = value; } public override bool IsShiny => Gift.IsShiny; + public override Shiny Shiny => Gift.Shiny; public override bool IsEgg { get => Gift.IsEgg; set => Gift.IsEgg = value; } public override int Gender { get => Gift.Gender; set => Gift.Gender = value; } public override int Form { get => Gift.Form; set => Gift.Form = value; } diff --git a/PKHeX.Core/MysteryGifts/PGF.cs b/PKHeX.Core/MysteryGifts/PGF.cs index 3fb113996..f191e4d28 100644 --- a/PKHeX.Core/MysteryGifts/PGF.cs +++ b/PKHeX.Core/MysteryGifts/PGF.cs @@ -329,6 +329,13 @@ private void SetPID(PKM pk, int av) pk.PID &= 0xFFFEFFFF; } + public override Shiny Shiny => PIDType switch + { + 1 => Shiny.Random, + 2 => Shiny.Always, + _ => Shiny.Never, + }; + private void SetIVs(PKM pk) { Span finalIVs = stackalloc int[6]; diff --git a/PKHeX.Core/MysteryGifts/PGT.cs b/PKHeX.Core/MysteryGifts/PGT.cs index 888afbe00..08f17a200 100644 --- a/PKHeX.Core/MysteryGifts/PGT.cs +++ b/PKHeX.Core/MysteryGifts/PGT.cs @@ -47,6 +47,7 @@ private enum GiftType public override string CardTitle { get => "Raw Gift (PGT)"; set { } } public override int CardID { get => -1; set { } } public override bool GiftUsed { get => false; set { } } + public override Shiny Shiny => IsEgg ? Shiny.Random : PK.PID == 1 ? Shiny.Never : IsShiny ? Shiny.Always : Shiny.Never; public PGT() : this(new byte[Size]) { } public PGT(byte[] data) : base(data) { } diff --git a/PKHeX.Core/MysteryGifts/WB7.cs b/PKHeX.Core/MysteryGifts/WB7.cs index 6f0a35315..1c83faa37 100644 --- a/PKHeX.Core/MysteryGifts/WB7.cs +++ b/PKHeX.Core/MysteryGifts/WB7.cs @@ -134,6 +134,7 @@ public override int Quantity // Pokémon Properties public override bool IsPokémon { get => CardType == 0; set { if (value) CardType = 0; } } public override bool IsShiny => PIDType == Shiny.Always; + public override Shiny Shiny => PIDType; public override int TID { diff --git a/PKHeX.Core/MysteryGifts/WB8.cs b/PKHeX.Core/MysteryGifts/WB8.cs index f1948eaf1..526684779 100644 --- a/PKHeX.Core/MysteryGifts/WB8.cs +++ b/PKHeX.Core/MysteryGifts/WB8.cs @@ -85,27 +85,36 @@ public override int Quantity // Pokémon Properties public override bool IsPokémon { get => CardType == GiftType.Pokemon; set { if (value) CardType = GiftType.Pokemon; } } - public override bool IsShiny + public override bool IsShiny => Shiny.IsShiny(); + + public override Shiny Shiny { get { var type = PIDType; - if (type is Shiny.AlwaysStar or Shiny.AlwaysSquare) - return true; - if (type != Shiny.FixedValue) - return false; - - // Player owned anti-shiny fixed PID - if (TID == 0 && SID == 0) - return false; - - var pid = PID; - var psv = (int)((pid >> 16 ^ (pid & 0xFFFF)) >> 4); - var tsv = (TID ^ SID) >> 4; - return (psv ^ tsv) == 0; + if (type is not Shiny.FixedValue) + return type; + return GetShinyXor() switch + { + 0 => Shiny.AlwaysSquare, + <= 15 => Shiny.AlwaysStar, + _ => Shiny.Never, + }; } } + private int GetShinyXor() + { + // Player owned anti-shiny fixed PID + if (TID == 0 && SID == 0) + return int.MaxValue; + + var pid = PID; + var psv = (int)(pid >> 16 ^ (pid & 0xFFFF)); + var tsv = (TID ^ SID); + return psv ^ tsv; + } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x20)); diff --git a/PKHeX.Core/MysteryGifts/WC3.cs b/PKHeX.Core/MysteryGifts/WC3.cs index e181cfeae..9411d8dd0 100644 --- a/PKHeX.Core/MysteryGifts/WC3.cs +++ b/PKHeX.Core/MysteryGifts/WC3.cs @@ -32,7 +32,7 @@ public sealed class WC3 : MysteryGift, IRibbonSetEvent3, ILangNicknamedTemplate public override bool IsEgg { get; set; } public override IReadOnlyList Moves { get; set; } = Array.Empty(); public bool NotDistributed { get; init; } - public Shiny Shiny { get; init; } = Shiny.Random; + public override Shiny Shiny { get; init; } = Shiny.Random; public bool Fateful { get; init; } // Obedience Flag // Mystery Gift Properties diff --git a/PKHeX.Core/MysteryGifts/WC6.cs b/PKHeX.Core/MysteryGifts/WC6.cs index de27f714e..b1da01b34 100644 --- a/PKHeX.Core/MysteryGifts/WC6.cs +++ b/PKHeX.Core/MysteryGifts/WC6.cs @@ -122,7 +122,36 @@ private uint Day // Pokémon Properties public override bool IsPokémon { get => CardType == 0; set { if (value) CardType = 0; } } - public override bool IsShiny => PIDType == Shiny.Always; + public override bool IsShiny => Shiny.IsShiny(); + + public override Shiny Shiny + { + get + { + var type = PIDType; + if (type is not Shiny.FixedValue) + return type; + return GetShinyXor() switch + { + 0 => Shiny.AlwaysSquare, + <= 15 => Shiny.AlwaysStar, + _ => Shiny.Never, + }; + } + } + + private int GetShinyXor() + { + // Player owned anti-shiny fixed PID + if (TID == 0 && SID == 0) + return int.MaxValue; + + var pid = PID; + var psv = (int)(pid >> 16 ^ (pid & 0xFFFF)); + var tsv = (TID ^ SID); + return psv ^ tsv; + } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x68)); set => WriteUInt16LittleEndian(Data.AsSpan(0x68), (ushort)value); } public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); } public int OriginGame { get => Data[0x6C]; set => Data[0x6C] = (byte)value; } diff --git a/PKHeX.Core/MysteryGifts/WC7.cs b/PKHeX.Core/MysteryGifts/WC7.cs index a40b0cdcb..b6ccecaaa 100644 --- a/PKHeX.Core/MysteryGifts/WC7.cs +++ b/PKHeX.Core/MysteryGifts/WC7.cs @@ -137,7 +137,35 @@ public override int Quantity // Pokémon Properties public override bool IsPokémon { get => CardType == 0; set { if (value) CardType = 0; } } - public override bool IsShiny => PIDType == Shiny.Always; + public override bool IsShiny => Shiny.IsShiny(); + + public override Shiny Shiny + { + get + { + var type = PIDType; + if (type is not Shiny.FixedValue) + return type; + return GetShinyXor() switch + { + 0 => Shiny.AlwaysSquare, + <= 15 => Shiny.AlwaysStar, + _ => Shiny.Never, + }; + } + } + + private int GetShinyXor() + { + // Player owned anti-shiny fixed PID + if (TID == 0 && SID == 0) + return int.MaxValue; + + var pid = PID; + var psv = (int)(pid >> 16 ^ (pid & 0xFFFF)); + var tsv = (TID ^ SID); + return psv ^ tsv; + } public override int TID { diff --git a/PKHeX.Core/MysteryGifts/WC8.cs b/PKHeX.Core/MysteryGifts/WC8.cs index 57fd57d34..42d20b11b 100644 --- a/PKHeX.Core/MysteryGifts/WC8.cs +++ b/PKHeX.Core/MysteryGifts/WC8.cs @@ -79,27 +79,36 @@ public override int Quantity // Pokémon Properties public override bool IsPokémon { get => CardType == GiftType.Pokemon; set { if (value) CardType = GiftType.Pokemon; } } - public override bool IsShiny + public override bool IsShiny => Shiny.IsShiny(); + + public override Shiny Shiny { get { var type = PIDType; - if (type is Shiny.AlwaysStar or Shiny.AlwaysSquare) - return true; - if (type != Shiny.FixedValue) - return false; - - // Player owned anti-shiny fixed PID - if (TID == 0 && SID == 0) - return false; - - var pid = PID; - var psv = (int)((pid >> 16 ^ (pid & 0xFFFF)) >> 4); - var tsv = (TID ^ SID) >> 4; - return (psv ^ tsv) == 0; + if (type is not Shiny.FixedValue) + return type; + return GetShinyXor() switch + { + 0 => Shiny.AlwaysSquare, + <= 15 => Shiny.AlwaysStar, + _ => Shiny.Never, + }; } } + private int GetShinyXor() + { + // Player owned anti-shiny fixed PID + if (TID == 0 && SID == 0) + return int.MaxValue; + + var pid = PID; + var psv = (int)(pid >> 16 ^ (pid & 0xFFFF)); + var tsv = (TID ^ SID); + return psv ^ tsv; + } + public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(CardStart + 0x20)); diff --git a/PKHeX.Core/MysteryGifts/WR7.cs b/PKHeX.Core/MysteryGifts/WR7.cs index 542993cb6..740624f48 100644 --- a/PKHeX.Core/MysteryGifts/WR7.cs +++ b/PKHeX.Core/MysteryGifts/WR7.cs @@ -104,6 +104,7 @@ public LanguageID LanguageReceived public override bool IsMatchExact(PKM pkm, DexLevel evo) => false; protected override bool IsMatchDeferred(PKM pkm) => false; protected override bool IsMatchPartial(PKM pkm) => false; + public override Shiny Shiny => Shiny.Never; public override int Location { get; set; } public override int EggLocation { get; set; } diff --git a/PKHeX.Core/PKM/Shared/IShinyPotential.cs b/PKHeX.Core/PKM/Shared/IShinyPotential.cs new file mode 100644 index 000000000..bb771fd57 --- /dev/null +++ b/PKHeX.Core/PKM/Shared/IShinyPotential.cs @@ -0,0 +1,9 @@ +namespace PKHeX.Core; + +/// +/// Interface that exposes a shiny potential state indicating what kinds of can be expressed. +/// +public interface IShinyPotential +{ + Shiny Shiny { get; } +}