From 26cabd022b09c2ed75773afdafcd3387a5416d52 Mon Sep 17 00:00:00 2001 From: Kurt Date: Sun, 9 Jul 2023 14:10:40 -0700 Subject: [PATCH] Update 23.07.09 --- Directory.Build.props | 2 +- PKHeX.Core/Editing/Showdown/ShowdownSet.cs | 3 +- .../Encounters/Data/Gen3/Encounters3FRLG.cs | 2 +- .../Encounters/Data/Gen45/Encounters5B2W2.cs | 2 +- .../Encounters/EncounterMisc/EncounterEgg.cs | 1 - .../EncounterSlot/EncounterSlot2.cs | 51 ++++---- .../EncounterSlot/GO/EncounterSlot7GO.cs | 2 +- .../EncounterSlot/GO/EncounterSlot8GO.cs | 2 +- .../Evolutions/Reversal/EvolutionNode.cs | 2 +- .../Evolutions/Reversal/EvolutionReversal.cs | 2 +- .../Reversal/EvolutionReverseLookup.cs | 4 +- .../Legality/Verifiers/Ball/BallVerifier.cs | 6 +- .../Legality/Verifiers/HistoryVerifier.cs | 2 - .../Verifiers/Ribbons/RibbonStrings.cs | 2 +- PKHeX.Core/PKM/EntityCharacteristic.cs | 76 ------------ PKHeX.Core/PKM/HOME/GameDataPA8.cs | 2 +- PKHeX.Core/PKM/HOME/IGameDataSplitAbility.cs | 10 ++ PKHeX.Core/PKM/HOME/IHomeStorage.cs | 18 +++ PKHeX.Core/PKM/HOME/IPokerusStatus.cs | 6 + .../{Shared => Interfaces}/IObedienceLevel.cs | 2 +- PKHeX.Core/PKM/PA8.cs | 2 +- PKHeX.Core/PKM/Util/EntityCharacteristic.cs | 114 ++++++++++++++++++ PKHeX.Core/Resources/legality/mgdb/wc8.pkl | Bin 677520 -> 683280 bytes PKHeX.Core/Resources/legality/mgdb/wc9.pkl | Bin 59096 -> 63368 bytes .../Saves/Substructures/Gen12/SAV2Offsets.cs | 9 +- .../Inventory/Pouch/InventoryPouch.cs | 2 +- PKHeX.Core/Util/Util.cs | 19 ++- PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs | 8 +- PKHeX.WinForms/Resources/text/changelog.txt | 26 +++- 29 files changed, 242 insertions(+), 135 deletions(-) delete mode 100644 PKHeX.Core/PKM/EntityCharacteristic.cs rename PKHeX.Core/PKM/{Shared => Interfaces}/IObedienceLevel.cs (90%) create mode 100644 PKHeX.Core/PKM/Util/EntityCharacteristic.cs diff --git a/Directory.Build.props b/Directory.Build.props index fd4ca8a9e..4338f8e40 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 23.06.03 + 23.07.09 11 enable en diff --git a/PKHeX.Core/Editing/Showdown/ShowdownSet.cs b/PKHeX.Core/Editing/Showdown/ShowdownSet.cs index 74a8b469c..6bf2d1e48 100644 --- a/PKHeX.Core/Editing/Showdown/ShowdownSet.cs +++ b/PKHeX.Core/Editing/Showdown/ShowdownSet.cs @@ -298,7 +298,8 @@ public sealed class ShowdownSet : IBattleTemplate var val = Util.ToInt32(value); if ((uint)val > 10) return false; - return (DynamaxLevel = (byte)val) is (>= 0 and <= 10); + DynamaxLevel = (byte)val; + return true; } private bool ParseTeraType(ReadOnlySpan value) diff --git a/PKHeX.Core/Legality/Encounters/Data/Gen3/Encounters3FRLG.cs b/PKHeX.Core/Legality/Encounters/Data/Gen3/Encounters3FRLG.cs index 2b8970110..f366c501f 100644 --- a/PKHeX.Core/Legality/Encounters/Data/Gen3/Encounters3FRLG.cs +++ b/PKHeX.Core/Legality/Encounters/Data/Gen3/Encounters3FRLG.cs @@ -42,7 +42,7 @@ internal static class Encounters3FRLG new(129, 05, FRLG) { Gift = true, Location = 099 }, // Magikarp @ Route 4 new(131, 25, FRLG) { Gift = true, Location = 134 }, // Lapras @ Silph Co. new(133, 25, FRLG) { Gift = true, Location = 094 }, // Eevee @ Celadon City - new(175, 05, FRLG) { Gift = true, EggLocation = 253 }, // Togepi Egg + new(175, 05, FRLG) { Gift = true, EggLocation = 253, Moves = new(045,204,118) }, // Togepi Egg // Celadon City Game Corner new(063, 09, FR) { Gift = true, Location = 94 }, // Abra diff --git a/PKHeX.Core/Legality/Encounters/Data/Gen45/Encounters5B2W2.cs b/PKHeX.Core/Legality/Encounters/Data/Gen45/Encounters5B2W2.cs index d92ea26d2..5485f2f84 100644 --- a/PKHeX.Core/Legality/Encounters/Data/Gen45/Encounters5B2W2.cs +++ b/PKHeX.Core/Legality/Encounters/Data/Gen45/Encounters5B2W2.cs @@ -171,7 +171,7 @@ public static class Encounters5B2W2 private static readonly EncounterStatic5[] Encounter_B2W2 = ArrayUtil.ConcatAll(Encounter_B2W2_Regular, Encounter_B2W2_N, Encounter_DreamRadar); #endregion #region Trade Tables - internal static readonly EncounterTrade5[] TradeGift_B2W2_Regular = + private static readonly EncounterTrade5[] TradeGift_B2W2_Regular = { new(B2 ) { Species = 548, Level = 20, Ability = OnlySecond, TID16 = 65217, SID16 = 00000, OTGender = 1, Gender = 1, IVs = new(20,20,20,20,31,20), Nature = Nature.Timid }, // Petilil new( W2) { Species = 546, Level = 20, Ability = OnlyFirst, TID16 = 05720, SID16 = 00001, OTGender = 0, Gender = 0, IVs = new(20,20,20,20,31,20), Nature = Nature.Modest }, // Cottonee diff --git a/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterEgg.cs b/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterEgg.cs index 7931bd92b..3a312ca59 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterEgg.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterMisc/EncounterEgg.cs @@ -21,7 +21,6 @@ public sealed record EncounterEgg(ushort Species, byte Form, byte Level, int Gen public AbilityPermission Ability => AbilityPermission.Any12H; public bool CanHaveVoltTackle => Species is (int)Core.Species.Pichu && (Generation > 3 || Version is GameVersion.E); - public bool CanInheritMoves => Breeding.GetCanInheritMoves(Species); public PKM ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr, EncounterCriteria.Unrestricted); diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot2.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot2.cs index 3d44add85..79cae773f 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot2.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot2.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System; namespace PKHeX.Core; @@ -44,33 +44,40 @@ public sealed record EncounterSlot2 : EncounterSlot, INumberedSlot pk2.Met_TimeOfDay = ((EncounterArea2)Area).Time.RandomValidTime(); } - private static readonly Dictionary Trees = new() + private static ReadOnlySpan TreeIndexes => new byte[] { - { 02, 0x3FF_3FF }, // Route 29 - { 04, 0x0FF_3FF }, // Route 30 - { 05, 0x3FE_3FF }, // Route 31 - { 08, 0x3EE_3FF }, // Route 32 - { 11, 0x240_3FF }, // Route 33 - { 12, 0x37F_3FF }, // Azalea Town - { 14, 0x3FF_3FF }, // Ilex Forest - { 15, 0x001_3FE }, // Route 34 - { 18, 0x261_3FF }, // Route 35 - { 20, 0x3FF_3FF }, // Route 36 - { 21, 0x2B9_3FF }, // Route 37 - { 25, 0x3FF_3FF }, // Route 38 - { 26, 0x184_3FF }, // Route 39 - { 34, 0x3FF_3FF }, // Route 42 - { 37, 0x3FF_3FF }, // Route 43 - { 38, 0x3FF_3FF }, // Lake of Rage - { 39, 0x2FF_3FF }, // Route 44 - { 91, 0x200_1FF }, // Route 26 - { 92, 0x2BB_3FF }, // Route 27 + 02, 04, 05, 08, 11, 12, 14, 15, 18, 20, 21, 25, 26, 34, 37, 38, 39, 91, 92, + }; + + private static ReadOnlySpan Trees => new[] + { + 0x3FF_3FF, // Route 29 + 0x0FF_3FF, // Route 30 + 0x3FE_3FF, // Route 31 + 0x3EE_3FF, // Route 32 + 0x240_3FF, // Route 33 + 0x37F_3FF, // Azalea Town + 0x3FF_3FF, // Ilex Forest + 0x001_3FE, // Route 34 + 0x261_3FF, // Route 35 + 0x3FF_3FF, // Route 36 + 0x2B9_3FF, // Route 37 + 0x3FF_3FF, // Route 38 + 0x184_3FF, // Route 39 + 0x3FF_3FF, // Route 42 + 0x3FF_3FF, // Route 43 + 0x3FF_3FF, // Lake of Rage + 0x2FF_3FF, // Route 44 + 0x200_1FF, // Route 26 + 0x2BB_3FF, // Route 27 }; public bool IsTreeAvailable(ushort trainerID) { - if (!Trees.TryGetValue(Location, out var permissions)) + var treeIndex = TreeIndexes.BinarySearch((byte)Location); + if (treeIndex < 0) return false; + var permissions = Trees[treeIndex]; var pivot = trainerID % 10; var type = Area.Type; diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlot7GO.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlot7GO.cs index df375f1b2..86878e07c 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlot7GO.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlot7GO.cs @@ -50,7 +50,7 @@ public sealed record EncounterSlot7GO : EncounterSlotGO protected override void SetEncounterMoves(PKM pk, GameVersion version, int level) { Span moves = stackalloc ushort[4]; - var source = GameData.GetLearnSource(GameVersion.GG); + ILearnSource source = LearnSource7GG.Instance; source.SetEncounterMoves(Species, Form, level, moves); pk.SetMoves(moves); pk.SetMaximumPPCurrent(moves); diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlot8GO.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlot8GO.cs index cf771b52b..6b58e616e 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlot8GO.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/GO/EncounterSlot8GO.cs @@ -103,7 +103,7 @@ public sealed record EncounterSlot8GO : EncounterSlotGO, IFixedOTFriendship, IEn s.HeightScalar = PokeSizeUtil.GetRandomScalar(); s.WeightScalar = PokeSizeUtil.GetRandomScalar(); if (pk is IScaledSize3 s3) - s3.Scale = s.HeightScalar = PokeSizeUtil.GetRandomScalar(); + s3.Scale = s.HeightScalar; } if (pk is PA8 pa8) diff --git a/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionNode.cs b/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionNode.cs index f478fd3b1..89ad71924 100644 --- a/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionNode.cs +++ b/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionNode.cs @@ -18,7 +18,7 @@ public struct EvolutionNode /// /// Link to register /// is thrown if the node is full. - public void Add(EvolutionLink link) + public void Add(in EvolutionLink link) { if (First.IsEmpty) First = link; diff --git a/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionReversal.cs b/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionReversal.cs index 7f6b23beb..a2f1cfdd4 100644 --- a/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionReversal.cs +++ b/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionReversal.cs @@ -84,7 +84,7 @@ public static class EvolutionReversal return false; } - private static EvoCriteria Create(EvolutionLink link, byte currentMaxLevel) => new() + private static EvoCriteria Create(in EvolutionLink link, byte currentMaxLevel) => new() { Species = link.Species, Form = link.Form, diff --git a/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionReverseLookup.cs b/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionReverseLookup.cs index 23394237c..c622ad737 100644 --- a/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionReverseLookup.cs +++ b/PKHeX.Core/Legality/Evolutions/Reversal/EvolutionReverseLookup.cs @@ -18,13 +18,13 @@ public sealed class EvolutionReverseLookup : IEvolutionLookup MaxSpecies = maxSpecies; } - private void Register(EvolutionLink link, int index) + private void Register(in EvolutionLink link, int index) { ref var node = ref Nodes[index]; node.Add(link); } - public void Register(EvolutionLink link, ushort species, byte form) + public void Register(in EvolutionLink link, ushort species, byte form) { if (form == 0) { diff --git a/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs b/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs index 7ac418e5e..65bb74434 100644 --- a/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/Ball/BallVerifier.cs @@ -459,10 +459,14 @@ public sealed class BallVerifier : Verifier if (species is >= (int)Species.Sprigatito and <= (int)Species.Quaquaval) // G9 Starters return VerifyBallEquals(data, (int)Poke); - // PLA Voltorb: Only via PLA (transfer only, not wild) + // PLA Voltorb: Only via PLA (transfer only, not wild) and GO if (enc is { Species: (ushort)Species.Voltorb, Form: 1 }) return VerifyBallEquals(data, BallUseLegality.WildPokeballs8g); + // S/V Tauros forms > 1: Only local Wild Balls for Blaze/Aqua breeds -- can't inherit balls from Kantonian/Combat. + if (enc is { Species: (ushort)Species.Tauros, Form: > 1 }) + return VerifyBallEquals(data, BallUseLegality.WildPokeballs9); + var pk = data.Entity; if (IsPaldeaCatchAndBreed(species) && IsBallPermitted(BallUseLegality.WildPokeballs9, pk.Ball)) return GetValid(LBallSpeciesPass); diff --git a/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs b/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs index 2f113c58c..094f3413d 100644 --- a/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/HistoryVerifier.cs @@ -81,8 +81,6 @@ public sealed class HistoryVerifier : Verifier data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid)); if ((Info.Generation != pk.Format || neverOT) && pk.CurrentHandler != 1) data.AddLine(GetInvalid(LTransferHTFlagRequired)); - if (pk is IHomeTrack { HasTracker: true } && pk.IsUntraded) // HOME sets HT name on moving in and out. - data.AddLine(GetInvalid(LTransferHTMismatchName)); } private static bool IsUntradeableEncounter(IEncounterTemplate enc) => enc switch diff --git a/PKHeX.Core/Legality/Verifiers/Ribbons/RibbonStrings.cs b/PKHeX.Core/Legality/Verifiers/Ribbons/RibbonStrings.cs index 8eb1b543f..3f717e71d 100644 --- a/PKHeX.Core/Legality/Verifiers/Ribbons/RibbonStrings.cs +++ b/PKHeX.Core/Legality/Verifiers/Ribbons/RibbonStrings.cs @@ -25,8 +25,8 @@ public static class RibbonStrings var index = line.IndexOf('\t'); if (index < 0) continue; - var name = line[..index]; var text = line[(index + 1)..]; + var name = line[..index]; RibbonNames[name] = text; } } diff --git a/PKHeX.Core/PKM/EntityCharacteristic.cs b/PKHeX.Core/PKM/EntityCharacteristic.cs deleted file mode 100644 index f5fc992a8..000000000 --- a/PKHeX.Core/PKM/EntityCharacteristic.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; - -namespace PKHeX.Core; - -public static class EntityCharacteristic -{ - public static int GetCharacteristic(uint ec, uint iv32) - { - int index = (int)(ec % 6); - - int maxStatIndex = index; - var maxStatValue = 0u; - do - { - var value = iv32 >> (index * 5) & 0x1F; - if (value > maxStatValue) - { - maxStatIndex = index; - maxStatValue = value; - } - if (index != 5) - index++; - else - index = 0; - } while (maxStatIndex != index); - - return (maxStatIndex * 5) + ((int)maxStatValue % 5); - } - - public static int GetCharacteristic(uint ec, Span ivs) - { - int index = (int)(ec % 6); - - int maxStatIndex = index; - var maxStatValue = 0; - do - { - var value = ivs[index]; - if (value > maxStatValue) - { - maxStatIndex = index; - maxStatValue = value; - } - if (index != 5) - index++; - else - index = 0; - } while (maxStatIndex != index); - - return (maxStatIndex * 5) + (maxStatValue % 5); - } - - public static int GetCharacteristicInvertFields(uint ec, uint iv32) - { - int index = (int)(ec % 6); - - int maxStatIndex = index; - var maxStatValue = 0u; - do - { - // IVs are stored in reverse order, get the bits from the end of the IV value - var value = iv32 >> (27 - (index * 5)) & 0x1F; - if (value > maxStatValue) - { - maxStatIndex = index; - maxStatValue = value; - } - if (index != 5) - index++; - else - index = 0; - } while (maxStatIndex != index); - - return (maxStatIndex * 5) + ((int)maxStatValue % 5); - } -} diff --git a/PKHeX.Core/PKM/HOME/GameDataPA8.cs b/PKHeX.Core/PKM/HOME/GameDataPA8.cs index 60c5fc55b..741fa52d3 100644 --- a/PKHeX.Core/PKM/HOME/GameDataPA8.cs +++ b/PKHeX.Core/PKM/HOME/GameDataPA8.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core; /// /// Side game data for data transferred into HOME. /// -public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsolute, IScaledSize3, IGameDataSplitAbility +public sealed class GameDataPA8 : HomeOptional1, IGameDataSide, IScaledSizeAbsolute, IScaledSize3, IGameDataSplitAbility, IPokerusStatus { private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PA8; private const int SIZE = HomeCrypto.SIZE_2GAME_PA8; diff --git a/PKHeX.Core/PKM/HOME/IGameDataSplitAbility.cs b/PKHeX.Core/PKM/HOME/IGameDataSplitAbility.cs index f6e0959db..6cebe439a 100644 --- a/PKHeX.Core/PKM/HOME/IGameDataSplitAbility.cs +++ b/PKHeX.Core/PKM/HOME/IGameDataSplitAbility.cs @@ -1,7 +1,17 @@ namespace PKHeX.Core; +/// +/// Holds the ability and ability number for a that has side-game specific data. +/// public interface IGameDataSplitAbility { + /// + /// Ability ID for the . + /// ushort Ability { get; set; } + + /// + /// Ability number for the . + /// byte AbilityNumber { get; set; } } diff --git a/PKHeX.Core/PKM/HOME/IHomeStorage.cs b/PKHeX.Core/PKM/HOME/IHomeStorage.cs index 754741b52..18c0960cc 100644 --- a/PKHeX.Core/PKM/HOME/IHomeStorage.cs +++ b/PKHeX.Core/PKM/HOME/IHomeStorage.cs @@ -1,11 +1,29 @@ namespace PKHeX.Core; +/// +/// Interface for a HOME storage system. +/// public interface IHomeStorage { + /// + /// Checks if the given tracker exists in the storage system. + /// + /// Tracker to check + /// True if the tracker exists, false otherwise. bool Exists(ulong tracker); + + /// + /// Gets the HOME entity for the given . + /// + /// Input PKM type + /// PKM to get the entity for + /// HOME entity for the given . PKH GetEntity(T pk) where T : PKM; } +/// +/// Facade for a HOME storage system. +/// public sealed class HomeStorageFacade : IHomeStorage { public bool Exists(ulong tracker) => false; diff --git a/PKHeX.Core/PKM/HOME/IPokerusStatus.cs b/PKHeX.Core/PKM/HOME/IPokerusStatus.cs index 64feb2a92..23e771e3c 100644 --- a/PKHeX.Core/PKM/HOME/IPokerusStatus.cs +++ b/PKHeX.Core/PKM/HOME/IPokerusStatus.cs @@ -1,6 +1,12 @@ namespace PKHeX.Core; +/// +/// Holds the Pokerus status of a . +/// public interface IPokerusStatus { + /// + /// Pokerus Strain and Duration + /// byte PKRS { get; set; } } diff --git a/PKHeX.Core/PKM/Shared/IObedienceLevel.cs b/PKHeX.Core/PKM/Interfaces/IObedienceLevel.cs similarity index 90% rename from PKHeX.Core/PKM/Shared/IObedienceLevel.cs rename to PKHeX.Core/PKM/Interfaces/IObedienceLevel.cs index 5908c59d2..65bc86c5f 100644 --- a/PKHeX.Core/PKM/Shared/IObedienceLevel.cs +++ b/PKHeX.Core/PKM/Interfaces/IObedienceLevel.cs @@ -16,7 +16,7 @@ public static class ObedienceExtensions { if (entity.Species is (int)Species.Koraidon or (int)Species.Miraidon && entity is PK9 { FormArgument: not 0 }) return 0; // Box Legend ride-able is default 0. Everything else is met level! - if (entity.Version is not (int)GameVersion.SL or (int)GameVersion.VL) + if (entity.Version is not ((int)GameVersion.SL or (int)GameVersion.VL)) return (byte)entity.CurrentLevel; // foreign, play it safe. // Can just assume min-level return (byte)originalMet; diff --git a/PKHeX.Core/PKM/PA8.cs b/PKHeX.Core/PKM/PA8.cs index 4e970ce0a..9f6a580ed 100644 --- a/PKHeX.Core/PKM/PA8.cs +++ b/PKHeX.Core/PKM/PA8.cs @@ -316,7 +316,7 @@ public sealed class PA8 : PKM, ISanityChecksum, public bool RIB47_5 { get => FlagUtil.GetFlag(Data, 0x47, 5); set => FlagUtil.SetFlag(Data, 0x47, 5, value); } public bool RIB47_6 { get => FlagUtil.GetFlag(Data, 0x47, 6); set => FlagUtil.SetFlag(Data, 0x47, 6, value); } public bool RIB47_7 { get => FlagUtil.GetFlag(Data, 0x47, 7); set => FlagUtil.SetFlag(Data, 0x47, 7, value); } - + public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b00000000_00011111__11111111_11111111__11111111_11111111__11111111_11111111) + BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000100_00011100__00000000_00000000__00000000_00000000); public int MarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000) diff --git a/PKHeX.Core/PKM/Util/EntityCharacteristic.cs b/PKHeX.Core/PKM/Util/EntityCharacteristic.cs new file mode 100644 index 000000000..4a84df60b --- /dev/null +++ b/PKHeX.Core/PKM/Util/EntityCharacteristic.cs @@ -0,0 +1,114 @@ +using System; + +namespace PKHeX.Core; + +/// +/// Logic for calculating the characteristic behavior of Pokémon. +/// +public static class EntityCharacteristic +{ + /// + /// Gets the characteristic index from the input parameters. + /// + /// Index of the highest IV. + /// Value of the highest IV. + /// Characteristic index. + public static int GetCharacteristic(int maxStatIndex, int maxStatValue) => (maxStatIndex * 5) + (maxStatValue % 5); + + /// + /// Gets the characteristic index of the given IVs in Little Endian format. + /// + /// Encryption Constant. + /// Lumped IVs in Little Endian format. + /// Characteristic index. + public static int GetCharacteristic(uint ec, uint iv32) + { + int index = (int)(ec % 6); + + // Get individual IVs from the lumped value. + // The IVs are stored in the following order: HP, Atk, Def, Spe, SpA, SpD + // Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen. + int maxStatIndex = index; + var maxStatValue = 0u; + do + { + var value = iv32 >> (index * 5) & 0x1F; + if (value > maxStatValue) + { + maxStatIndex = index; + maxStatValue = value; + } + if (index != 5) + index++; + else + index = 0; + } while (maxStatIndex != index); + + return GetCharacteristic(maxStatIndex, (int)maxStatValue); + } + + /// + /// Gets the characteristic index of the given unpacked IVs. + /// + /// Encryption Constant. + /// Unpacked IVs. + /// Characteristic index. + public static int GetCharacteristic(uint ec, Span ivs) + { + int index = (int)(ec % 6); + + // Get individual IVs from the lumped value. + // The IVs are stored in the following order: HP, Atk, Def, Spe, SpA, SpD + // Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen. + int maxStatIndex = index; + var maxStatValue = 0; + do + { + var value = ivs[index]; + if (value > maxStatValue) + { + maxStatIndex = index; + maxStatValue = value; + } + if (index != 5) + index++; + else + index = 0; + } while (maxStatIndex != index); + + return GetCharacteristic(maxStatIndex, maxStatValue); + } + + /// + /// Gets the characteristic index of the given IVs in Big Endian format. + /// + /// Encryption Constant. + /// Lumped IVs in Big Endian format. + /// Characteristic index. + public static int GetCharacteristicInvertFields(uint ec, uint iv32) + { + int index = (int)(ec % 6); + + // Get individual IVs from the lumped value. + // The IVs are stored in the following order: SpD, SpA, Spe, Def, Atk, HP + // Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen. + int maxStatIndex = index; + var maxStatValue = 0u; + do + { + // IVs are stored in reverse order, get the bits from the end of the IV value + var value = iv32 >> (27 - (index * 5)) & 0x1F; + if (value > maxStatValue) + { + maxStatIndex = index; + maxStatValue = value; + } + if (index != 5) + index++; + else + index = 0; + } while (maxStatIndex != index); + + return GetCharacteristic(maxStatIndex, (int)maxStatValue); + } +} diff --git a/PKHeX.Core/Resources/legality/mgdb/wc8.pkl b/PKHeX.Core/Resources/legality/mgdb/wc8.pkl index eb77d6205cf04970a445f1b4c21439252c7e4cf1..0e8515e257d3686f6dc12856a27eee67848871db 100644 GIT binary patch delta 277 zcmbQRR&&BF%?&r0HgY&Hu`sYQFiZ|)lAfHv#5y^LDS=6)aPv8)TTGj8FkzF;KeTxs zD^PX?D>hljvzzlcfU*@BCg0&oU}|`~*^V11>%r~7#Kli1>I8A=X(Kq=6hXqHhL8n2zL5jiT2a+Q8 zFb+aHjK#!&TaFnfH(6guR2jEAR=B$TQlbcpFH93*hiaPqo>3H!Be;OBNuDFX@QQ(h z*^hA>!$w9Jy_J!H$%uiSfx(Dla&WH(JA;@!!ykq}lN&VTCOVif?OwcjThlH9ksrW7 z0EZVeFrIzlm76G|HrY!<1~2|MfsMJ8A!nkY@MKys^?EsEyO_EdS%Log$^fIS85kIi zfC+%fh#TlHO->GZhBrPX7Hk5O7Yh3`d0yX~Hd%{X<1H|SAbX2@)+?Y!2w>94ez)V& znw<+Sp4dDY7}Oke;;@OHxNuDN;6y_a4h{yIvXwaC$zmR(6@w%&0AciUV74)0U}c>A zV3H;egFJ`4Ji``-GYkwh;NX+Gy}4~(kiO&xR-{}649bsSU0e(?n-z-|Gcg^n*nIBn zApvfnC byte.MaxValue => byte.MaxValue, - >= 3 when count > ushort.MaxValue => ushort.MaxValue, + _ when count > ushort.MaxValue => ushort.MaxValue, _ => count, }; return true; diff --git a/PKHeX.Core/Util/Util.cs b/PKHeX.Core/Util/Util.cs index e639f9c9e..231e08041 100644 --- a/PKHeX.Core/Util/Util.cs +++ b/PKHeX.Core/Util/Util.cs @@ -119,15 +119,24 @@ public static partial class Util return result; } - public static byte[] GetBytesFromHexString(ReadOnlySpan seed) + /// + /// Parses a variable length hex string (non-spaced, bytes in reverse order). + /// + public static byte[] GetBytesFromHexString(ReadOnlySpan input) + { + byte[] result = new byte[input.Length / 2]; + GetBytesFromHexString(input, result); + return result; + } + + /// + public static void GetBytesFromHexString(ReadOnlySpan input, Span result) { - byte[] result = new byte[seed.Length / 2]; for (int i = 0; i < result.Length; i++) { - var slice = seed.Slice(i * 2, 2); - result[^(i+1)] = (byte)GetHexValue(slice); + var slice = input.Slice(i * 2, 2); + result[^(i + 1)] = (byte)GetHexValue(slice); } - return result; } private const string HexChars = "0123456789ABCDEF"; diff --git a/PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs b/PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs index 111ac176c..e2f6a2251 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/SizeCP.cs @@ -108,7 +108,7 @@ public partial class SizeCP : UserControl var label = L_SizeH; var value = ss.HeightScalar; label.Text = SizeClass[(int)PokeSizeUtil.GetSizeRating(value)]; - SetLabelColorHeightWeight(label, value); + SetLabelColorHeightWeight(label); } if (!CHK_Auto.Checked || Loading || sv == null) @@ -128,7 +128,7 @@ public partial class SizeCP : UserControl var label = L_SizeW; var value = ss.WeightScalar; label.Text = SizeClass[(int)PokeSizeUtil.GetSizeRating(value)]; - SetLabelColorHeightWeight(label, value); + SetLabelColorHeightWeight(label); } if (!CHK_Auto.Checked || Loading || sv == null) @@ -158,12 +158,10 @@ public partial class SizeCP : UserControl } } - private void SetLabelColorHeightWeight(Control label, byte value) + private void SetLabelColorHeightWeight(Control label) { if (scale is not null) label.ForeColor = Color.Gray; // not indicative of actual size - else if (value is 255 && scale is PA8) - label.ForeColor = Color.Red; // Alpha or (unlikely, user error?) else label.ResetForeColor(); } diff --git a/PKHeX.WinForms/Resources/text/changelog.txt b/PKHeX.WinForms/Resources/text/changelog.txt index a3900742b..c41d56f28 100644 --- a/PKHeX.WinForms/Resources/text/changelog.txt +++ b/PKHeX.WinForms/Resources/text/changelog.txt @@ -1,7 +1,27 @@ PKHeX - By Kaphotics http://projectpokemon.org/pkhex/ -23/06/03 - New Update: +23/07/09 - New Update: + - Legality: Updated evolution checking algorithms to better identify game visitation. Thanks @Lusamine & @sora10pls ! + - - Added: HOME 3.0.0 move sharing logic for learning moves via HOME instead of in-game. + - - Added: HOME 3.0.0 ball inheritance logic for breeding encounters for/from Scarlet/Violet. + - - Added: GO Master Ball legality checks now allows Master Ball when encounter date & type permits. + - - Added: GO encounters outside the availability window now display a detailed illegal message. + - Added: Gen4/5 Geonet/Unity Tower editor. Thanks @abcboy101 ! + - Changed: Gen9 Tera Type icons now use higher quality sprites from HOME. Thanks @sora10pls ! + - Changed: Gen8a Move Shop editor GUI has been redesigned for a more visual experience. + - Changed: Gen7b+ Height/Weight now show as gray whenever Scale takes precedence in sizing determinations. + - Fixed: Batch Editor filters now work correctly for Box/Slot when using operators other than ! or = + - Fixed: Gen7b Randomize IV/AV buttons now center correctly within the form. + - Fixed: Gen7 Report grid now shows Resort (Poké Pelago) slots correctly. + - Fixed: Gen6/7 Memory Editor now shows Country/Region visitation correctly. + - Fixed: Gen6 Hall of Fame editor now keeps leading zeroes on the TID/SID entry fields. + - Fixed: Gen5 unused second Roamer block editor editing now reads from the correct offsets. Thanks SaltedNeos ! + - Fixed: Gen4 Ranch checksums are now applied correctly again. Thanks @Zazsona ! + - Fixed: Gen3 adding Ferry Tickets no longer causes the program to error when your Key Items bag is full. + - Fixed: Gen2 International Crystal saves now set the backup checksum to the correct offset. Thanks @Zazsona ! + +23/06/03 - New Update: (178908) [6981641] - Added: HOME 3.0.0 initial support. Legality analysis is still under research. Thanks @Lusamine & @sora10pls ! - - Lacks full support for cross-game transfers involving evolutions and moves. Please be patient, there's a lot of things to handle! - Added: Tech Record editor GUI has been redesigned for a more visual experience. Can now sort alphabetically, owned, or move type. @@ -10,7 +30,7 @@ http://projectpokemon.org/pkhex/ - Changed: Gen7 Totem Form sprites now display with an Orange-colored glow, similar to Gen3 C/XD shadow purple. - Changed: DPI scaling adjusted for better appearance on high resolution monitors. -23/05/11 - New Update: +23/05/11 - New Update: (80094) [6802733] - Legality: - - Changed: Creating a new PKM from template will set current moves same as the games. - - Changed: Pokérus strains 0 & 8 now permitted due to official implementation errors in Gen2 & Gen3. @@ -29,7 +49,7 @@ http://projectpokemon.org/pkhex/ - Fixed: Gen3 XD un-purified shadow Pokémon no longer flag w/o Ribbon if they have a Shadow Gauge of 0 (ready to purify). - Changed: Sub-form scaling mode changed to "Inherit" -- added a Display setting to revert back to Form scaling (from Dpi). -23/04/06 - New Update: +23/04/06 - New Update: (166321) [6722639] - Legality: - - Changed: Vivillon 3DS Region handling reworked to handle all regions. Thanks @abcboy101! - - Fixed: BDSP footprint ribbon now checks correctly.