using System; using System.Collections.Generic; using System.Linq; namespace PKHeX.Core { /// /// /// encounter area /// public sealed class EncounterArea8 : EncounterAreaSH { /// public override bool IsMatchLocation(int location) { if (Location == location) return true; if (!PermitCrossover) return false; // Get all other areas that the Location can bleed encounters to if (!ConnectingArea8.TryGetValue(Location, out var others)) return false; // Check if any of the other areas are the met location return others.Contains((byte)location); } public override IEnumerable GetMatchingSlots(PKM pkm, IReadOnlyList chain) { // wild area gets boosted up to level 60 post-game bool isBoosted = pkm.Met_Level == 60 && (IsWildArea8(Location) || IsWildArea8Armor(Location)); foreach (var slot in Slots) { foreach (var evo in chain) { if (slot.Species != evo.Species) continue; if (!slot.IsLevelWithinRange(pkm.Met_Level) && !isBoosted) break; if (slot.Form != evo.Form && !Legal.WildChangeFormAfter.Contains(evo.Species)) break; yield return slot; break; } } } public static bool IsWildArea8(int loc) => 122 <= loc && loc <= 154; // Rolling Fields -> Lake of Outrage public static bool IsWildArea8Armor(int loc) => 164 <= loc && loc <= 194; // Fields of Honor -> Honeycalm Island // Location, and areas that it can feed encounters to. public static readonly IReadOnlyDictionary> ConnectingArea8 = new Dictionary> { // Rolling Fields // Dappled Grove, East Lake Axewell, West Lake Axewell // Also connects to South Lake Miloch but too much of a stretch {122, new byte[] {124, 128, 130}}, // Dappled Grove // Rolling Fields, Watchtower Ruins {124, new byte[] {122, 126}}, // Watchtower Ruins // Dappled Grove, West Lake Axewell {126, new byte[] {124, 130}}, // East Lake Axewell // Rolling Fields, West Lake Axewell, Axew's Eye, North Lake Miloch {128, new byte[] {122, 130, 132, 138}}, // West Lake Axewell // Rolling Fields, Watchtower Ruins, East Lake Axewell, Axew's Eye {130, new byte[] {122, 126, 128, 132}}, // Axew's Eye // East Lake Axewell, West Lake Axewell {132, new byte[] {128, 130}}, // South Lake Miloch // Giant's Seat, North Lake Miloch {134, new byte[] {136, 138}}, // Giant's Seat // South Lake Miloch, North Lake Miloch {136, new byte[] {134, 138}}, // North Lake Miloch // East Lake Axewell, South Lake Miloch, Giant's Seat // Also connects to Motostoke Riverbank but too much of a stretch {138, new byte[] {134, 136}}, // Motostoke Riverbank // Bridge Field {140, new byte[] {142}}, // Bridge Field // Motostoke Riverbank, Stony Wilderness {142, new byte[] {140, 144}}, // Stony Wilderness // Bridge Field, Dusty Bowl, Giant's Mirror, Giant's Cap {144, new byte[] {142, 146, 148, 152}}, // Dusty Bowl // Stony Wilderness, Giant's Mirror, Hammerlocke Hills {146, new byte[] {144, 148, 150}}, // Giant's Mirror // Stony Wilderness, Dusty Bowl, Hammerlocke Hills {148, new byte[] {144, 146, 148}}, // Hammerlocke Hills // Dusty Bowl, Giant's Mirror, Giant's Cap {150, new byte[] {146, 148, 152}}, // Giant's Cap // Stony Wilderness, Giant's Cap // Also connects to Lake of Outrage but too much of a stretch {152, new byte[] {144, 150}}, // Lake of Outrage is just itself. // Challenge Beach // Soothing Wetlands, Courageous Cavern {170, new byte[] {166, 176}}, // Challenge Road // Brawler's Cave {174, new byte[] {172}}, // Courageous Cavern // Loop Lagoon {176, new byte[] {178}}, // Warm-Up Tunnel // Training Lowlands, Potbottom Desert {182, new byte[] {180, 184}}, // Workout Sea // Fields of Honor {186, new byte[] {164}}, // Stepping-Stone Sea // Fields of Honor {188, new byte[] {170}}, // Insular Sea // Honeycalm Sea {190, new byte[] {192}}, // Honeycalm Sea // Honeycalm Island {192, new byte[] {194}}, }; } public abstract class EncounterAreaSH : EncounterArea { /// /// Slots from this area can cross over to another area, resulting in a different met location. /// public bool PermitCrossover { get; internal set; } /// /// Gets an array of areas from an array of raw area data /// /// Simplified raw format of an Area /// Array of areas public static T[] GetArray(byte[][] entries) where T : EncounterAreaSH, new() { T[] data = new T[entries.Length]; for (int i = 0; i < data.Length; i++) { var loc = data[i] = new T(); loc.LoadSlots(entries[i]); } return data; } private void LoadSlots(byte[] areaData) { Location = areaData[0]; Slots = new EncounterSlot[areaData[1]]; int ctr = 0; int ofs = 2; do { var flags = (AreaWeather8)BitConverter.ToUInt16(areaData, ofs); var min = areaData[ofs + 2]; var max = areaData[ofs + 3]; var count = areaData[ofs + 4]; // ofs+5 reserved ofs += 6; for (int i = 0; i < count; i++, ctr++, ofs += 2) { var specForm = BitConverter.ToUInt16(areaData, ofs); Slots[ctr] = new EncounterSlot8(specForm, min, max, flags); } } while (ctr != Slots.Length); foreach (var slot in Slots) slot.Area = this; } } /// /// Encounter Conditions for /// /// Values above are for Shaking/Fishing hidden encounters only. [Flags] public enum AreaWeather8 { None, Normal = 1, Overcast = 1 << 1, Raining = 1 << 2, Thunderstorm = 1 << 3, Intense_Sun = 1 << 4, Snowing = 1 << 5, Snowstorm = 1 << 6, Sandstorm = 1 << 7, Heavy_Fog = 1 << 8, All = Normal | Overcast | Raining | Thunderstorm | Intense_Sun | Snowing | Snowstorm | Sandstorm | Heavy_Fog, Shaking_Trees = 1 << 9, Fishing = 1 << 10, NotWeather = Shaking_Trees | Fishing, } /// /// Encounter Slot found in /// public sealed class EncounterSlot8 : EncounterSlot { public readonly AreaWeather8 Weather; public override string LongName => Weather == AreaWeather8.All ? wild : $"{wild} - {Weather.ToString().Replace("_", string.Empty)}"; public override int Generation => 8; public EncounterSlot8(int specForm, int min, int max, AreaWeather8 weather) { Species = specForm & 0x7FF; Form = specForm >> 11; LevelMin = min; LevelMax = max; Weather = weather; } } }