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); } protected override IEnumerable GetMatchFromEvoLevel(PKM pkm, IEnumerable vs, int minLevel) { var loc = Location; if (IsWildArea8(loc)) // wild area gets boosted up to level 60 post-game { const int boostTo = 60; if (pkm.Met_Level == boostTo) { var boost = Slots.Where(slot => vs.Any(evo => IsMatch(evo, slot) && evo.Level >= boostTo)); return boost.Where(s => s.LevelMax < boostTo || s.IsLevelWithinRange(minLevel)); } } var slots = Slots.Where(slot => vs.Any(evo => IsMatch(evo, slot) && evo.Level >= slot.LevelMin)); // Get slots where pokemon can exist with respect to level constraints return slots.Where(s => s.IsLevelWithinRange(minLevel)); } private static bool IsMatch(DexLevel evo, EncounterSlot slot) { if (evo.Species != slot.Species) return false; if (evo.Form == slot.Form) return true; if (Legal.FormChange.Contains(evo.Species)) return true; return false; } protected override IEnumerable GetFilteredSlots(PKM pkm, IEnumerable slots, int minLevel) => slots; public static bool IsWildArea8(int loc) => 122 <= loc && loc <= 154; // Rolling Fields -> Lake of Outrage // Location, and areas that can feed encounters into it. 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. }; } 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 EncounterSlot8(int specForm, int min, int max, AreaWeather8 weather) { Species = specForm & 0x7FF; Form = specForm >> 11; LevelMin = min; LevelMax = max; Weather = weather; } } }