diff --git a/PKHeX.Core/Game/GameUtil.cs b/PKHeX.Core/Game/GameUtil.cs index 48a19cce2..874659e9d 100644 --- a/PKHeX.Core/Game/GameUtil.cs +++ b/PKHeX.Core/Game/GameUtil.cs @@ -141,6 +141,8 @@ public static int GetMaxSpeciesID(this GameVersion game) return Legal.MaxSpeciesID_7; if (USUM.Contains(game)) return Legal.MaxSpeciesID_7_USUM; + if (GG.Contains(game)) + return Legal.MaxSpeciesID_7b; return Legal.MaxSpeciesID_7_USUM; } return -1; diff --git a/PKHeX.Core/PKM/Searching/SearchSettings.cs b/PKHeX.Core/PKM/Searching/SearchSettings.cs index 6e413487e..6e8532988 100644 --- a/PKHeX.Core/PKM/Searching/SearchSettings.cs +++ b/PKHeX.Core/PKM/Searching/SearchSettings.cs @@ -11,7 +11,7 @@ public class SearchSettings { public int Format { private get; set; } public int Generation { private get; set; } - public int Species { private get; set; } = -1; + public int Species { get; set; } = -1; public int Ability { private get; set; } = -1; public int Nature { private get; set; } = -1; public int Item { private get; set; } = -1; @@ -23,7 +23,7 @@ public class SearchSettings public bool? SearchShiny { private get; set; } public bool? SearchLegal { private get; set; } - public bool? SearchEgg { private get; set; } + public bool? SearchEgg { get; set; } public int? ESV { private get; set; } public int? Level { private get; set; } @@ -33,7 +33,7 @@ public class SearchSettings public CloneDetectionMethod SearchClones { private get; set; } public IList BatchInstructions { private get; set; } - private readonly List Moves = new List(); + public readonly List Moves = new List(); // ReSharper disable once CollectionNeverUpdated.Global /// @@ -134,5 +134,19 @@ private IEnumerable FilterResultEgg(IEnumerable res) return res.Where(pk => pk.IsEgg && pk.PSV == ESV); return res.Where(pk => pk.IsEgg); } + + public GameVersion[] GetVersions(SaveFile SAV, GameVersion fallback) + { + if (Version > 0) + return new[] {(GameVersion) Version}; + if (Generation != 0) + { + return fallback.GetGeneration() == Generation + ? GameUtil.GetVersionsWithinRange(SAV, Generation).ToArray() + : GameUtil.GameVersions; + } + + return GameUtil.GameVersions; + } } } diff --git a/PKHeX.Core/PKM/Util/PKX.cs b/PKHeX.Core/PKM/Util/PKX.cs index 8c8417dec..bfd1e1a2c 100644 --- a/PKHeX.Core/PKM/Util/PKX.cs +++ b/PKHeX.Core/PKM/Util/PKX.cs @@ -489,8 +489,9 @@ public static uint GetRandomPID(int species, int cg, int origin, int nature, int // Data Requests public static string GetResourceStringBall(int ball) => $"_ball{ball}"; private const string ResourceSeparator = "_"; - private const string ResourcePikachuCosplay = "c"; - private const string ResourceShiny = "s"; + private const string ResourcePikachuCosplay = "c"; // osplay + private const string ResourceShiny = "s"; // hiny + private const string ResourceGGStarter = "p"; //artner public static bool AllowShinySprite { get; set; } public static string GetResourceStringSprite(int species, int form, int gender, int generation = Generation, bool shiny = false) @@ -507,6 +508,9 @@ public static string GetResourceStringSprite(int species, int form, int gender, if (species == 25 && form > 0 && generation == 6) // Cosplay Pikachu sb.Append(ResourcePikachuCosplay); + else if (GameVersion.GG.Contains(PKMConverter.Trainer.Game) && (species == 25 || species == 133) && form != 0) + sb.Append(ResourceGGStarter); + if (shiny && AllowShinySprite) sb.Append(ResourceShiny); return sb.ToString(); @@ -783,9 +787,10 @@ public static string GetLocationString(this PKM pk, bool eggmet) /// Source list to copy from /// Destination list/array /// Context for checking slot write protection + /// Criteria for skipping a slot /// Starting point to copy to /// Count of copied. - public static int CopyTo(this IEnumerable list, IList dest, SaveFile sav, int start = 0) + public static int CopyTo(this IEnumerable list, IList dest, SaveFile sav, Func skip, int start = 0) { int ctr = start; foreach (var z in list) @@ -793,7 +798,7 @@ public static int CopyTo(this IEnumerable list, IList dest, SaveFile s if (dest.Count <= ctr) break; var exist = dest[ctr]; - if (exist != null && sav.IsSlotOverwriteProtected(exist.Box, exist.Slot)) + if (exist != null && skip(exist.Box, exist.Slot)) continue; dest[ctr++] = z; } diff --git a/PKHeX.Core/Saves/SaveFile.cs b/PKHeX.Core/Saves/SaveFile.cs index 00d172d40..ff0d6641b 100644 --- a/PKHeX.Core/Saves/SaveFile.cs +++ b/PKHeX.Core/Saves/SaveFile.cs @@ -117,7 +117,7 @@ protected virtual byte[] Write(bool DSV) public virtual bool HasBoxWallpapers => GetBoxWallpaperOffset(0) > -1; public virtual bool HasNamableBoxes => HasBoxWallpapers; public bool HasPokeBlock => ORAS && !ORASDEMO; - public bool HasEvents => EventFlags != null; + public virtual bool HasEvents => EventFlags != null; public bool HasLink => (ORAS && !ORASDEMO) || XY; // Counts @@ -454,6 +454,7 @@ public bool IsPartyAllEggs(params int[] except) public virtual int CurrentBox { get => 0; set { } } protected int[] LockedSlots = Array.Empty(); protected int[] TeamSlots = Array.Empty(); + protected virtual IList[] SlotPointers => new[] {LockedSlots,TeamSlots}; public bool MoveBox(int box, int insertBeforeBox) { @@ -491,6 +492,8 @@ public bool MoveBox(int box, int insertBeforeBox) SetBoxName(b, boxNames[i]); SetBoxWallpaper(b, boxWallpapers[i]); } + SlotPointerUtil.UpdateMove(box, insertBeforeBox, BoxSlotCount, SlotPointers); + return true; } @@ -522,6 +525,10 @@ public bool SwapBox(int box1, int box2) int b1w = GetBoxWallpaper(box1); SetBoxWallpaper(box1, GetBoxWallpaper(box2)); SetBoxWallpaper(box2, b1w); + + // Pointers + SlotPointerUtil.UpdateSwap(box1, box2, BoxSlotCount, SlotPointers); + return true; } @@ -657,21 +664,23 @@ public void DeletePartySlot(int slot) public virtual bool IsSlotLocked(int box, int slot) => false; public virtual bool IsSlotInBattleTeam(int box, int slot) => false; - - public bool IsSlotOverwriteProtected(int box, int slot) => IsSlotLocked(box, slot) || IsSlotInBattleTeam(box, slot); + protected virtual bool IsSlotOverwriteProtected(int box, int slot) => IsSlotLocked(box, slot) || IsSlotInBattleTeam(box, slot); + protected virtual bool IsSlotSwapProtected(int box, int slot) => false; private bool IsRegionOverwriteProtected(int min, int max) { - if (LockedSlots.Any(slot => min <= slot && slot < max)) // locked slot within box + if (LockedSlots.Any(slot => WithinRange(slot, min, max))) // locked slot within box return true; - if (TeamSlots.Any(slot => min <= slot && slot < max)) // team slot within box + if (TeamSlots.Any(slot => WithinRange(slot, min, max))) // team slot within box return true; return false; } + private static bool WithinRange(int slot, int min, int max) => min <= slot && slot < max; + public bool IsAnySlotLockedInBox(int BoxStart, int BoxEnd) { - return LockedSlots.Any(slot => BoxStart*BoxSlotCount <= slot && slot < (BoxEnd + 1)*BoxSlotCount); + return LockedSlots.Any(slot => WithinRange(slot, BoxStart*BoxSlotCount, (BoxEnd + 1)*BoxSlotCount)); } public void SortBoxes(int BoxStart = 0, int BoxEnd = -1, Func, IEnumerable> sortMethod = null, bool reverse = false) @@ -682,13 +691,19 @@ public void SortBoxes(int BoxStart = 0, int BoxEnd = -1, Func, if (BoxEnd >= BoxStart) Section = Section.Take(BoxSlotCount * (BoxEnd - BoxStart + 1)); - Section = Section.Where(z => !IsSlotOverwriteProtected(z.Box, z.Slot)); + Func skip = IsSlotSwapProtected; + Section = Section.Where(z => !skip(z.Box, z.Slot)); var Sorted = (sortMethod ?? PKMSorting.OrderBySpecies)(Section); if (reverse) Sorted = Sorted.ReverseSort(); - Sorted.CopyTo(BD, this, start); - BoxData = BD; + var result = Sorted.ToArray(); + var boxclone = new PKM[BD.Count]; + BD.CopyTo(boxclone, 0); + result.CopyTo(boxclone, this, skip, start); + + SlotPointerUtil.UpdateRepointFrom(boxclone, BD, 0, SlotPointers); + BoxData = boxclone; } public void ClearBoxes(int BoxStart = 0, int BoxEnd = -1, Func deleteCriteria = null) @@ -751,7 +766,7 @@ public bool SetPCBinary(byte[] data) var BD = BoxData; var pkdata = PKX.GetPKMDataFromConcatenatedBinary(data, BlankPKM.EncryptedBoxData.Length); - pkdata.Select(z => GetPKM(DecryptPKM(z))).CopyTo(BD, this); + pkdata.Select(z => GetPKM(DecryptPKM(z))).CopyTo(BD, this, IsSlotOverwriteProtected); BoxData = BD; return true; } @@ -767,7 +782,7 @@ public bool SetBoxBinary(byte[] data, int box) var BD = BoxData; var pkdata = PKX.GetPKMDataFromConcatenatedBinary(data, BlankPKM.EncryptedBoxData.Length); - pkdata.Select(z => GetPKM(DecryptPKM(z))).CopyTo(BD, this, start); + pkdata.Select(z => GetPKM(DecryptPKM(z))).CopyTo(BD, this, IsSlotOverwriteProtected, start); BoxData = BD; return true; } @@ -832,7 +847,7 @@ public bool IsRangeAll(int Offset, int Length, int value) /// Count of actual stored. /// Important slot pointers that need to be repointed if a slot moves. /// True if was updated, false if no update done. - public bool CompressStorage(out int storedCount, params ushort[][] slotPointers) + public bool CompressStorage(out int storedCount, params IList[] slotPointers) { // keep track of empty slots, and only write them at the end if slots were shifted (no need otherwise). var empty = new List(); @@ -850,14 +865,7 @@ public bool CompressStorage(out int storedCount, params ushort[][] slotPointers) { shiftedSlots = true; // appending empty slots afterwards is now required since a rewrite was done Buffer.BlockCopy(Data, offset, Data, Box + (ctr * size), size); - foreach (var ptrSet in slotPointers) - { - for (int j = 0; j < ptrSet.Length; j++) // update ptr - { - if (ptrSet[j] == i) - ptrSet[j] = ctr; - } - } + SlotPointerUtil.UpdateRepointFrom(ctr, i, slotPointers); } ctr++; continue; diff --git a/PKHeX.Core/Saves/Storage/SlotPointerUtil.cs b/PKHeX.Core/Saves/Storage/SlotPointerUtil.cs new file mode 100644 index 000000000..69310b17b --- /dev/null +++ b/PKHeX.Core/Saves/Storage/SlotPointerUtil.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace PKHeX.Core +{ + public static class SlotPointerUtil + { + private static bool WithinRange(int slot, int min, int max) => min <= slot && slot < max; + + public static void UpdateSwap(int b1, int b2, int slotsPerBox, params IList[] ptrset) + { + int diff = (b1 - b2) * slotsPerBox; + + int b1s = b1 * slotsPerBox; + int b1e = b1s + slotsPerBox; + int b12 = b1 > b2 ? -diff : diff; + + int b2s = b2 * slotsPerBox; + int b2e = b2s + slotsPerBox; + int b21 = b2 > b1 ? -diff : diff; + foreach (var list in ptrset) + { + for (int s = 0; s < list.Count; s++) + { + if (WithinRange(b1s, b1e, list[s])) + list[s] += b12; + else if (WithinRange(b2s, b2e, list[s])) + list[s] += b21; + } + } + } + + public static void UpdateRepointFrom(IList result, IList bd, int start, params IList[] slotPointers) + { + foreach (var p in slotPointers) + { + for (int i = 0; i < p.Count; i++) + { + var index = p[i]; + if (index >= bd.Count) + continue; + var pk = bd[index]; + var newIndex = result.IndexOf(pk); + if (newIndex < 0) + continue; + + Debug.WriteLine($"Repointing {pk.Nickname} from ({pk.Box}|{pk.Slot}) to {newIndex}"); + Debug.WriteLine($"{result[newIndex]}"); + p[i] = start + newIndex; + } + } + } + + public static void UpdateRepointFrom(int newIndex, int oldIndex, params IList[] slotPointers) + { + foreach (var p in slotPointers) + { + for (int s = 0; s < p.Count; s++) + { + if (p[s] != oldIndex) + continue; + p[s] = newIndex; + break; + } + } + } + + public static void UpdateMove(int bMove, int cMove, int slotsPerBox, params IList[] ptrset) + { + int bStart = bMove * slotsPerBox; + int cStart = cMove * slotsPerBox; + int bEnd = bStart + slotsPerBox; + int cEnd = cStart + slotsPerBox; + int cShift; + int bShift; + if (bMove < cMove) // shift chunk down, shift box up + { + cShift = -slotsPerBox; + bShift = (cStart - bStart); + } + else // shift box down, shift chunk up + { + cShift = slotsPerBox; + bShift = -(bStart - cStart); + } + foreach (var list in ptrset) + { + for (int s = 0; s < list.Count; s++) + { + if (WithinRange(cStart, cEnd, list[s])) + list[s] += cShift; + if (WithinRange(bStart, bEnd, list[s])) + list[s] += bShift; + } + } + } + } +}