diff --git a/PKHeX.Core/Editing/PKM/EntitySummary.cs b/PKHeX.Core/Editing/PKM/EntitySummary.cs index e86a439a3..de062a7fd 100644 --- a/PKHeX.Core/Editing/PKM/EntitySummary.cs +++ b/PKHeX.Core/Editing/PKM/EntitySummary.cs @@ -104,7 +104,6 @@ public class EntitySummary // do NOT seal, allow inheritance public int Met_Year => pkm.MetDate.GetValueOrDefault().Year; public int Met_Month => pkm.MetDate.GetValueOrDefault().Month; public int Met_Day => pkm.MetDate.GetValueOrDefault().Day; - public int Encounter => pkm.EncounterType; #endregion diff --git a/PKHeX.Core/Legality/Areas/EncounterArea.cs b/PKHeX.Core/Legality/Areas/EncounterArea.cs index 118575a07..e8668a589 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea.cs @@ -1,5 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Linq; namespace PKHeX.Core { @@ -11,7 +11,7 @@ public abstract record EncounterArea : IVersion public GameVersion Version { get; } public int Location { get; protected init; } public SlotType Type { get; protected init; } = SlotType.Any; - public EncounterSlot[] Slots { get; protected init; } = Array.Empty(); + protected abstract IReadOnlyList Raw { get; } protected EncounterArea(GameVersion game) => Version = game; @@ -29,5 +29,8 @@ public abstract record EncounterArea : IVersion /// Met Location ID /// True if possibly originated from this area, false otherwise. public virtual bool IsMatchLocation(int location) => Location == location; + + public bool HasSpecies(int species) => Raw.Any(z => z.Species == species); + public IEnumerable GetSpecies(IReadOnlyList chain) => Raw.Where(z => chain.Any(c => z.Species == c.Species)); } } diff --git a/PKHeX.Core/Legality/Areas/EncounterArea1.cs b/PKHeX.Core/Legality/Areas/EncounterArea1.cs index 7b075ec95..195dd0e51 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea1.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea1.cs @@ -9,6 +9,9 @@ namespace PKHeX.Core public sealed record EncounterArea1 : EncounterArea { public readonly int Rate; + public readonly EncounterSlot1[] Slots; + + protected override IReadOnlyList Raw => Slots; public static EncounterArea1[] GetAreas(byte[][] input, GameVersion game) { diff --git a/PKHeX.Core/Legality/Areas/EncounterArea2.cs b/PKHeX.Core/Legality/Areas/EncounterArea2.cs index 8d1153a1a..7318e4c42 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea2.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea2.cs @@ -16,6 +16,9 @@ public sealed record EncounterArea2 : EncounterArea internal readonly EncounterTime Time; public readonly int Rate; public readonly IReadOnlyList Rates; + public readonly EncounterSlot2[] Slots; + + protected override IReadOnlyList Raw => Slots; public static EncounterArea2[] GetAreas(byte[][] input, GameVersion game) { @@ -86,7 +89,7 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi return GetSlotsSpecificLevelTime(chain, pk2.Met_TimeOfDay, pk2.Met_Level); } - private IEnumerable GetSlotsSpecificLevelTime(IReadOnlyList chain, int time, int lvl) + private IEnumerable GetSlotsSpecificLevelTime(IReadOnlyList chain, int time, int lvl) { foreach (var slot in Slots) { @@ -113,7 +116,7 @@ private IEnumerable GetSlotsSpecificLevelTime(IReadOnlyList GetSlotsFuzzy(IReadOnlyList chain) + private IEnumerable GetSlotsFuzzy(IReadOnlyList chain) { foreach (var slot in Slots) { diff --git a/PKHeX.Core/Legality/Areas/EncounterArea3.cs b/PKHeX.Core/Legality/Areas/EncounterArea3.cs index 4cf6585d8..fcafbe735 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea3.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea3.cs @@ -10,6 +10,9 @@ namespace PKHeX.Core public sealed record EncounterArea3 : EncounterArea { public readonly int Rate; + public readonly EncounterSlot3[] Slots; + + protected override IReadOnlyList Raw => Slots; public static EncounterArea3[] GetAreas(byte[][] input, GameVersion game) { @@ -108,7 +111,7 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi return GetSlotsMatching(chain, pkm.Met_Level); } - private IEnumerable GetSlotsMatching(IReadOnlyList chain, int lvl) + private IEnumerable GetSlotsMatching(IReadOnlyList chain, int lvl) { foreach (var slot in Slots) { @@ -128,7 +131,7 @@ private IEnumerable GetSlotsMatching(IReadOnlyList c } } - private IEnumerable GetSlotsFuzzy(IReadOnlyList chain) + private IEnumerable GetSlotsFuzzy(IReadOnlyList chain) { foreach (var slot in Slots) { diff --git a/PKHeX.Core/Legality/Areas/EncounterArea3XD.cs b/PKHeX.Core/Legality/Areas/EncounterArea3XD.cs index aea6d18af..e309cdeda 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea3XD.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea3XD.cs @@ -9,6 +9,10 @@ namespace PKHeX.Core /// public sealed record EncounterArea3XD : EncounterArea { + public readonly EncounterSlot3PokeSpot[] Slots; + + protected override IReadOnlyList Raw => Slots; + public EncounterArea3XD(int loc, int s0, int l0, int s1, int l1, int s2, int l2) : base(GameVersion.XD) { Location = loc; @@ -26,11 +30,11 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi if (pkm.Format != 3) // Met Location and Met Level are changed on PK3->PK4 return GetSlotsFuzzy(chain); if (pkm.Met_Location != Location) - return Array.Empty(); + return Array.Empty(); return GetSlotsMatching(chain, pkm.Met_Level); } - private IEnumerable GetSlotsMatching(IReadOnlyList chain, int lvl) + private IEnumerable GetSlotsMatching(IReadOnlyList chain, int lvl) { foreach (var slot in Slots) { @@ -50,7 +54,7 @@ private IEnumerable GetSlotsMatching(IReadOnlyList c } } - private IEnumerable GetSlotsFuzzy(IReadOnlyList chain) + private IEnumerable GetSlotsFuzzy(IReadOnlyList chain) { foreach (var slot in Slots) { diff --git a/PKHeX.Core/Legality/Areas/EncounterArea4.cs b/PKHeX.Core/Legality/Areas/EncounterArea4.cs index 9bce24031..e6550de31 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea4.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea4.cs @@ -9,8 +9,11 @@ namespace PKHeX.Core /// public sealed record EncounterArea4 : EncounterArea { - public readonly EncounterType TypeEncounter; public readonly int Rate; + public readonly GroundTilePermission GroundTile; + public readonly EncounterSlot4[] Slots; + + protected override IReadOnlyList Raw => Slots; public static EncounterArea4[] GetAreas(byte[][] input, GameVersion game) { @@ -25,7 +28,8 @@ private EncounterArea4(byte[] data, GameVersion game) : base(game) Location = data[0] | (data[1] << 8); Type = (SlotType)data[2]; Rate = data[3]; - TypeEncounter = (EncounterType) BitConverter.ToUInt16(data, 4); + // although GroundTilePermission flags are 32bit, none have values > 16bit. + GroundTile = (GroundTilePermission) BitConverter.ToUInt16(data, 4); Slots = ReadRegularSlots(data); } @@ -60,11 +64,11 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi if (pkm.Format != 4) // Met Location and Met Level are changed on PK4->PK5 return GetSlotsFuzzy(chain); if (pkm.Met_Location != Location) - return Array.Empty(); + return Array.Empty(); return GetSlotsMatching(chain, pkm.Met_Level); } - private IEnumerable GetSlotsMatching(IReadOnlyList chain, int lvl) + private IEnumerable GetSlotsMatching(IReadOnlyList chain, int lvl) { foreach (var slot in Slots) { @@ -84,7 +88,7 @@ private IEnumerable GetSlotsMatching(IReadOnlyList c } } - private IEnumerable GetSlotsFuzzy(IReadOnlyList chain) + private IEnumerable GetSlotsFuzzy(IReadOnlyList chain) { foreach (var slot in Slots) { diff --git a/PKHeX.Core/Legality/Areas/EncounterArea5.cs b/PKHeX.Core/Legality/Areas/EncounterArea5.cs index d6c945015..d541df943 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea5.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea5.cs @@ -9,6 +9,10 @@ namespace PKHeX.Core /// public sealed record EncounterArea5 : EncounterArea { + public readonly EncounterSlot5[] Slots; + + protected override IReadOnlyList Raw => Slots; + public static EncounterArea5[] GetAreas(byte[][] input, GameVersion game) { var result = new EncounterArea5[input.Length]; diff --git a/PKHeX.Core/Legality/Areas/EncounterArea6AO.cs b/PKHeX.Core/Legality/Areas/EncounterArea6AO.cs index be9b7ccac..21c66c2c0 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea6AO.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea6AO.cs @@ -9,6 +9,10 @@ namespace PKHeX.Core /// public sealed record EncounterArea6AO : EncounterArea { + public readonly EncounterSlot6AO[] Slots; + + protected override IReadOnlyList Raw => Slots; + public static EncounterArea6AO[] GetAreas(byte[][] input, GameVersion game) { var result = new EncounterArea6AO[input.Length]; @@ -68,12 +72,11 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi break; // Track some metadata about how this slot was matched. - var ao = (EncounterSlot6AO)slot; - var clone = ao with + var clone = slot with { WhiteFlute = evo.MinLevel < slot.LevelMin, BlackFlute = evo.MinLevel > slot.LevelMax && evo.MinLevel <= slot.LevelMax + FluteBoostMax, - DexNav = ao.CanDexNav && (evo.MinLevel != slot.LevelMax || pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4), + DexNav = slot.CanDexNav && (evo.MinLevel != slot.LevelMax || pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4), }; yield return clone; break; diff --git a/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs b/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs index 7a92e4f3f..1358ca25b 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs @@ -9,6 +9,10 @@ namespace PKHeX.Core /// public sealed record EncounterArea6XY : EncounterArea { + public readonly EncounterSlot6XY[] Slots; + + protected override IReadOnlyList Raw => Slots; + public static EncounterArea6XY[] GetAreas(byte[][] input, GameVersion game, EncounterArea6XY safari) { var result = new EncounterArea6XY[input.Length + 1]; @@ -124,7 +128,7 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi if (maxLevel != pkm.Met_Level) break; - yield return ((EncounterSlot6XY)slot).CreatePressureFormCopy(evo.Form); + yield return slot.CreatePressureFormCopy(evo.Form); break; } diff --git a/PKHeX.Core/Legality/Areas/EncounterArea7.cs b/PKHeX.Core/Legality/Areas/EncounterArea7.cs index 0d8c6c087..dd7a2b218 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea7.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea7.cs @@ -9,6 +9,10 @@ namespace PKHeX.Core /// public sealed record EncounterArea7 : EncounterArea { + public readonly EncounterSlot7[] Slots; + + protected override IReadOnlyList Raw => Slots; + public static EncounterArea7[] GetAreas(byte[][] input, GameVersion game) { var result = new EncounterArea7[input.Length]; diff --git a/PKHeX.Core/Legality/Areas/EncounterArea7b.cs b/PKHeX.Core/Legality/Areas/EncounterArea7b.cs index bad63fef8..e580553e1 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea7b.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea7b.cs @@ -8,6 +8,10 @@ namespace PKHeX.Core /// public sealed record EncounterArea7b : EncounterArea { + public readonly EncounterSlot7b[] Slots; + + protected override IReadOnlyList Raw => Slots; + public static EncounterArea7b[] GetAreas(byte[][] input, GameVersion game) { var result = new EncounterArea7b[input.Length]; diff --git a/PKHeX.Core/Legality/Areas/EncounterArea7g.cs b/PKHeX.Core/Legality/Areas/EncounterArea7g.cs index f96fab25a..6e7f0f80e 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea7g.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea7g.cs @@ -15,12 +15,16 @@ public sealed record EncounterArea7g : EncounterArea, ISpeciesForm public int Species { get; } /// Form of the Species public int Form { get; } + public readonly EncounterSlot7GO[] Slots; - private EncounterArea7g(int species, int form) : base(GameVersion.GO) + protected override IReadOnlyList Raw => Slots; + + private EncounterArea7g(int species, int form, EncounterSlot7GO[] slots) : base(GameVersion.GO) { Species = species; Form = form; Location = Locations.GO7; + Slots = slots; } internal static EncounterArea7g[] GetArea(byte[][] data) @@ -40,7 +44,7 @@ private static EncounterArea7g GetArea(byte[] data) int form = sf >> 11; var result = new EncounterSlot7GO[(data.Length - 2) / entrySize]; - var area = new EncounterArea7g(species, form) { Slots = result }; + var area = new EncounterArea7g(species, form, result); for (int i = 0; i < result.Length; i++) { var offset = (i * entrySize) + 2; @@ -72,11 +76,10 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi var stamp = EncounterSlotGO.GetTimeStamp(pkm.Met_Year + 2000, pkm.Met_Month, pkm.Met_Day); var met = Math.Max(sf.MinLevel, pkm.Met_Level); - EncounterSlot? deferredIV = null; + EncounterSlot7GO? deferredIV = null; - foreach (var s in Slots) + foreach (var slot in Slots) { - var slot = (EncounterSlot7GO)s; if (!slot.IsLevelWithinRange(met)) continue; //if (!slot.IsBallValid(ball)) -- can have any of the in-game balls due to re-capture diff --git a/PKHeX.Core/Legality/Areas/EncounterArea8.cs b/PKHeX.Core/Legality/Areas/EncounterArea8.cs index 268c5c520..06046b6ba 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea8.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea8.cs @@ -10,6 +10,9 @@ namespace PKHeX.Core /// public sealed record EncounterArea8 : EncounterArea { + public readonly EncounterSlot8[] Slots; + + protected override IReadOnlyList Raw => Slots; /// /// Slots from this area can cross over to another area, resulting in a different met location. /// @@ -234,9 +237,9 @@ private EncounterArea8(byte[] areaData, bool symbol, GameVersion game) : base(ga Slots = ReadSlots(areaData, areaData[1]); } - private EncounterSlot[] ReadSlots(byte[] areaData, byte slotCount) + private EncounterSlot8[] ReadSlots(byte[] areaData, byte slotCount) { - var slots = new EncounterSlot[slotCount]; + var slots = new EncounterSlot8[slotCount]; int ctr = 0; int ofs = 2; diff --git a/PKHeX.Core/Legality/Areas/EncounterArea8g.cs b/PKHeX.Core/Legality/Areas/EncounterArea8g.cs index e2e56cea1..e938a2622 100644 --- a/PKHeX.Core/Legality/Areas/EncounterArea8g.cs +++ b/PKHeX.Core/Legality/Areas/EncounterArea8g.cs @@ -15,12 +15,16 @@ public sealed record EncounterArea8g : EncounterArea, ISpeciesForm public int Species { get; } /// Form of the Species public int Form { get; } + public readonly EncounterSlot8GO[] Slots; - private EncounterArea8g(int species, int form) : base(GameVersion.GO) + protected override IReadOnlyList Raw => Slots; + + private EncounterArea8g(int species, int form, EncounterSlot8GO[] slots) : base(GameVersion.GO) { Species = species; Form = form; Location = Locations.GO8; + Slots = slots; } internal static EncounterArea8g[] GetArea(byte[][] data) @@ -42,7 +46,7 @@ private static EncounterArea8g GetArea(byte[] data) var group = GetGroup(species, form); var result = new EncounterSlot8GO[(data.Length - 2) / entrySize]; - var area = new EncounterArea8g(species, form) {Slots = result}; + var area = new EncounterArea8g(species, form, result); for (int i = 0; i < result.Length; i++) { var offset = (i * entrySize) + 2; @@ -102,11 +106,10 @@ public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyLi var ball = (Ball)pkm.Ball; var met = Math.Max(sf.MinLevel, pkm.Met_Level); - EncounterSlot? deferredIV = null; + EncounterSlot8GO? deferredIV = null; - foreach (var s in Slots) + foreach (var slot in Slots) { - var slot = (EncounterSlot8GO)s; if (!slot.IsLevelWithinRange(met)) continue; if (!slot.IsBallValid(ball)) diff --git a/PKHeX.Core/Legality/Encounters/Data/Encounters4.cs b/PKHeX.Core/Legality/Encounters/Data/Encounters4.cs index fe1a64c25..1656a5fde 100644 --- a/PKHeX.Core/Legality/Encounters/Data/Encounters4.cs +++ b/PKHeX.Core/Legality/Encounters/Data/Encounters4.cs @@ -1,5 +1,6 @@ using static PKHeX.Core.EncounterUtil; using static PKHeX.Core.GameVersion; +using static PKHeX.Core.GroundTilePermission; namespace PKHeX.Core { @@ -198,33 +199,33 @@ static Encounters4() private static readonly EncounterStatic4[] Encounter_DPPt = { // Starters - new(DP) { Gift = true, Species = 387, Level = 5, Location = 076, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Turtwig @ Lake Verity - new(DP) { Gift = true, Species = 390, Level = 5, Location = 076, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Chimchar - new(DP) { Gift = true, Species = 393, Level = 5, Location = 076, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Piplup - new(Pt) { Gift = true, Species = 387, Level = 5, Location = 016, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Turtwig @ Route 201 - new(Pt) { Gift = true, Species = 390, Level = 5, Location = 016, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Chimchar - new(Pt) { Gift = true, Species = 393, Level = 5, Location = 016, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Piplup + new(DP) { Gift = true, Species = 387, Level = 5, Location = 076, GroundTile = Max_DP }, // Turtwig @ Lake Verity + new(DP) { Gift = true, Species = 390, Level = 5, Location = 076, GroundTile = Max_DP }, // Chimchar + new(DP) { Gift = true, Species = 393, Level = 5, Location = 076, GroundTile = Max_DP }, // Piplup + new(Pt) { Gift = true, Species = 387, Level = 5, Location = 016, GroundTile = Max_Pt }, // Turtwig @ Route 201 + new(Pt) { Gift = true, Species = 390, Level = 5, Location = 016, GroundTile = Max_Pt }, // Chimchar + new(Pt) { Gift = true, Species = 393, Level = 5, Location = 016, GroundTile = Max_Pt }, // Piplup // Fossil @ Mining Museum - new(DP) { Gift = true, Species = 138, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Omanyte - new(DP) { Gift = true, Species = 140, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Kabuto - new(DP) { Gift = true, Species = 142, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Aerodactyl - new(DP) { Gift = true, Species = 345, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Lileep - new(DP) { Gift = true, Species = 347, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Anorith - new(DP) { Gift = true, Species = 408, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Cranidos - new(DP) { Gift = true, Species = 410, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Shieldon - new(Pt) { Gift = true, Species = 138, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Omanyte - new(Pt) { Gift = true, Species = 140, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Kabuto - new(Pt) { Gift = true, Species = 142, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Aerodactyl - new(Pt) { Gift = true, Species = 345, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Lileep - new(Pt) { Gift = true, Species = 347, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Anorith - new(Pt) { Gift = true, Species = 408, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Cranidos - new(Pt) { Gift = true, Species = 410, Level = 20, Location = 094, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Shieldon + new(DP) { Gift = true, Species = 138, Level = 20, Location = 094, GroundTile = Max_DP }, // Omanyte + new(DP) { Gift = true, Species = 140, Level = 20, Location = 094, GroundTile = Max_DP }, // Kabuto + new(DP) { Gift = true, Species = 142, Level = 20, Location = 094, GroundTile = Max_DP }, // Aerodactyl + new(DP) { Gift = true, Species = 345, Level = 20, Location = 094, GroundTile = Max_DP }, // Lileep + new(DP) { Gift = true, Species = 347, Level = 20, Location = 094, GroundTile = Max_DP }, // Anorith + new(DP) { Gift = true, Species = 408, Level = 20, Location = 094, GroundTile = Max_DP }, // Cranidos + new(DP) { Gift = true, Species = 410, Level = 20, Location = 094, GroundTile = Max_DP }, // Shieldon + new(Pt) { Gift = true, Species = 138, Level = 20, Location = 094, GroundTile = Max_Pt }, // Omanyte + new(Pt) { Gift = true, Species = 140, Level = 20, Location = 094, GroundTile = Max_Pt }, // Kabuto + new(Pt) { Gift = true, Species = 142, Level = 20, Location = 094, GroundTile = Max_Pt }, // Aerodactyl + new(Pt) { Gift = true, Species = 345, Level = 20, Location = 094, GroundTile = Max_Pt }, // Lileep + new(Pt) { Gift = true, Species = 347, Level = 20, Location = 094, GroundTile = Max_Pt }, // Anorith + new(Pt) { Gift = true, Species = 408, Level = 20, Location = 094, GroundTile = Max_Pt }, // Cranidos + new(Pt) { Gift = true, Species = 410, Level = 20, Location = 094, GroundTile = Max_Pt }, // Shieldon // Gift - new(DP) { Gift = true, Species = 133, Level = 05, Location = 010, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP, }, // Eevee @ Hearthome City - new(Pt) { Gift = true, Species = 133, Level = 20, Location = 010, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Eevee @ Hearthome City - new(Pt) { Gift = true, Species = 137, Level = 25, Location = 012, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Porygon @ Veilstone City + new(DP) { Gift = true, Species = 133, Level = 05, Location = 010, GroundTile = Max_DP, }, // Eevee @ Hearthome City + new(Pt) { Gift = true, Species = 133, Level = 20, Location = 010, GroundTile = Max_Pt, }, // Eevee @ Hearthome City + new(Pt) { Gift = true, Species = 137, Level = 25, Location = 012, GroundTile = Max_Pt, }, // Porygon @ Veilstone City new(Pt) { Gift = true, Species = 175, Level = 01, EggLocation = 2011 }, // Togepi Egg from Cynthia new(DP) { Gift = true, Species = 440, Level = 01, EggLocation = 2009 }, // Happiny Egg from Traveling Man new(DPPt) { Gift = true, Species = 447, Level = 01, EggLocation = 2010, }, // Riolu Egg from Riley @@ -232,95 +233,95 @@ static Encounters4() // Stationary new(DP) { Species = 425, Level = 22, Location = 47, }, // Drifloon @ Valley Windworks new(Pt) { Species = 425, Level = 15, Location = 47, }, // Drifloon @ Valley Windworks - new(DP) { Species = 479, Level = 15, Location = 70, TypeEncounter = EncounterType.Building_EnigmaStone, }, // Rotom @ Old Chateau - new(Pt) { Species = 479, Level = 20, Location = 70, TypeEncounter = EncounterType.Building_EnigmaStone, }, // Rotom @ Old Chateau + new(DP) { Species = 479, Level = 15, Location = 70, GroundTile = Building, }, // Rotom @ Old Chateau + new(Pt) { Species = 479, Level = 20, Location = 70, GroundTile = Building, }, // Rotom @ Old Chateau new(DPPt) { Species = 442, Level = 25, Location = 24 }, // Spiritomb @ Route 209 // Stationary Legendary - new(Pt) { Species = 377, Level = 30, Location = 125, TypeEncounter = EncounterType.Cave_HallOfOrigin, }, // Regirock @ Rock Peak Ruins - new(Pt) { Species = 378, Level = 30, Location = 124, TypeEncounter = EncounterType.Cave_HallOfOrigin, }, // Regice @ Iceberg Ruins - new(Pt) { Species = 379, Level = 30, Location = 123, TypeEncounter = EncounterType.Cave_HallOfOrigin, }, // Registeel @ Iron Ruins - new(DPPt) { Species = 480, Level = 50, Location = 089, TypeEncounter = EncounterType.Cave_HallOfOrigin, }, // Uxie @ Acuity Cavern - new(DPPt) { Species = 482, Level = 50, Location = 088, TypeEncounter = EncounterType.Cave_HallOfOrigin, }, // Azelf @ Valor Cavern - new(D ) { Species = 483, Level = 47, Location = 051, TypeEncounter = EncounterType.DialgaPalkia }, // Dialga @ Spear Pillar - new( P) { Species = 484, Level = 47, Location = 051, TypeEncounter = EncounterType.DialgaPalkia }, // Palkia @ Spear Pillar - new(Pt) { Species = 483, Level = 70, Location = 051, TypeEncounter = EncounterType.DialgaPalkia }, // Dialga @ Spear Pillar - new(Pt) { Species = 484, Level = 70, Location = 051, TypeEncounter = EncounterType.DialgaPalkia }, // Palkia @ Spear Pillar - new(DP) { Species = 485, Level = 70, Location = 084, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Heatran @ Stark Mountain - new(Pt) { Species = 485, Level = 50, Location = 084, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Heatran @ Stark Mountain - new(DP) { Species = 486, Level = 70, Location = 064, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Regigigas @ Snowpoint Temple - new(Pt) { Species = 486, Level = 01, Location = 064, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Regigigas @ Snowpoint Temple - new(DP) { Species = 487, Level = 70, Location = 062, TypeEncounter = EncounterType.Cave_HallOfOrigin, Form = 0, }, // Giratina @ Turnback Cave - new(Pt) { Species = 487, Level = 47, Location = 062, TypeEncounter = EncounterType.Cave_HallOfOrigin, Form = 0, }, // Giratina @ Turnback Cave - new(Pt) { Species = 487, Level = 47, Location = 117, TypeEncounter = EncounterType.DistortionWorld_Pt,Form = 1, HeldItem = 112 }, // Giratina @ Distortion World + new(Pt) { Species = 377, Level = 30, Location = 125, GroundTile = Cave, }, // Regirock @ Rock Peak Ruins + new(Pt) { Species = 378, Level = 30, Location = 124, GroundTile = Cave, }, // Regice @ Iceberg Ruins + new(Pt) { Species = 379, Level = 30, Location = 123, GroundTile = Cave, }, // Registeel @ Iron Ruins + new(DPPt) { Species = 480, Level = 50, Location = 089, GroundTile = Cave, }, // Uxie @ Acuity Cavern + new(DPPt) { Species = 482, Level = 50, Location = 088, GroundTile = Cave, }, // Azelf @ Valor Cavern + new(D ) { Species = 483, Level = 47, Location = 051, GroundTile = Rock }, // Dialga @ Spear Pillar + new( P) { Species = 484, Level = 47, Location = 051, GroundTile = Rock }, // Palkia @ Spear Pillar + new(Pt) { Species = 483, Level = 70, Location = 051, GroundTile = Rock }, // Dialga @ Spear Pillar + new(Pt) { Species = 484, Level = 70, Location = 051, GroundTile = Rock }, // Palkia @ Spear Pillar + new(DP) { Species = 485, Level = 70, Location = 084, GroundTile = Cave }, // Heatran @ Stark Mountain + new(Pt) { Species = 485, Level = 50, Location = 084, GroundTile = Cave }, // Heatran @ Stark Mountain + new(DP) { Species = 486, Level = 70, Location = 064, GroundTile = Cave }, // Regigigas @ Snowpoint Temple + new(Pt) { Species = 486, Level = 01, Location = 064, GroundTile = Cave }, // Regigigas @ Snowpoint Temple + new(DP) { Species = 487, Level = 70, Location = 062, GroundTile = Cave, Form = 0, }, // Giratina @ Turnback Cave + new(Pt) { Species = 487, Level = 47, Location = 062, GroundTile = Cave, Form = 0, }, // Giratina @ Turnback Cave + new(Pt) { Species = 487, Level = 47, Location = 117, GroundTile = Distortion,Form = 1, HeldItem = 112 }, // Giratina @ Distortion World // Event - new(DP) { Species = 491, Level = 40, Location = 079, TypeEncounter = EncounterType.TallGrass }, // Darkrai @ Newmoon Island (Unreleased in Diamond and Pearl) - new(Pt) { Species = 491, Level = 50, Location = 079, TypeEncounter = EncounterType.TallGrass }, // Darkrai @ Newmoon Island + new(DP) { Species = 491, Level = 40, Location = 079, GroundTile = Grass }, // Darkrai @ Newmoon Island (Unreleased in Diamond and Pearl) + new(Pt) { Species = 491, Level = 50, Location = 079, GroundTile = Grass }, // Darkrai @ Newmoon Island new(Pt) { Species = 492, Form = 0, Level = 30, Location = 063, Fateful = true }, // Shaymin @ Flower Paradise new(DP) { Species = 492, Form = 0, Level = 30, Location = 063, Fateful = false }, // Shaymin @ Flower Paradise (Unreleased in Diamond and Pearl) - new(DPPt) { Species = 493, Form = 0, Level = 80, Location = 086, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Arceus @ Hall of Origin (Unreleased) + new(DPPt) { Species = 493, Form = 0, Level = 80, Location = 086, GroundTile = Cave }, // Arceus @ Hall of Origin (Unreleased) // Roamers - new(DPPt) { Roaming = true, Species = 481, Level = 50, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing }, // Mesprit - new(DPPt) { Roaming = true, Species = 488, Level = 50, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing }, // Cresselia - new(Pt) { Roaming = true, Species = 144, Level = 60, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing }, // Articuno - new(Pt) { Roaming = true, Species = 145, Level = 60, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing }, // Zapdos - new(Pt) { Roaming = true, Species = 146, Level = 60, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing }, // Moltres + new(DPPt) { Roaming = true, Species = 481, Level = 50, GroundTile = Grass | Water }, // Mesprit + new(DPPt) { Roaming = true, Species = 488, Level = 50, GroundTile = Grass | Water }, // Cresselia + new(Pt) { Roaming = true, Species = 144, Level = 60, GroundTile = Grass | Water }, // Articuno + new(Pt) { Roaming = true, Species = 145, Level = 60, GroundTile = Grass | Water }, // Zapdos + new(Pt) { Roaming = true, Species = 146, Level = 60, GroundTile = Grass | Water }, // Moltres }; private static readonly EncounterStatic4[] Encounter_HGSS = { // Starters - new(HGSS) { Gift = true, Species = 001, Level = 05, Location = 138, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Bulbasaur @ Pallet Town - new(HGSS) { Gift = true, Species = 004, Level = 05, Location = 138, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Charmander - new(HGSS) { Gift = true, Species = 007, Level = 05, Location = 138, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Squirtle - new(HGSS) { Gift = true, Species = 152, Level = 05, Location = 126, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Chikorita @ New Bark Town - new(HGSS) { Gift = true, Species = 155, Level = 05, Location = 126, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Cyndaquil - new(HGSS) { Gift = true, Species = 158, Level = 05, Location = 126, TypeEncounter = EncounterType.Starter_Fossil_Gift_DP }, // Totodile - new(HGSS) { Gift = true, Species = 252, Level = 05, Location = 148, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Treecko @ Saffron City - new(HGSS) { Gift = true, Species = 255, Level = 05, Location = 148, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Torchic - new(HGSS) { Gift = true, Species = 258, Level = 05, Location = 148, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Mudkip + new(HGSS) { Gift = true, Species = 001, Level = 05, Location = 138, GroundTile = Max_Pt }, // Bulbasaur @ Pallet Town + new(HGSS) { Gift = true, Species = 004, Level = 05, Location = 138, GroundTile = Max_Pt }, // Charmander + new(HGSS) { Gift = true, Species = 007, Level = 05, Location = 138, GroundTile = Max_Pt }, // Squirtle + new(HGSS) { Gift = true, Species = 152, Level = 05, Location = 126, GroundTile = Max_DP }, // Chikorita @ New Bark Town + new(HGSS) { Gift = true, Species = 155, Level = 05, Location = 126, GroundTile = Max_DP }, // Cyndaquil + new(HGSS) { Gift = true, Species = 158, Level = 05, Location = 126, GroundTile = Max_DP }, // Totodile + new(HGSS) { Gift = true, Species = 252, Level = 05, Location = 148, GroundTile = Max_Pt }, // Treecko @ Saffron City + new(HGSS) { Gift = true, Species = 255, Level = 05, Location = 148, GroundTile = Max_Pt }, // Torchic + new(HGSS) { Gift = true, Species = 258, Level = 05, Location = 148, GroundTile = Max_Pt }, // Mudkip // Fossils @ Pewter City - new(HGSS) { Gift = true, Species = 138, Level = 20, Location = 140, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Omanyte - new(HGSS) { Gift = true, Species = 140, Level = 20, Location = 140, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Kabuto - new(HGSS) { Gift = true, Species = 142, Level = 20, Location = 140, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Aerodactyl - new(HGSS) { Gift = true, Species = 345, Level = 20, Location = 140, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Lileep - new(HGSS) { Gift = true, Species = 347, Level = 20, Location = 140, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Anorith - new(HGSS) { Gift = true, Species = 408, Level = 20, Location = 140, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Cranidos - new(HGSS) { Gift = true, Species = 410, Level = 20, Location = 140, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Shieldon + new(HGSS) { Gift = true, Species = 138, Level = 20, Location = 140, GroundTile = Max_Pt, }, // Omanyte + new(HGSS) { Gift = true, Species = 140, Level = 20, Location = 140, GroundTile = Max_Pt, }, // Kabuto + new(HGSS) { Gift = true, Species = 142, Level = 20, Location = 140, GroundTile = Max_Pt, }, // Aerodactyl + new(HGSS) { Gift = true, Species = 345, Level = 20, Location = 140, GroundTile = Max_Pt, }, // Lileep + new(HGSS) { Gift = true, Species = 347, Level = 20, Location = 140, GroundTile = Max_Pt, }, // Anorith + new(HGSS) { Gift = true, Species = 408, Level = 20, Location = 140, GroundTile = Max_Pt, }, // Cranidos + new(HGSS) { Gift = true, Species = 410, Level = 20, Location = 140, GroundTile = Max_Pt, }, // Shieldon // Gift - new(HGSS) { Gift = true, Species = 072, Level = 15, Location = 130, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Tentacool @ Cianwood City - new(HGSS) { Gift = true, Species = 133, Level = 05, Location = 131, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Eevee @ Goldenrod City - new(HGSS) { Gift = true, Species = 147, Level = 15, Location = 222, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, Moves = new[] {245} }, // Dratini @ Dragon's Den (ExtremeSpeed) - new(HGSS) { Gift = true, Species = 236, Level = 10, Location = 216, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, }, // Tyrogue @ Mt. Mortar + new(HGSS) { Gift = true, Species = 072, Level = 15, Location = 130, GroundTile = Max_Pt }, // Tentacool @ Cianwood City + new(HGSS) { Gift = true, Species = 133, Level = 05, Location = 131, GroundTile = Max_Pt }, // Eevee @ Goldenrod City + new(HGSS) { Gift = true, Species = 147, Level = 15, Location = 222, GroundTile = Max_Pt, Moves = new[] {245} }, // Dratini @ Dragon's Den (ExtremeSpeed) + new(HGSS) { Gift = true, Species = 236, Level = 10, Location = 216, GroundTile = Max_Pt, }, // Tyrogue @ Mt. Mortar new(HGSS) { Gift = true, Species = 175, Level = 01, EggLocation = 2013, Moves = new[] {326} }, // Togepi Egg from Mr. Pokemon (Extrasensory as Egg move) new(HGSS) { Gift = true, Species = 179, Level = 01, EggLocation = 2014, }, // Mareep Egg from Primo new(HGSS) { Gift = true, Species = 194, Level = 01, EggLocation = 2014, }, // Wooper Egg from Primo new(HGSS) { Gift = true, Species = 218, Level = 01, EggLocation = 2014, }, // Slugma Egg from Primo // Celadon City Game Corner - new(HGSS) { Gift = true, Species = 122, Level = 15, Location = 144, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Mr. Mime - new(HGSS) { Gift = true, Species = 133, Level = 15, Location = 144, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Eevee - new(HGSS) { Gift = true, Species = 137, Level = 15, Location = 144, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Porygon + new(HGSS) { Gift = true, Species = 122, Level = 15, Location = 144, GroundTile = Max_Pt }, // Mr. Mime + new(HGSS) { Gift = true, Species = 133, Level = 15, Location = 144, GroundTile = Max_Pt }, // Eevee + new(HGSS) { Gift = true, Species = 137, Level = 15, Location = 144, GroundTile = Max_Pt }, // Porygon // Goldenrod City Game Corner - new(HGSS) { Gift = true, Species = 063, Level = 15, Location = 131, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Abra - new(HG ) { Gift = true, Species = 023, Level = 15, Location = 131, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Ekans - new( SS) { Gift = true, Species = 027, Level = 15, Location = 131, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Sandshrew - new(HGSS) { Gift = true, Species = 147, Level = 15, Location = 131, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Dratini + new(HGSS) { Gift = true, Species = 063, Level = 15, Location = 131, GroundTile = Max_Pt }, // Abra + new(HG ) { Gift = true, Species = 023, Level = 15, Location = 131, GroundTile = Max_Pt }, // Ekans + new( SS) { Gift = true, Species = 027, Level = 15, Location = 131, GroundTile = Max_Pt }, // Sandshrew + new(HGSS) { Gift = true, Species = 147, Level = 15, Location = 131, GroundTile = Max_Pt }, // Dratini // Team Rocket HQ Trap Floor - new(HGSS) { Species = 100, Level = 23, Location = 213, TypeEncounter = EncounterType.Building_EnigmaStone, }, // Voltorb - new(HGSS) { Species = 074, Level = 21, Location = 213, TypeEncounter = EncounterType.Building_EnigmaStone, }, // Geodude - new(HGSS) { Species = 109, Level = 21, Location = 213, TypeEncounter = EncounterType.Building_EnigmaStone, }, // Koffing + new(HGSS) { Species = 100, Level = 23, Location = 213, GroundTile = Building, }, // Voltorb + new(HGSS) { Species = 074, Level = 21, Location = 213, GroundTile = Building, }, // Geodude + new(HGSS) { Species = 109, Level = 21, Location = 213, GroundTile = Building, }, // Koffing // Stationary - new(HGSS) { Species = 130, Level = 30, Location = 135, TypeEncounter = EncounterType.Surfing_Fishing, Shiny = Shiny.Always }, // Gyarados @ Lake of Rage - new(HGSS) { Species = 131, Level = 20, Location = 210, TypeEncounter = EncounterType.Surfing_Fishing, }, // Lapras @ Union Cave Friday Only - new(HGSS) { Species = 101, Level = 23, Location = 213, TypeEncounter = EncounterType.Building_EnigmaStone, }, // Electrode @ Team Rocket HQ + new(HGSS) { Species = 130, Level = 30, Location = 135, GroundTile = Water, Shiny = Shiny.Always }, // Gyarados @ Lake of Rage + new(HGSS) { Species = 131, Level = 20, Location = 210, GroundTile = Water, }, // Lapras @ Union Cave Friday Only + new(HGSS) { Species = 101, Level = 23, Location = 213, GroundTile = Building, }, // Electrode @ Team Rocket HQ new(HGSS) { Species = 143, Level = 50, Location = 159, }, // Snorlax @ Route 11 new(HGSS) { Species = 143, Level = 50, Location = 160, }, // Snorlax @ Route 12 new(HGSS) { Species = 185, Level = 20, Location = 184, }, // Sudowoodo @ Route 36, Encounter does not have type @@ -334,37 +335,37 @@ static Encounters4() Nature = Nature.Naughty, Location = 214, Moves = new[] { 344, 270, 207, 220 }, - TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, + GroundTile = Max_Pt, Shiny = Shiny.Never }, // Stationary Legendary - new(HGSS) { Species = 144, Level = 50, Location = 203, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Articuno @ Seafoam Islands + new(HGSS) { Species = 144, Level = 50, Location = 203, GroundTile = Cave }, // Articuno @ Seafoam Islands new(HGSS) { Species = 145, Level = 50, Location = 158, }, // Zapdos @ Route 10 - new(HGSS) { Species = 146, Level = 50, Location = 219, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Moltres @ Mt. Silver Cave - new(HGSS) { Species = 150, Level = 70, Location = 199, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Mewtwo @ Cerulean Cave + new(HGSS) { Species = 146, Level = 50, Location = 219, GroundTile = Cave }, // Moltres @ Mt. Silver Cave + new(HGSS) { Species = 150, Level = 70, Location = 199, GroundTile = Cave }, // Mewtwo @ Cerulean Cave new(HGSS) { Species = 245, Level = 40, Location = 173, }, // Suicune @ Route 25 - new(HGSS) { Species = 245, Level = 40, Location = 206, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Suicune @ Burned Tower - new( SS) { Species = 249, Level = 45, Location = 218, TypeEncounter = EncounterType.Surfing_Fishing }, // Lugia @ Whirl Islands - new(HG ) { Species = 249, Level = 70, Location = 218, TypeEncounter = EncounterType.Surfing_Fishing }, // Lugia @ Whirl Islands - new(HG ) { Species = 250, Level = 45, Location = 205, TypeEncounter = EncounterType.Building_EnigmaStone }, // Ho-Oh @ Bell Tower - new( SS) { Species = 250, Level = 70, Location = 205, TypeEncounter = EncounterType.Building_EnigmaStone }, // Ho-Oh @ Bell Tower - new( SS) { Species = 380, Level = 40, Location = 140, TypeEncounter = EncounterType.Building_EnigmaStone }, // Latias @ Pewter City - new(HG ) { Species = 381, Level = 40, Location = 140, TypeEncounter = EncounterType.Building_EnigmaStone }, // Latios @ Pewter City - new(HG ) { Species = 382, Level = 50, Location = 232, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Kyogre @ Embedded Tower - new( SS) { Species = 383, Level = 50, Location = 232, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Groudon @ Embedded Tower - new(HGSS) { Species = 384, Level = 50, Location = 232, TypeEncounter = EncounterType.Cave_HallOfOrigin }, // Rayquaza @ Embedded Tower - new(HGSS) { Species = 483, Level = 01, Location = 231, Gift = true, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Dialga @ Sinjoh Ruins - new(HGSS) { Species = 484, Level = 01, Location = 231, Gift = true, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio }, // Palkia @ Sinjoh Ruins - new(HGSS) { Species = 487, Level = 01, Location = 231, Gift = true, TypeEncounter = EncounterType.Starter_Fossil_Gift_Pt_DPTrio, Form = 1, HeldItem = 112 }, // Giratina @ Sinjoh Ruins + new(HGSS) { Species = 245, Level = 40, Location = 206, GroundTile = Cave }, // Suicune @ Burned Tower + new( SS) { Species = 249, Level = 45, Location = 218, GroundTile = Water }, // Lugia @ Whirl Islands + new(HG ) { Species = 249, Level = 70, Location = 218, GroundTile = Water }, // Lugia @ Whirl Islands + new(HG ) { Species = 250, Level = 45, Location = 205, GroundTile = Building }, // Ho-Oh @ Bell Tower + new( SS) { Species = 250, Level = 70, Location = 205, GroundTile = Building }, // Ho-Oh @ Bell Tower + new( SS) { Species = 380, Level = 40, Location = 140, GroundTile = Building }, // Latias @ Pewter City + new(HG ) { Species = 381, Level = 40, Location = 140, GroundTile = Building }, // Latios @ Pewter City + new(HG ) { Species = 382, Level = 50, Location = 232, GroundTile = Cave }, // Kyogre @ Embedded Tower + new( SS) { Species = 383, Level = 50, Location = 232, GroundTile = Cave }, // Groudon @ Embedded Tower + new(HGSS) { Species = 384, Level = 50, Location = 232, GroundTile = Cave }, // Rayquaza @ Embedded Tower + new(HGSS) { Species = 483, Level = 01, Location = 231, Gift = true, GroundTile = Max_Pt }, // Dialga @ Sinjoh Ruins + new(HGSS) { Species = 484, Level = 01, Location = 231, Gift = true, GroundTile = Max_Pt }, // Palkia @ Sinjoh Ruins + new(HGSS) { Species = 487, Level = 01, Location = 231, Gift = true, GroundTile = Max_Pt, Form = 1, HeldItem = 112 }, // Giratina @ Sinjoh Ruins // Johto Roamers - new(HGSS) { Roaming = true, Species = 243, Level = 40, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing, }, // Raikou - new(HGSS) { Roaming = true, Species = 244, Level = 40, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing, }, // Entei + new(HGSS) { Roaming = true, Species = 243, Level = 40, GroundTile = Grass | Water, }, // Raikou + new(HGSS) { Roaming = true, Species = 244, Level = 40, GroundTile = Grass | Water, }, // Entei // Kanto Roamers - new(HG ) { Roaming = true, Species = 380, Level = 35, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing, }, // Latias - new( SS) { Roaming = true, Species = 381, Level = 35, TypeEncounter = EncounterType.TallGrass | EncounterType.Surfing_Fishing, }, // Latios + new(HG ) { Roaming = true, Species = 380, Level = 35, GroundTile = Grass | Water, }, // Latias + new( SS) { Roaming = true, Species = 381, Level = 35, GroundTile = Grass | Water, }, // Latios }; #endregion #region Trade Tables diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot4.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot4.cs index 2d7e8037e..fccf4d562 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot4.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/EncounterSlot4.cs @@ -4,10 +4,10 @@ namespace PKHeX.Core /// Encounter Slot found in . /// /// - public sealed record EncounterSlot4 : EncounterSlot, IMagnetStatic, INumberedSlot, IEncounterTypeTile + public sealed record EncounterSlot4 : EncounterSlot, IMagnetStatic, INumberedSlot, IGroundTypeTile { public override int Generation => 4; - public EncounterType TypeEncounter => ((EncounterArea4)Area).TypeEncounter; + public GroundTilePermission GroundTile => ((EncounterArea4)Area).GroundTile; public int StaticIndex { get; } public int MagnetPullIndex { get; } @@ -27,7 +27,7 @@ public EncounterSlot4(EncounterArea4 area, int species, int form, int min, int m StaticCount = stc; } - protected override void SetFormatSpecificData(PKM pk) => ((PK4)pk).EncounterType = TypeEncounter.GetIndex(); + protected override void SetFormatSpecificData(PKM pk) => ((PK4)pk).GroundTile = GroundTile.GetIndex(); public override EncounterMatchRating GetMatchRating(PKM pkm) { diff --git a/PKHeX.Core/Legality/Encounters/EncounterSlot/IEncounterTypeTile.cs b/PKHeX.Core/Legality/Encounters/EncounterSlot/IGroundTypeTile.cs similarity index 67% rename from PKHeX.Core/Legality/Encounters/EncounterSlot/IEncounterTypeTile.cs rename to PKHeX.Core/Legality/Encounters/EncounterSlot/IGroundTypeTile.cs index 0a67a53c8..fb549d7df 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterSlot/IEncounterTypeTile.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterSlot/IGroundTypeTile.cs @@ -7,20 +7,20 @@ namespace PKHeX.Core /// /// /// - public interface IEncounterTypeTile + public interface IGroundTypeTile { /// /// Tile Type the was obtained on. /// - EncounterType TypeEncounter { get; } + GroundTilePermission GroundTile { get; } } - public static class EncounterTypeTileExtensions + public static class GroundTypeTileExtensions { /// /// Gets if the resulting will still have a value depending on the current . /// - /// Generation 6 no longer stores this value. - public static bool HasTypeEncounter(this IEncounterTypeTile _, int format) => format is 4 or 5; + /// Generation 7 no longer stores this value. + public static bool HasGroundTile(this IGroundTypeTile _, int format) => format is (4 or 5 or 6); } } diff --git a/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic4.cs b/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic4.cs index 03e5c7392..bad6e71d8 100644 --- a/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic4.cs +++ b/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic4.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using static PKHeX.Core.EncounterType; +using static PKHeX.Core.GroundTilePermission; namespace PKHeX.Core { @@ -8,15 +8,15 @@ namespace PKHeX.Core /// Generation 4 Static Encounter /// /// - public sealed record EncounterStatic4 : EncounterStatic, IEncounterTypeTile + public sealed record EncounterStatic4 : EncounterStatic, IGroundTypeTile { public override int Generation => 4; /// Indicates if the encounter is a Roamer (variable met location) public bool Roaming { get; init; } - /// values permitted for the encounter. - public EncounterType TypeEncounter { get; init; } = None; + /// values permitted for the encounter. + public GroundTilePermission GroundTile { get; init; } = None; public EncounterStatic4(GameVersion game) : base(game) { } @@ -29,7 +29,7 @@ protected override bool IsMatchLocation(PKM pkm) if (pkm is not G4PKM pk4) return true; - var locs = GetRoamLocations(Species, pk4.EncounterType); + var locs = GetRoamLocations(Species, pk4.GroundTile); return locs.Contains(pk4.Met_Location); } @@ -95,17 +95,17 @@ protected override bool IsMatchPartial(PKM pkm) protected override void SetMetData(PKM pk, int level, DateTime today) { var pk4 = (PK4)pk; - var type = pk4.EncounterType = TypeEncounter.GetIndex(); + var type = pk4.GroundTile = GroundTile.GetIndex(); pk.Met_Location = Roaming ? GetRoamLocations(Species, type)[0] : Location; pk.Met_Level = level; pk.MetDate = today; } - private static int[] GetRoamLocations(int species, int type) => species switch + private static int[] GetRoamLocations(int species, GroundTileType type) => species switch { - 481 or 488 or 144 or 145 or 146 => 1 << type == (int)TallGrass ? Roaming_MetLocation_DPPt_Grass : Roaming_MetLocation_DPPt_Surf, - 243 or 244 => 1 << type == (int)TallGrass ? Roaming_MetLocation_HGSS_Johto_Grass : Roaming_MetLocation_HGSS_Johto_Surf, - 380 or 381 => 1 << type == (int)TallGrass ? Roaming_MetLocation_HGSS_Kanto_Grass : Roaming_MetLocation_HGSS_Kanto_Surf, + 481 or 488 or 144 or 145 or 146 => type == GroundTileType.Grass ? Roaming_MetLocation_DPPt_Grass : Roaming_MetLocation_DPPt_Surf, + 243 or 244 => type == GroundTileType.Grass ? Roaming_MetLocation_HGSS_Johto_Grass : Roaming_MetLocation_HGSS_Johto_Surf, + 380 or 381 => type == GroundTileType.Grass ? Roaming_MetLocation_HGSS_Kanto_Grass : Roaming_MetLocation_HGSS_Kanto_Surf, _ => throw new IndexOutOfRangeException(nameof(species)), }; diff --git a/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator4.cs b/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator4.cs index d379f308b..efbfd0538 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator4.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/ByGeneration/EncounterGenerator4.cs @@ -22,7 +22,7 @@ public static IEnumerable GetEncounters(PKM pkm, LegalInfo info) { if (!info.PIDIV.Type.IsCompatible4(z, pkm)) deferredPIDIV.Add(z); - else if (pkm.Format <= 6 && !(z is IEncounterTypeTile t ? t.TypeEncounter.Contains(pkm.EncounterType) : pkm.EncounterType == 0)) + else if (pkm is IGroundTile e && !(z is IGroundTypeTile t ? t.GroundTile.Contains(e.GroundTile) : e.GroundTile == 0)) deferredEType.Add(z); else yield return z; diff --git a/PKHeX.Core/Legality/Encounters/Generator/Specific/EncounterSlotGenerator.cs b/PKHeX.Core/Legality/Encounters/Generator/Specific/EncounterSlotGenerator.cs index 6e6872249..8fe4ce8ea 100644 --- a/PKHeX.Core/Legality/Encounters/Generator/Specific/EncounterSlotGenerator.cs +++ b/PKHeX.Core/Legality/Encounters/Generator/Specific/EncounterSlotGenerator.cs @@ -24,7 +24,7 @@ public static class EncounterSlotGenerator public static IEnumerable GetPossible(PKM pkm, IReadOnlyList chain, GameVersion gameSource = Any) { var possibleAreas = GetAreasByGame(pkm, gameSource); - return possibleAreas.SelectMany(area => area.Slots).Where(z => chain.Any(v => v.Species == z.Species)); + return possibleAreas.SelectMany(z => z.GetSpecies(chain)); } private static IEnumerable GetAreasByGame(PKM pkm, GameVersion gameSource) => gameSource switch diff --git a/PKHeX.Core/Legality/Encounters/Information/EncounterSuggestion.cs b/PKHeX.Core/Legality/Encounters/Information/EncounterSuggestion.cs index 2d7214594..52bfd8a07 100644 --- a/PKHeX.Core/Legality/Encounters/Information/EncounterSuggestion.cs +++ b/PKHeX.Core/Legality/Encounters/Information/EncounterSuggestion.cs @@ -203,7 +203,7 @@ public EncounterSuggestionData(PKM pkm, int met, int lvl) public int LevelMax { get; } public int GetSuggestedMetLevel(PKM pkm) => EncounterSuggestion.GetSuggestedMetLevel(pkm, LevelMin); - public int GetSuggestedEncounterType() => Encounter is IEncounterTypeTile t ? t.TypeEncounter.GetIndex() : 0; - public bool HasEncounterType(int format) => Encounter is IEncounterTypeTile t && t.HasTypeEncounter(format); + public GroundTileType GetSuggestedGroundTile() => Encounter is IGroundTypeTile t ? t.GroundTile.GetIndex() : 0; + public bool HasGroundTile(int format) => Encounter is IGroundTypeTile t && t.HasGroundTile(format); } } diff --git a/PKHeX.Core/Legality/Enums/EncounterType.cs b/PKHeX.Core/Legality/Enums/EncounterType.cs deleted file mode 100644 index 37188a083..000000000 --- a/PKHeX.Core/Legality/Enums/EncounterType.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; - -namespace PKHeX.Core -{ - /// - /// Tile type the was encountered from. - /// - /// - /// Used in Generation 4 games, this value is set depending on what type of overworld tile the player is standing on when the is obtained. - /// Some locations have multiple tile types, requiring multiple values possible. - /// - [Flags] - public enum EncounterType - { - Undefined = 0, - None = 1 << 00, - RockSmash = 1 << 01, - TallGrass = 1 << 02, - DialgaPalkia = 1 << 04, - Cave_HallOfOrigin = 1 << 05, - Surfing_Fishing = 1 << 07, - Building_EnigmaStone = 1 << 09, - MarshSafari = 1 << 10, - Starter_Fossil_Gift_DP = 1 << 12, - DistortionWorld_Pt = 1 << 23, - Starter_Fossil_Gift_Pt_DPTrio = 1 << 24, - } - - public static class EncounterTypeExtension - { - public static bool Contains(this EncounterType g1, int g2) => (g1 & (EncounterType)(1 << g2)) != 0; - - public static int GetIndex(this EncounterType g) - { - int val = (int) g; - for (int i = 0; i < 8 * sizeof(EncounterType); i++) - { - val >>= 1; - if (val == 0) - return i; - } - return 0; - } - } -} diff --git a/PKHeX.Core/Legality/Enums/GroundTilePermission.cs b/PKHeX.Core/Legality/Enums/GroundTilePermission.cs new file mode 100644 index 000000000..65692d039 --- /dev/null +++ b/PKHeX.Core/Legality/Enums/GroundTilePermission.cs @@ -0,0 +1,68 @@ +using System; + +namespace PKHeX.Core +{ + /// + /// Permitted values an encounter can be acquired with. + /// + /// + /// Some locations have multiple tile types that can proc an encounter, requiring multiple values possible. + /// + [Flags] + public enum GroundTilePermission + { + Undefined = 0, + None = 1 << 00, // No animation for the tile + Sand = 1 << 01, // Obtainable only via HG/SS + Grass = 1 << 02, + Puddle = 1 << 03, // No encounters from this tile type + Rock = 1 << 04, + Cave = 1 << 05, + Snow = 1 << 06, // No encounters from this tile type + Water = 1 << 07, + Ice = 1 << 08, // No encounters from this tile type + Building = 1 << 09, + Marsh = 1 << 10, + Bridge = 1 << 11, // No encounters from this tile type + Max_DP = 1 << 12, // Unspecific, catch-all for DP undefined tiles. + + // added tile types in Pt + // no encounters from these tile types + Elite4_1 = 1 << 12, // Elite Four Room #1 + Elite4_2 = 1 << 13, // Elite Four Room #2 + Elite4_3 = 1 << 14, // Elite Four Room #3 + Elite4_4 = 1 << 15, // Elite Four Room #4 + Elite4_M = 1 << 16, // Elite Four Champion Room + DistortionSideways = 1 << 17, // Distortion World (Not Giratina) + BattleTower = 1 << 18, + BattleFactory = 1 << 19, + BattleArcade = 1 << 20, + BattleCastle = 1 << 21, + BattleHall = 1 << 22, + + Distortion = 1 << 23, + Max_Pt = 1 << 24, // Unspecific, catch-all for Pt undefined tiles. + } + + public static class GroundTilePermissionExtensions + { + public static bool Contains(this GroundTilePermission g1, GroundTileType g2) => (g1 & (GroundTilePermission)(1 << (int)g2)) != 0; + + /// + /// Grabs the highest set bit from the tile value. + /// + /// Tile bit-permission value + /// Bit index + public static GroundTileType GetIndex(this GroundTilePermission g) + { + int val = (int) g; + for (byte i = 0; i < 8 * sizeof(GroundTilePermission); i++) + { + val >>= 1; + if (val == 0) + return (GroundTileType)i; + } + return 0; + } + } +} diff --git a/PKHeX.Core/Legality/Enums/SlotType.cs b/PKHeX.Core/Legality/Enums/SlotType.cs index a3e263b89..585a1a487 100644 --- a/PKHeX.Core/Legality/Enums/SlotType.cs +++ b/PKHeX.Core/Legality/Enums/SlotType.cs @@ -6,7 +6,7 @@ namespace PKHeX.Core /// Wild Encounter data Type /// /// - /// Different from , this corresponds to the method that the may be encountered. + /// Different from , this corresponds to the method that the may be encountered. [Flags] #pragma warning disable RCS1191 // Declare enum value as combination of names. public enum SlotType : byte diff --git a/PKHeX.Core/Legality/LegalityAnalysis.cs b/PKHeX.Core/Legality/LegalityAnalysis.cs index a75124c2c..194ad5bd6 100644 --- a/PKHeX.Core/Legality/LegalityAnalysis.cs +++ b/PKHeX.Core/Legality/LegalityAnalysis.cs @@ -297,7 +297,7 @@ private void UpdateChecks() var format = pkm.Format; if (format is 4 or 5 or 6) - Gen4EncounterType.Verify(this); // Gen 6->7 transfer deletes encounter type data + Gen4GroundTile.Verify(this); // Gen 6->7 transfer deletes encounter type data if (format < 6) return; diff --git a/PKHeX.Core/Legality/LegalityAnalyzers.cs b/PKHeX.Core/Legality/LegalityAnalyzers.cs index 5b4943995..edb2a7873 100644 --- a/PKHeX.Core/Legality/LegalityAnalyzers.cs +++ b/PKHeX.Core/Legality/LegalityAnalyzers.cs @@ -16,7 +16,7 @@ internal static class LegalityAnalyzers public static readonly MedalVerifier Medal = new(); public static readonly RibbonVerifier Ribbon = new(); public static readonly ItemVerifier Item = new(); - public static readonly EncounterTypeVerifier Gen4EncounterType = new(); + public static readonly GroundTileVerifier Gen4GroundTile = new(); public static readonly HyperTrainingVerifier HyperTraining = new(); public static readonly GenderVerifier GenderValues = new(); public static readonly PIDVerifier PIDEC = new(); diff --git a/PKHeX.Core/Legality/RNG/MethodFinder.cs b/PKHeX.Core/Legality/RNG/MethodFinder.cs index 984143120..70359dffe 100644 --- a/PKHeX.Core/Legality/RNG/MethodFinder.cs +++ b/PKHeX.Core/Legality/RNG/MethodFinder.cs @@ -878,9 +878,9 @@ public static bool IsCompatible4(this PIDType val, IEncounterable encounter, PKM return true; if (val != ChainShiny) return false; - // Chain shiny with poke radar is only possible in DPPt in tall grass, safari zone do not allow pokeradar - // TypeEncounter TallGrass discard any cave or city - return pkm.IsShiny && !pkm.HGSS && sl.TypeEncounter == EncounterType.TallGrass && !Locations.IsSafariZoneLocation4(sl.Location); + // Chain shiny with poke radar is only possible in DPPt in grass, safari zone does not allow pokeradar + // TypeEncounter Grass discard any cave or city + return pkm.IsShiny && !pkm.HGSS && sl.GroundTile == GroundTilePermission.Grass && !Locations.IsSafariZoneLocation4(sl.Location); case PGT: // manaphy return IsG4ManaphyPIDValid(val, pkm); case PCD d when d.Gift.PK.PID != 1: diff --git a/PKHeX.Core/Legality/Verifiers/EncounterTypeVerifier.cs b/PKHeX.Core/Legality/Verifiers/EncounterTypeVerifier.cs deleted file mode 100644 index 4029e7aac..000000000 --- a/PKHeX.Core/Legality/Verifiers/EncounterTypeVerifier.cs +++ /dev/null @@ -1,19 +0,0 @@ -using static PKHeX.Core.LegalityCheckStrings; - -namespace PKHeX.Core -{ - /// - /// Verifies the . - /// - public sealed class EncounterTypeVerifier : Verifier - { - protected override CheckIdentifier Identifier => CheckIdentifier.Encounter; - - public override void Verify(LegalityAnalysis data) - { - var type = data.EncounterMatch is IEncounterTypeTile t ? t.TypeEncounter : EncounterType.None; - var result = !type.Contains(data.pkm.EncounterType) ? GetInvalid(LEncTypeMismatch) : GetValid(LEncTypeMatch); - data.AddLine(result); - } - } -} diff --git a/PKHeX.Core/Legality/Verifiers/GroundTileVerifier.cs b/PKHeX.Core/Legality/Verifiers/GroundTileVerifier.cs new file mode 100644 index 000000000..fa241ce84 --- /dev/null +++ b/PKHeX.Core/Legality/Verifiers/GroundTileVerifier.cs @@ -0,0 +1,21 @@ +using static PKHeX.Core.LegalityCheckStrings; + +namespace PKHeX.Core +{ + /// + /// Verifies the . + /// + public sealed class GroundTileVerifier : Verifier + { + protected override CheckIdentifier Identifier => CheckIdentifier.Encounter; + + public override void Verify(LegalityAnalysis data) + { + if (data.pkm is not IGroundTile e) + return; + var type = data.EncounterMatch is IGroundTypeTile t ? t.GroundTile : GroundTilePermission.None; + var result = !type.Contains(e.GroundTile) ? GetInvalid(LEncTypeMismatch) : GetValid(LEncTypeMatch); + data.AddLine(result); + } + } +} diff --git a/PKHeX.Core/Legality/Verifiers/MemoryPermissions.cs b/PKHeX.Core/Legality/Verifiers/MemoryPermissions.cs index 967419586..d25232220 100644 --- a/PKHeX.Core/Legality/Verifiers/MemoryPermissions.cs +++ b/PKHeX.Core/Legality/Verifiers/MemoryPermissions.cs @@ -157,7 +157,7 @@ private static bool GetCanKnowMove(PKM pkm, int move, int generation, IReadOnlyL private static bool GetCanBeCaptured(int species, IEnumerable area, IEnumerable statics) { - if (area.Any(loc => loc.Slots.Any(slot => slot.Species == species))) + if (area.Any(loc => loc.HasSpecies(species))) return true; if (statics.Any(enc => enc.Species == species && !enc.Gift)) return true; diff --git a/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs b/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs index d81b81ec2..30aef0c72 100644 --- a/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/NicknameVerifier.cs @@ -39,7 +39,7 @@ public override void Verify(LegalityAnalysis data) if (pkm.Format <= 7 && pkm.IsNicknamed) // can nickname afterwards { if (pkm.VC) - VerifyG1NicknameWithinBounds(data, nickname); + VerifyG1NicknameWithinBounds(data, nickname.AsSpan()); else if (enc is MysteryGift {IsEgg: false}) data.AddLine(Get(LEncGiftNicknamed, ParseSettings.NicknamedMysteryGift)); } @@ -264,7 +264,7 @@ private void VerifyNicknameTrade(LegalityAnalysis data, EncounterTrade t) } } - private void VerifyG1NicknameWithinBounds(LegalityAnalysis data, string str) + private void VerifyG1NicknameWithinBounds(LegalityAnalysis data, ReadOnlySpan str) { var pkm = data.pkm; if (StringConverter12.GetIsG1English(str)) diff --git a/PKHeX.Core/Legality/Verifiers/TrainerNameVerifier.cs b/PKHeX.Core/Legality/Verifiers/TrainerNameVerifier.cs index 740485383..4e43fa629 100644 --- a/PKHeX.Core/Legality/Verifiers/TrainerNameVerifier.cs +++ b/PKHeX.Core/Legality/Verifiers/TrainerNameVerifier.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System; using static PKHeX.Core.LegalityCheckStrings; namespace PKHeX.Core @@ -118,7 +118,7 @@ public void VerifyOTG1(LegalityAnalysis data) } } - VerifyG1OTWithinBounds(data, tr); + VerifyG1OTWithinBounds(data, tr.AsSpan()); if (pkm.OT_Gender == 1) { @@ -127,7 +127,7 @@ public void VerifyOTG1(LegalityAnalysis data) } } - private void VerifyG1OTWithinBounds(LegalityAnalysis data, string str) + private void VerifyG1OTWithinBounds(LegalityAnalysis data, ReadOnlySpan str) { if (StringConverter12.GetIsG1English(str)) { @@ -152,7 +152,12 @@ private void VerifyG1OTWithinBounds(LegalityAnalysis data, string str) private static bool IsOTNameSuspicious(string name) { - return SuspiciousOTNames.Any(name.StartsWith); + foreach (var s in SuspiciousOTNames) + { + if (s.StartsWith(name, StringComparison.InvariantCultureIgnoreCase)) + return true; + } + return false; } public static bool IsOTIDSuspicious(int tid16, int sid16) @@ -187,7 +192,13 @@ static bool IsNumber(char c) return (uint)(c - '0') <= 9; } - return str.Count(IsNumber); + int ctr = 0; + foreach (var c in str) + { + if (IsNumber(c)) + ++ctr; + } + return ctr; } } } diff --git a/PKHeX.Core/PKM/BK4.cs b/PKHeX.Core/PKM/BK4.cs index 40e284df9..5f7b46ad9 100644 --- a/PKHeX.Core/PKM/BK4.cs +++ b/PKHeX.Core/PKM/BK4.cs @@ -331,7 +331,7 @@ public override int Ball public override int Met_Level { get => Data[0x84] >> 1; set => Data[0x84] = (byte)((Data[0x84] & 0x1) | value << 1); } public override int OT_Gender { get => Data[0x84] & 1; set => Data[0x84] = (byte)((Data[0x84] & ~0x1) | (value & 1)); } - public override int EncounterType { get => Data[0x85]; set => Data[0x85] = (byte)value; } + public override GroundTileType GroundTile { get => (GroundTileType)Data[0x85]; set => Data[0x85] = (byte)value; } // Unused 0x87 #endregion diff --git a/PKHeX.Core/PKM/CK3.cs b/PKHeX.Core/PKM/CK3.cs index f21251fa4..8d939a76f 100644 --- a/PKHeX.Core/PKM/CK3.cs +++ b/PKHeX.Core/PKM/CK3.cs @@ -29,8 +29,8 @@ public sealed class CK3 : G3PKM, IShadowPKM private static byte[] SetString(string value, int maxLength) => StringConverter3.SetBEString3(value, maxLength); // Trash Bytes - public override byte[] Nickname_Trash { get => GetData(0x2E, 20); set { if (value.Length == 20) value.CopyTo(Data, 0x2E); } } - public override byte[] OT_Trash { get => GetData(0x18, 20); set { if (value.Length == 20) value.CopyTo(Data, 0x18); } } + public override Span Nickname_Trash { get => Data.AsSpan(0x2E, 20); set { if (value.Length == 20) value.CopyTo(Data.AsSpan(0x2E)); } } + public override Span OT_Trash { get => Data.AsSpan(0x18, 20); set { if (value.Length == 20) value.CopyTo(Data.AsSpan(0x18)); } } // Future Attributes diff --git a/PKHeX.Core/PKM/PK3.cs b/PKHeX.Core/PKM/PK3.cs index 17cf2a2ff..19e929fbc 100644 --- a/PKHeX.Core/PKM/PK3.cs +++ b/PKHeX.Core/PKM/PK3.cs @@ -43,8 +43,8 @@ public override PKM Clone() private const string EggNameJapanese = "タマゴ"; // Trash Bytes - public override byte[] Nickname_Trash { get => GetData(0x08, 10); set { if (value.Length == 10) value.CopyTo(Data, 0x08); } } - public override byte[] OT_Trash { get => GetData(0x14, 7); set { if (value.Length == 7) value.CopyTo(Data, 0x14); } } + public override Span Nickname_Trash { get => Data.AsSpan(0x08, 10); set { if (value.Length == 10) value.CopyTo(Data.AsSpan(0x08)); } } + public override Span OT_Trash { get => Data.AsSpan(0x14, 7); set { if (value.Length == 7) value.CopyTo(Data.AsSpan(0x14)); } } // At top for System.Reflection execution order hack diff --git a/PKHeX.Core/PKM/PK4.cs b/PKHeX.Core/PKM/PK4.cs index 1fdfa12bf..ddd732faf 100644 --- a/PKHeX.Core/PKM/PK4.cs +++ b/PKHeX.Core/PKM/PK4.cs @@ -310,8 +310,8 @@ public override int Ball public override int Met_Level { get => Data[0x84] & ~0x80; set => Data[0x84] = (byte)((Data[0x84] & 0x80) | value); } public override int OT_Gender { get => Data[0x84] >> 7; set => Data[0x84] = (byte)((Data[0x84] & ~0x80) | value << 7); } - public override int EncounterType { get => Data[0x85]; set => Data[0x85] = (byte)value; } - public int PokéathlonStat { get => Data[0x87]; set => Data[0x87] = (byte)value; } + public override GroundTileType GroundTile { get => (GroundTileType)Data[0x85]; set => Data[0x85] = (byte)value; } + public byte PokéathlonStat { get => Data[0x87]; set => Data[0x87] = value; } // Unused 0x87 #endregion diff --git a/PKHeX.Core/PKM/PK5.cs b/PKHeX.Core/PKM/PK5.cs index 60e50bd1b..1b8679cee 100644 --- a/PKHeX.Core/PKM/PK5.cs +++ b/PKHeX.Core/PKM/PK5.cs @@ -4,7 +4,9 @@ namespace PKHeX.Core { /// Generation 5 format. - public sealed class PK5 : PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, IContestStats, IContestStatsMutable + public sealed class PK5 : PKM, + IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, + IContestStats, IContestStatsMutable, IGroundTile { private static readonly ushort[] Unused = { @@ -39,8 +41,8 @@ private static byte[] DecryptParty(byte[] data) private static byte[] SetString(string value, int maxLength) => StringConverter.SetString5(value, maxLength); // Trash Bytes - public override byte[] Nickname_Trash { get => GetData(0x48, 22); set { if (value.Length == 22) value.CopyTo(Data, 0x48); } } - public override byte[] OT_Trash { get => GetData(0x68, 16); set { if (value.Length == 16) value.CopyTo(Data, 0x68); } } + public override Span Nickname_Trash { get => Data.AsSpan(0x48, 22); set { if (value.Length == 22) value.CopyTo(Data.AsSpan(0x48)); } } + public override Span OT_Trash { get => Data.AsSpan(0x68, 16); set { if (value.Length == 16) value.CopyTo(Data.AsSpan(0x68)); } } // Future Attributes public override uint EncryptionConstant { get => PID; set { } } @@ -242,7 +244,7 @@ private static byte[] DecryptParty(byte[] data) public override int Ball { get => Data[0x83]; set => Data[0x83] = (byte)value; } public override int Met_Level { get => Data[0x84] & ~0x80; set => Data[0x84] = (byte)((Data[0x84] & 0x80) | value); } public override int OT_Gender { get => Data[0x84] >> 7; set => Data[0x84] = (byte)((Data[0x84] & ~0x80) | value << 7); } - public override int EncounterType { get => Data[0x85]; set => Data[0x85] = (byte)value; } + public GroundTileType GroundTile { get => (GroundTileType)Data[0x85]; set => Data[0x85] = (byte)value; } // 0x86 Unused public byte PokeStarFame { get => Data[0x87]; set => Data[0x87] = value; } public bool IsPokeStar { get => PokeStarFame > 250; set => PokeStarFame = value ? (byte)255 : (byte)0; } @@ -394,7 +396,7 @@ public PK6 ConvertToPK6() // OT Gender & Encounter Level Met_Level = Met_Level, OT_Gender = OT_Gender, - EncounterType = EncounterType, + GroundTile = GroundTile, // Fill the Ribbon Counter Bytes RibbonCountMemoryContest = CountContestRibbons(), diff --git a/PKHeX.Core/PKM/PK6.cs b/PKHeX.Core/PKM/PK6.cs index 5bb485f47..a05f1eee9 100644 --- a/PKHeX.Core/PKM/PK6.cs +++ b/PKHeX.Core/PKM/PK6.cs @@ -5,7 +5,7 @@ namespace PKHeX.Core { /// Generation 6 format. public sealed class PK6 : G6PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, - IContestStats, IContestStatsMutable, IGeoTrack, ISuperTrain, IFormArgument, ITrainerMemories, IAffection + IContestStats, IContestStatsMutable, IGeoTrack, ISuperTrain, IFormArgument, ITrainerMemories, IAffection, IGroundTile { private static readonly ushort[] Unused = { @@ -343,7 +343,7 @@ public override int RelearnMove4 public override int Ball { get => Data[0xDC]; set => Data[0xDC] = (byte)value; } public override int Met_Level { get => Data[0xDD] & ~0x80; set => Data[0xDD] = (byte)((Data[0xDD] & 0x80) | value); } public override int OT_Gender { get => Data[0xDD] >> 7; set => Data[0xDD] = (byte)((Data[0xDD] & ~0x80) | (value << 7)); } - public override int EncounterType { get => Data[0xDE]; set => Data[0xDE] = (byte)value; } + public GroundTileType GroundTile { get => (GroundTileType)Data[0xDE]; set => Data[0xDE] = (byte)value; } public override int Version { get => Data[0xDF]; set => Data[0xDF] = (byte)value; } public int Country { get => Data[0xE0]; set => Data[0xE0] = (byte)value; } public int Region { get => Data[0xE1]; set => Data[0xE1] = (byte)value; } diff --git a/PKHeX.Core/PKM/PK8.cs b/PKHeX.Core/PKM/PK8.cs index 792b8aa08..b8eba3e9b 100644 --- a/PKHeX.Core/PKM/PK8.cs +++ b/PKHeX.Core/PKM/PK8.cs @@ -66,9 +66,9 @@ public override int CurrentFriendship public override int SIZE_STORED => PokeCrypto.SIZE_8STORED; // Trash Bytes - public override byte[] Nickname_Trash { get => GetData(0x58, 24); set { if (value.Length == 24) value.CopyTo(Data, 0x58); } } - public override byte[] HT_Trash { get => GetData(0xA8, 24); set { if (value.Length == 24) value.CopyTo(Data, 0xA8); } } - public override byte[] OT_Trash { get => GetData(0xF8, 24); set { if (value.Length == 24) value.CopyTo(Data, 0xF8); } } + public override Span Nickname_Trash { get => Data.AsSpan(0x58, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0x58)); } } + public override Span HT_Trash { get => Data.AsSpan(0xA8, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xA8)); } } + public override Span OT_Trash { get => Data.AsSpan(0xF8, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xF8)); } } // Maximums public override int MaxIV => 31; diff --git a/PKHeX.Core/PKM/PKM.cs b/PKHeX.Core/PKM/PKM.cs index faa97a1d7..a69a4be4b 100644 --- a/PKHeX.Core/PKM/PKM.cs +++ b/PKHeX.Core/PKM/PKM.cs @@ -31,11 +31,9 @@ public abstract class PKM : ISpeciesForm, ITrainerID, IGeneration, IShiny, ILang public virtual bool Valid { get => ChecksumValid && Sanity == 0; set { if (!value) return; Sanity = 0; RefreshChecksum(); } } // Trash Bytes - public abstract byte[] Nickname_Trash { get; set; } - public abstract byte[] OT_Trash { get; set; } - public virtual byte[] HT_Trash { get => Array.Empty(); set { } } - - protected byte[] GetData(int Offset, int Length) => Data.Slice(Offset, Length); + public abstract Span Nickname_Trash { get; set; } + public abstract Span OT_Trash { get; set; } + public virtual Span HT_Trash { get => Span.Empty; set { } } protected virtual ushort CalculateChecksum() => PokeCrypto.GetCHK(Data, SIZE_STORED); @@ -223,7 +221,6 @@ private byte[] Write() public virtual int RelearnMove2 { get => 0; set { } } public virtual int RelearnMove3 { get => 0; set { } } public virtual int RelearnMove4 { get => 0; set { } } - public virtual int EncounterType { get => 0; set { } } // Exposed but not Present in all public abstract int CurrentHandler { get; set; } @@ -293,7 +290,6 @@ private void SetID7(int sid7, int tid7) public bool LGPE => Version is (int)GP or (int)GE; public bool SWSH => Version is (int)SW or (int)SH; - protected internal bool PtHGSS => Pt || HGSS; public bool GO_LGPE => GO && Met_Location == Locations.GO7; public bool GO_HOME => GO && Met_Location == Locations.GO8; public bool VC => VC1 || VC2; diff --git a/PKHeX.Core/PKM/SK2.cs b/PKHeX.Core/PKM/SK2.cs index b027d822e..1a61f5b45 100644 --- a/PKHeX.Core/PKM/SK2.cs +++ b/PKHeX.Core/PKM/SK2.cs @@ -106,8 +106,8 @@ public override string OT_Name } } - public override byte[] Nickname_Trash { get => GetData(0x24, 12); set { if (value.Length == 12) value.CopyTo(Data, 0x24); } } - public override byte[] OT_Trash { get => GetData(0x30, 12); set { if (value.Length == 12) value.CopyTo(Data, 0x30); } } + public override Span Nickname_Trash { get => Data.AsSpan(0x24, 12); set { if (value.Length == 12) value.CopyTo(Data.AsSpan(0x24)); } } + public override Span OT_Trash { get => Data.AsSpan(0x30, 12); set { if (value.Length == 12) value.CopyTo(Data.AsSpan(0x30)); } } #endregion #region Party Attributes @@ -186,24 +186,32 @@ public PK2 ConvertToPK2() }; } - private static bool IsJapanese(byte[] data) + private static bool IsJapanese(ReadOnlySpan data) { - if (!StringConverter12.GetIsG1Japanese(data, 0x24, StringLength)) + if (!StringConverter12.GetIsG1Japanese(data.Slice(0x30, StringLength))) return false; - if (!StringConverter12.GetIsG1Japanese(data, 0x30, StringLength)) + if (!StringConverter12.GetIsG1Japanese(data.Slice(0x24, StringLength))) return false; for (int i = 6; i < 0xC; i++) { - if (data[0x24 + i] is not (0 or StringConverter12.G1TerminatorCode)) - return false; if (data[0x30 + i] is not (0 or StringConverter12.G1TerminatorCode)) return false; + if (data[0x24 + i] is not (0 or StringConverter12.G1TerminatorCode)) + return false; } return true; } - private static bool IsEnglish(byte[] data) => StringConverter12.GetIsG1English(data, 0x24, StringLength) && StringConverter12.GetIsG1English(data, 0x30, StringLength); + private static bool IsEnglish(ReadOnlySpan data) + { + if (!StringConverter12.GetIsG1English(data.Slice(0x30, StringLength))) + return false; + if (!StringConverter12.GetIsG1English(data.Slice(0x24, StringLength))) + return false; + return true; + } + public bool IsPossible(bool japanese) => japanese ? IsJapanese(Data) : IsEnglish(Data); public void SwapLanguage() => IsEncodingJapanese ^= true; } diff --git a/PKHeX.Core/PKM/Shared/G4PKM.cs b/PKHeX.Core/PKM/Shared/G4PKM.cs index df898d629..c28686c16 100644 --- a/PKHeX.Core/PKM/Shared/G4PKM.cs +++ b/PKHeX.Core/PKM/Shared/G4PKM.cs @@ -1,6 +1,10 @@ -namespace PKHeX.Core +using System; + +namespace PKHeX.Core { - public abstract class G4PKM : PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, IContestStats, IContestStatsMutable + public abstract class G4PKM : PKM, + IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetUnique3, IRibbonSetUnique4, IRibbonSetCommon3, IRibbonSetCommon4, + IContestStats, IContestStatsMutable, IGroundTile { protected G4PKM(byte[] data) : base(data) { } protected G4PKM(int size) : base(size) { } @@ -20,6 +24,8 @@ public abstract class G4PKM : PKM, IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSe public sealed override int PSV => (int)((PID >> 16 ^ (PID & 0xFFFF)) >> 3); public sealed override int TSV => (TID ^ SID) >> 3; + protected bool PtHGSS => Pt || HGSS; + public sealed override int Characteristic { get @@ -38,8 +44,8 @@ public sealed override int Characteristic } // Trash Bytes - public sealed override byte[] Nickname_Trash { get => GetData(0x48, 22); set { if (value.Length == 22) value.CopyTo(Data, 0x48); } } - public sealed override byte[] OT_Trash { get => GetData(0x68, 16); set { if (value.Length == 16) value.CopyTo(Data, 0x68); } } + public sealed override Span Nickname_Trash { get => Data.AsSpan(0x48, 22); set { if (value.Length == 22) value.CopyTo(Data.AsSpan(0x48)); } } + public sealed override Span OT_Trash { get => Data.AsSpan(0x68, 16); set { if (value.Length == 16) value.CopyTo(Data.AsSpan(0x68)); } } // Future Attributes public sealed override uint EncryptionConstant { get => PID; set { } } @@ -158,6 +164,8 @@ public sealed override int Characteristic public abstract byte CNT_Tough { get; set; } public abstract byte CNT_Sheen { get; set; } + public abstract GroundTileType GroundTile { get; set; } + protected T ConvertTo() where T : G4PKM, new() { var pk = new T @@ -215,7 +223,7 @@ protected T ConvertTo() where T : G4PKM, new() PKRS_Days = PKRS_Days, PKRS_Strain = PKRS_Strain, Ball = Ball, - EncounterType = EncounterType, + GroundTile = GroundTile, FatefulEncounter = FatefulEncounter, Met_Level = Met_Level, diff --git a/PKHeX.Core/PKM/Shared/G6PKM.cs b/PKHeX.Core/PKM/Shared/G6PKM.cs index 273198ff9..746db594c 100644 --- a/PKHeX.Core/PKM/Shared/G6PKM.cs +++ b/PKHeX.Core/PKM/Shared/G6PKM.cs @@ -11,9 +11,9 @@ public abstract class G6PKM : PKM protected G6PKM(int size) : base(size) { } // Trash Bytes - public sealed override byte[] Nickname_Trash { get => GetData(0x40, 24); set { if (value.Length == 24) value.CopyTo(Data, 0x40); } } - public sealed override byte[] HT_Trash { get => GetData(0x78, 24); set { if (value.Length == 24) value.CopyTo(Data, 0x78); } } - public sealed override byte[] OT_Trash { get => GetData(0xB0, 24); set { if (value.Length == 24) value.CopyTo(Data, 0xB0); } } + public sealed override Span Nickname_Trash { get => Data.AsSpan(0x40, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0x40)); } } + public sealed override Span HT_Trash { get => Data.AsSpan(0x78, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0x78)); } } + public sealed override Span OT_Trash { get => Data.AsSpan(0xB0, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xB0)); } } protected sealed override ushort CalculateChecksum() { diff --git a/PKHeX.Core/PKM/Shared/GBPKM.cs b/PKHeX.Core/PKM/Shared/GBPKM.cs index a80732625..1134679fd 100644 --- a/PKHeX.Core/PKM/Shared/GBPKM.cs +++ b/PKHeX.Core/PKM/Shared/GBPKM.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; namespace PKHeX.Core { diff --git a/PKHeX.Core/PKM/Shared/GBPKML.cs b/PKHeX.Core/PKM/Shared/GBPKML.cs index 37aadb02e..13b649b4c 100644 --- a/PKHeX.Core/PKM/Shared/GBPKML.cs +++ b/PKHeX.Core/PKM/Shared/GBPKML.cs @@ -18,8 +18,8 @@ public abstract class GBPKML : GBPKM internal readonly byte[] RawNickname; // Trash Bytes - public sealed override byte[] Nickname_Trash { get => RawNickname; set { if (value.Length == RawNickname.Length) value.CopyTo(RawNickname, 0); } } - public sealed override byte[] OT_Trash { get => RawOT; set { if (value.Length == RawOT.Length) value.CopyTo(RawOT, 0); } } + public sealed override Span Nickname_Trash { get => RawNickname; set { if (value.Length == RawNickname.Length) value.CopyTo(RawNickname); } } + public sealed override Span OT_Trash { get => RawOT; set { if (value.Length == RawOT.Length) value.CopyTo(RawOT); } } protected GBPKML(int size, bool jp = false) : base(size) { diff --git a/PKHeX.Core/PKM/Shared/GroundTileType.cs b/PKHeX.Core/PKM/Shared/GroundTileType.cs new file mode 100644 index 000000000..cf8d83cab --- /dev/null +++ b/PKHeX.Core/PKM/Shared/GroundTileType.cs @@ -0,0 +1,47 @@ +namespace PKHeX.Core +{ + /// + /// Ground Tile Type the was encountered from. + /// + /// + /// Used in Generation 4 games, this value is set depending on what type of overworld tile the player is standing on when the is obtained. + /// + public enum GroundTileType : byte + { + None = 00, // No animation for the tile + Sand = 01, // Obtainable only via HG/SS + Grass = 02, + Puddle = 03, // No encounters from this tile type + Rock = 04, + Cave = 05, + Snow = 06, // No encounters from this tile type + Water = 07, + Ice = 08, // No encounters from this tile type + Building = 09, + Marsh = 10, + Bridge = 11, // No encounters from this tile type + Max_DP = 12, // Unspecific, catch-all for DP undefined tiles. + + // added tile types in Pt + // no encounters from these tile types + Elite4_1 = 12, // Elite Four Room #1 + Elite4_2 = 13, // Elite Four Room #2 + Elite4_3 = 14, // Elite Four Room #3 + Elite4_4 = 15, // Elite Four Room #4 + Elite4_M = 16, // Elite Four Champion Room + DistortionSideways = 17, // Distortion World (Not Giratina) + BattleTower = 18, + BattleFactory = 19, + BattleArcade = 20, + BattleCastle = 21, + BattleHall = 22, + + Distortion = 23, + Max_Pt = 24, // Unspecific, catch-all for Pt undefined tiles. + } + + public static class GroundTileTypeExtensions + { + public static bool IsObtainable(this GroundTileType type) => ((0b_1_10000000_00010110_10110111 >> (int) type) & 1) == 1; + } +} diff --git a/PKHeX.Core/PKM/Shared/IGroundTile.cs b/PKHeX.Core/PKM/Shared/IGroundTile.cs new file mode 100644 index 000000000..19cb108ce --- /dev/null +++ b/PKHeX.Core/PKM/Shared/IGroundTile.cs @@ -0,0 +1,13 @@ +namespace PKHeX.Core +{ + /// + /// Indicates the Type of Encounter Tile the Pokémon was encountered on. + /// + public interface IGroundTile + { + /// + /// Type of Encounter Tile the Pokémon was encountered on. + /// + GroundTileType GroundTile { get; set; } + } +} diff --git a/PKHeX.Core/PKM/Shared/PokeListGB.cs b/PKHeX.Core/PKM/Shared/PokeListGB.cs index b89f31bba..b8a4a93be 100644 --- a/PKHeX.Core/PKM/Shared/PokeListGB.cs +++ b/PKHeX.Core/PKM/Shared/PokeListGB.cs @@ -149,9 +149,11 @@ private void SetEntry(int base_ofs, int i) int pkOfs = GetOffsetPKMData(base_ofs, i); int otOfs = GetOffsetPKMOT(base_ofs, i); int nkOfs = GetOffsetPKMNickname(base_ofs, i); - Array.Copy(Pokemon[i].Data, 0, Data, pkOfs, Entry_Size); - Array.Copy(Pokemon[i].OT_Trash, 0, Data, otOfs, StringLength); - Array.Copy(Pokemon[i].Nickname_Trash, 0, Data, nkOfs, StringLength); + + var pk = Pokemon[i]; + Array.Copy(pk.Data, 0, Data, pkOfs, Entry_Size); + pk.RawOT.CopyTo(Data, otOfs); + pk.RawNickname.CopyTo(Data, nkOfs); } } } \ No newline at end of file diff --git a/PKHeX.Core/PKM/Strings/StringConverter12.cs b/PKHeX.Core/PKM/Strings/StringConverter12.cs index fa7a97d00..0ac06395b 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter12.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter12.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; namespace PKHeX.Core @@ -10,18 +9,25 @@ namespace PKHeX.Core /// public static class StringConverter12 { - public static bool GetIsG1Japanese(string str) => AllCharsInDictionary(str, U2RBY_J); - public static bool GetIsG1English(string str) => AllCharsInDictionary(str, U2RBY_U); - public static bool GetIsG1Japanese(byte[] data, int start, int length) => AllCharsInDictionary(data, start, length, RBY2U_J); - public static bool GetIsG1English(byte[] data, int start, int length) => AllCharsInDictionary(data, start, length, RBY2U_U); + public static bool GetIsG1Japanese(ReadOnlySpan str) => AllCharsInDictionary(str, U2RBY_J); + public static bool GetIsG1English(ReadOnlySpan str) => AllCharsInDictionary(str, U2RBY_U); + public static bool GetIsG1Japanese(ReadOnlySpan raw) => AllCharsInDictionary(raw, RBY2U_J); + public static bool GetIsG1English(ReadOnlySpan raw) => AllCharsInDictionary(raw, RBY2U_U); - private static bool AllCharsInDictionary(IEnumerable c, IReadOnlyDictionary d) => c.All(d.ContainsKey); - - private static bool AllCharsInDictionary(IReadOnlyList data, int start, int length, IReadOnlyDictionary d) + private static bool AllCharsInDictionary(ReadOnlySpan c, IReadOnlyDictionary d) { - for (int i = start; i < start + length; i++) + foreach (var x in c) + { + if (!d.ContainsKey(x)) + return false; + } + return true; + } + + private static bool AllCharsInDictionary(ReadOnlySpan data, IReadOnlyDictionary d) + { + foreach (var c in data) { - var c = data[i]; if (c == 0) break; if (!d.ContainsKey(c)) @@ -54,7 +60,15 @@ private static bool AllCharsInDictionary(IReadOnlyList data, int start, in /// /// Raw string bytes /// Indication if the data is from a definitely-german string - public static bool IsG12German(IEnumerable data) => data.Any(z => z is >= 0xC0 and <= 0xC6); + public static bool IsG12German(ReadOnlySpan data) + { + foreach (var b in data) + { + if (b is >= 0xC0 and <= 0xC6) + return true; + } + return false; + } /// /// Checks if the input byte array is definitely of German origin (any ÄÖÜäöü) @@ -71,14 +85,21 @@ private static bool AllCharsInDictionary(IReadOnlyList data, int start, in /// /// Data source is Japanese. /// Decoded string. - public static string GetString1(byte[] data, int offset, int count, bool jp) + public static string GetString1(byte[] data, int offset, int count, bool jp) => GetString1(data.AsSpan(offset, count), jp); + + /// + /// Converts Generation 1 encoded data into a string. + /// + /// Encoded data. + /// Data source is Japanese. + /// Decoded string. + public static string GetString1(ReadOnlySpan data, bool jp) { var dict = jp ? RBY2U_J : RBY2U_U; - var s = new StringBuilder(count); - for (int i = 0; i < count; i++) + var s = new StringBuilder(data.Length); + foreach (var val in data) { - var val = data[offset + i]; if (!dict.TryGetValue(val, out var c)) // Take valid values break; if (c == G1Terminator) // Stop if Terminator @@ -127,12 +148,12 @@ public static byte[] SetString1(string value, int maxLength, bool jp, int padTo /// Encoded character. /// Data source is Japanese. /// Decoded string. - public static string GetG1Char(byte key, bool jp) + public static char GetG1Char(byte key, bool jp) { var dict = jp ? RBY2U_J : RBY2U_U; if (dict.TryGetValue(key, out var val)) - return val.ToString(); - return string.Empty; + return val; + return G1Terminator; } #region Gen 1/2 Character Tables diff --git a/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs b/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs index d52cea3e8..a611011fc 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter2KOR.cs @@ -15,7 +15,16 @@ public static class StringConverter2KOR /// /// Checks if any of the characters inside are from the special Korean codepoint pages. /// - public static bool GetIsG2Korean(string str) => str.All(z => U2GSC_KOR.Any(x => x.ContainsKey(z))); + public static bool GetIsG2Korean(ReadOnlySpan str) + { + var dict = U2GSC_KOR; + foreach (var c in str) + { + if (!dict.Any(d => d.ContainsKey(c))) + return false; + } + return true; + } /// /// Converts Generation 2 Korean encoded data into a string. diff --git a/PKHeX.Core/PKM/Strings/StringConverter3.cs b/PKHeX.Core/PKM/Strings/StringConverter3.cs index a97f501ee..13193954a 100644 --- a/PKHeX.Core/PKM/Strings/StringConverter3.cs +++ b/PKHeX.Core/PKM/Strings/StringConverter3.cs @@ -20,12 +20,19 @@ public static class StringConverter3 /// Length of data to read. /// Value source is Japanese font. /// Decoded string. - public static string GetString3(byte[] data, int offset, int count, bool jp) + public static string GetString3(byte[] data, int offset, int count, bool jp) => GetString3(data.AsSpan(offset, count), jp); + + /// + /// Converts a Generation 3 encoded value array to string. + /// + /// Byte array containing string data. + /// Value source is Japanese font. + /// Decoded string. + public static string GetString3(ReadOnlySpan data, bool jp) { - var s = new StringBuilder(count); - for (int i = 0; i < count; i++) + var s = new StringBuilder(data.Length); + foreach (var val in data) { - var val = data[offset + i]; var c = GetG3Char(val, jp); // Convert to Unicode if (c == Terminator) // Stop if Terminator/Invalid break; diff --git a/PKHeX.Core/PKM/Util/PKMConverter.cs b/PKHeX.Core/PKM/Util/PKMConverter.cs index 5e19ae8b4..f51b9f20a 100644 --- a/PKHeX.Core/PKM/Util/PKMConverter.cs +++ b/PKHeX.Core/PKM/Util/PKMConverter.cs @@ -160,7 +160,8 @@ private static bool IsPK6FormatReallyPK7(PK6 pk, int preferredFormat) if (pk.HeldItem > Legal.MaxItemID_6_AO) return true; - int et = pk.EncounterType; + // Ground Tile property is replaced with Hyper Training PK6->PK7 + var et = pk.GroundTile; if (et != 0) { if (pk.CurrentLevel < 100) // can't be hyper trained @@ -168,7 +169,7 @@ private static bool IsPK6FormatReallyPK7(PK6 pk, int preferredFormat) if (!pk.Gen4) // can't have encounter type return true; - if (et > 24) // invalid gen4 EncounterType + if (et > GroundTileType.Max_Pt) // invalid gen4 EncounterType return true; } diff --git a/PKHeX.Core/PKM/XK3.cs b/PKHeX.Core/PKM/XK3.cs index 82f827dea..9ce33058d 100644 --- a/PKHeX.Core/PKM/XK3.cs +++ b/PKHeX.Core/PKM/XK3.cs @@ -28,8 +28,8 @@ public sealed class XK3 : G3PKM, IShadowPKM private static byte[] SetString(string value, int maxLength) => StringConverter3.SetBEString3(value, maxLength); // Trash Bytes - public override byte[] Nickname_Trash { get => GetData(0x4E, 20); set { if (value.Length == 20) value.CopyTo(Data, 0x4E); } } - public override byte[] OT_Trash { get => GetData(0x38, 20); set { if (value.Length == 20) value.CopyTo(Data, 0x38); } } + public override Span Nickname_Trash { get => Data.AsSpan(0x4E, 20); set { if (value.Length == 20) value.CopyTo(Data.AsSpan(0x4E)); } } + public override Span OT_Trash { get => Data.AsSpan(0x38, 20); set { if (value.Length == 20) value.CopyTo(Data.AsSpan(0x38)); } } // Silly Attributes public override ushort Sanity { get => 0; set { } } // valid flag set in pkm structure. diff --git a/PKHeX.Core/Saves/SAV1.cs b/PKHeX.Core/Saves/SAV1.cs index a69e857eb..8efe06118 100644 --- a/PKHeX.Core/Saves/SAV1.cs +++ b/PKHeX.Core/Saves/SAV1.cs @@ -240,7 +240,7 @@ public override string OT set => SetString(value, OTLength).CopyTo(Data, Offsets.OT); } - public byte[] OT_Trash { get => GetData(Offsets.OT, StringLength); set { if (value.Length == StringLength) SetData(value, Offsets.OT); } } + public Span OT_Trash { get => Data.AsSpan(Offsets.OT, StringLength); set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.OT)); } } public override int Gender { diff --git a/PKHeX.Core/Saves/SAV2.cs b/PKHeX.Core/Saves/SAV2.cs index daacb34e6..f3819c354 100644 --- a/PKHeX.Core/Saves/SAV2.cs +++ b/PKHeX.Core/Saves/SAV2.cs @@ -321,10 +321,10 @@ public override string OT set => SetString(value, (Korean ? 2 : 1) * OTLength).CopyTo(Data, Offsets.Trainer1 + 2); } - public byte[] OT_Trash - { - get => GetData(Offsets.Trainer1 + 2, StringLength); - set { if (value.Length == StringLength) SetData(value, Offsets.Trainer1 + 2); } + public Span OT_Trash + { + get => Data.AsSpan(Offsets.Trainer1 + 2, StringLength); + set { if (value.Length == StringLength) value.CopyTo(Data.AsSpan(Offsets.Trainer1 + 2)); } } public override int Gender diff --git a/PKHeX.WinForms/Controls/PKM Editor/EditPK4.cs b/PKHeX.WinForms/Controls/PKM Editor/EditPK4.cs index 9c4f51360..c4c18490e 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/EditPK4.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/EditPK4.cs @@ -15,7 +15,7 @@ private void PopulateFieldsPK4() LoadMisc3(pk4); LoadMisc4(pk4); - CB_EncounterType.SelectedValue = pk4.Gen4 ? pk4.EncounterType : 0; + CB_EncounterType.SelectedValue = pk4.Gen4 ? (int)pk4.GroundTile : 0; CB_EncounterType.Visible = Label_EncounterType.Visible = Entity.Gen4; if (HaX) @@ -40,7 +40,7 @@ private G4PKM PreparePK4() SaveMisc3(pk4); SaveMisc4(pk4); - pk4.EncounterType = WinFormsUtil.GetIndex(CB_EncounterType); + pk4.GroundTile = (GroundTileType)WinFormsUtil.GetIndex(CB_EncounterType); // Minor properties pk4.ShinyLeaf = ShinyLeaf.GetValue(); diff --git a/PKHeX.WinForms/Controls/PKM Editor/EditPK5.cs b/PKHeX.WinForms/Controls/PKM Editor/EditPK5.cs index f6ccfbc66..b345d6ed1 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/EditPK5.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/EditPK5.cs @@ -14,7 +14,7 @@ private void PopulateFieldsPK5() LoadMisc2(pk5); LoadMisc3(pk5); LoadMisc4(pk5); - CB_EncounterType.SelectedValue = pk5.Gen4 ? pk5.EncounterType : 0; + CB_EncounterType.SelectedValue = pk5.Gen4 ? (int)pk5.GroundTile : 0; CB_EncounterType.Visible = Label_EncounterType.Visible = pk5.Gen4; CHK_NSparkle.Checked = pk5.NPokémon; @@ -39,7 +39,7 @@ private PK5 PreparePK5() SaveMisc3(pk5); SaveMisc4(pk5); - pk5.EncounterType = WinFormsUtil.GetIndex(CB_EncounterType); + pk5.GroundTile = (GroundTileType)WinFormsUtil.GetIndex(CB_EncounterType); pk5.NPokémon = CHK_NSparkle.Checked; if (!HaX) // specify via extra 0x42 instead pk5.HiddenAbility = CB_Ability.SelectedIndex > 1; // not 0 or 1 diff --git a/PKHeX.WinForms/Controls/PKM Editor/EditPK6.cs b/PKHeX.WinForms/Controls/PKM Editor/EditPK6.cs index 4e97d8683..cfe4be51f 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/EditPK6.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/EditPK6.cs @@ -16,7 +16,7 @@ private void PopulateFieldsPK6() LoadMisc4(pk6); LoadMisc6(pk6); - CB_EncounterType.SelectedValue = pk6.Gen4 ? pk6.EncounterType : 0; + CB_EncounterType.SelectedValue = pk6.Gen4 ? (int)pk6.GroundTile : 0; CB_EncounterType.Visible = Label_EncounterType.Visible = pk6.Gen4; LoadPartyStats(pk6); @@ -35,7 +35,7 @@ private PK6 PreparePK6() SaveMisc6(pk6); CheckTransferPIDValid(pk6); - pk6.EncounterType = WinFormsUtil.GetIndex(CB_EncounterType); + pk6.GroundTile = (GroundTileType)WinFormsUtil.GetIndex(CB_EncounterType); // Toss in Party Stats SavePartyStats(pk6); diff --git a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs index 334c1fa01..304dab190 100644 --- a/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs +++ b/PKHeX.WinForms/Controls/PKM Editor/PKMEditor.cs @@ -798,7 +798,7 @@ private bool SetSuggestedMetLocation(bool silent = false) if (Entity.CurrentLevel >= minlvl && Entity.Met_Level == level && Entity.Met_Location == location) { - if (!encounter.HasEncounterType(Entity.Format) || WinFormsUtil.GetIndex(CB_EncounterType) == encounter.GetSuggestedEncounterType()) + if (!encounter.HasGroundTile(Entity.Format) || WinFormsUtil.GetIndex(CB_EncounterType) == (int)encounter.GetSuggestedGroundTile()) return false; } if (minlvl < level) @@ -821,8 +821,8 @@ private bool SetSuggestedMetLocation(bool silent = false) TB_MetLevel.Text = encounter.GetSuggestedMetLevel(Entity).ToString(); CB_MetLocation.SelectedValue = location; - if (encounter.HasEncounterType(Entity.Format)) - CB_EncounterType.SelectedValue = encounter.GetSuggestedEncounterType(); + if (encounter.HasGroundTile(Entity.Format)) + CB_EncounterType.SelectedValue = (int)encounter.GetSuggestedGroundTile(); if (Entity.Gen6 && Entity.WasEgg && ModifyPKM) Entity.SetHatchMemory6(); @@ -1200,7 +1200,7 @@ private void UpdateOriginGame(object sender, EventArgs e) } // Visibility logic for Gen 4 encounter type; only show for Gen 4 Pokemon. - if (Entity.Format >= 4) + if (Entity is IGroundTile) { bool g4 = Entity.Gen4; CB_EncounterType.Visible = Label_EncounterType.Visible = g4 && Entity.Format < 7; diff --git a/PKHeX.WinForms/Subforms/PKM Editors/Text.cs b/PKHeX.WinForms/Subforms/PKM Editors/Text.cs index 63c422003..f5be0a6a2 100644 --- a/PKHeX.WinForms/Subforms/PKM Editors/Text.cs +++ b/PKHeX.WinForms/Subforms/PKM Editors/Text.cs @@ -12,14 +12,14 @@ public partial class TrashEditor : Form public TrashEditor(TextBoxBase TB_NN, SaveFile sav) : this(TB_NN, Array.Empty(), sav) { } - public TrashEditor(TextBoxBase TB_NN, byte[] raw, SaveFile sav) + public TrashEditor(TextBoxBase TB_NN, Span raw, SaveFile sav) { InitializeComponent(); WinFormsUtil.TranslateInterface(this, Main.CurrentLanguage); SAV = sav; FinalString = TB_NN.Text; - Raw = FinalBytes = raw; + Raw = FinalBytes = raw.ToArray(); editing = true; if (raw.Length != 0) diff --git a/Tests/PKHeX.Core.Tests/PKHeX.Core.Tests.csproj b/Tests/PKHeX.Core.Tests/PKHeX.Core.Tests.csproj index e4cbb0bf1..c0f6d7a47 100644 --- a/Tests/PKHeX.Core.Tests/PKHeX.Core.Tests.csproj +++ b/Tests/PKHeX.Core.Tests/PKHeX.Core.Tests.csproj @@ -10,7 +10,7 @@ - + all diff --git a/Tests/PKHeX.Core.Tests/PKM/StringTests.cs b/Tests/PKHeX.Core.Tests/PKM/StringTests.cs index aa5798425..23ddddb38 100644 --- a/Tests/PKHeX.Core.Tests/PKM/StringTests.cs +++ b/Tests/PKHeX.Core.Tests/PKM/StringTests.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System; using FluentAssertions; using PKHeX.Core; using Xunit; @@ -49,14 +49,22 @@ public void EncodesNicknameCorrectly() CheckStringGetSet(nameof(pkm.Nickname), name_nidoran, pkm.Nickname, byte_nidoran, pkm.Nickname_Trash); } - private static void CheckStringGetSet(string check, string instr, string outstr, byte[] indata, byte[] outdata) + private static void CheckStringGetSet(string check, string instr, string outstr, ReadOnlySpan indata, ReadOnlySpan outdata) { instr.Should().BeEquivalentTo(outstr); outdata = outdata[..indata.Length]; indata.SequenceEqual(outdata).Should() - .BeTrue($"expected {check} to set properly, instead got {string.Join(", ", outdata.Select(z => $"{z:X2}"))}"); + .BeTrue($"expected {check} to set properly, instead got {Hex(outdata)}"); + } + + private static string Hex(ReadOnlySpan outdata) + { + var sb = new System.Text.StringBuilder(outdata.Length); + foreach (var b in outdata) + sb.AppendFormat("{0:X2}, ", b); + return sb.ToString(); } [Theory]