PKHeX/PKHeX.Core/Legality/Areas/EncounterArea6XY.cs
Kurt ccf87242c1 Eliminate boxing on encounter search (criteria)
struct implementing interface is boxed when passed to method that accepts interface (not generic method).
Removes IDexLevel (no other inheritors but EvoCriteria) and uses the primitive the data is stored (array, not IReadOnlyList) for slightly better perf.
2022-05-07 18:29:36 -07:00

159 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core
{
/// <inheritdoc cref="EncounterArea" />
/// <summary>
/// <see cref="GameVersion.XY"/> encounter area
/// </summary>
public sealed record EncounterArea6XY : EncounterArea
{
public readonly EncounterSlot6XY[] Slots;
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea6XY[] GetAreas(BinLinkerAccessor input, GameVersion game, EncounterArea6XY safari)
{
int count = input.Length;
var result = new EncounterArea6XY[count + 1];
for (int i = 0; i < count; i++)
result[i] = new EncounterArea6XY(input[i], game);
result[^1] = safari;
return result;
}
public EncounterArea6XY() : base(GameVersion.XY)
{
Location = 148; // Friend Safari
Type = SlotType.FriendSafari;
Slots = LoadSafariSlots();
}
private EncounterArea6XY(ReadOnlySpan<byte> data, GameVersion game) : base(game)
{
Location = data[0] | (data[1] << 8);
Type = (SlotType)data[2];
Slots = ReadSlots(data);
}
private EncounterSlot6XY[] LoadSafariSlots()
{
const int SpeciesFormSlots = 4;
// Single form species
Span<ushort> species = stackalloc ushort[]
{
002, 005, 008, 012, 014, 016, 021, 025, 027, 035,
038, 039, 043, 044, 046, 049, 049, 051, 056, 058,
061, 063, 067, 077, 082, 083, 084, 087, 089, 091,
095, 096, 098, 101, 105, 112, 113, 114, 125, 126,
127, 130, 131, 132, 133, 148, 163, 165, 168, 175,
178, 184, 190, 191, 194, 195, 202, 203, 205, 206,
209, 213, 214, 215, 215, 216, 218, 219, 221, 222,
224, 225, 227, 231, 235, 236, 247, 262, 267, 268,
274, 281, 284, 286, 290, 294, 297, 299, 302, 303,
303, 307, 310, 313, 314, 317, 323, 326, 328, 332,
336, 342, 352, 353, 356, 357, 359, 361, 363, 372,
375, 400, 404, 415, 417, 419, 423, 426, 437, 442,
444, 447, 452, 454, 459, 506, 510, 511, 513, 515,
517, 520, 523, 525, 527, 530, 531, 536, 538, 539,
541, 544, 548, 551, 556, 557, 561, 569, 572, 575,
578, 581, 586, 587, 596, 597, 600, 608, 611, 614,
618, 619, 621, 623, 624, 627, 629, 636, 651, 654,
657, 660, 662, 662, 668, 673, 674, 677, 682, 684,
686, 689, 694, 701, 702, 702, 705, 707, 708, 710,
712, 714,
};
var slots = new EncounterSlot6XY[species.Length + SpeciesFormSlots];
int i = 0;
for (; i < species.Length; i++)
slots[i] = new EncounterSlot6XY(this, species[i], 0, 30, 30);
// Floette has 3 separate forms (RBY)
slots[i++] = new EncounterSlot6XY(this, (int)Species.Floette, 0, 30, 30);
slots[i++] = new EncounterSlot6XY(this, (int)Species.Floette, 1, 30, 30);
slots[i++] = new EncounterSlot6XY(this, (int)Species.Floette, 3, 30, 30);
// Region Random Vivillon
slots[i] = new EncounterSlot6XY(this, (int)Species.Vivillon, EncounterSlot.FormVivillon, 30, 30);
return slots;
}
private EncounterSlot6XY[] ReadSlots(ReadOnlySpan<byte> data)
{
const int size = 4;
int count = (data.Length - 4) / size;
var slots = new EncounterSlot6XY[count];
for (int i = 0; i < slots.Length; i++)
{
int offset = 4 + (size * i);
var entry = data.Slice(offset, size);
ushort species = ReadUInt16LittleEndian(entry);
byte form = (byte)(species >> 11);
species &= 0x3FF;
byte min = entry[2];
byte max = entry[3];
slots[i] = new EncounterSlot6XY(this, species, form, min, max);
}
return slots;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, EvoCriteria[] chain)
{
foreach (var slot in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (!slot.IsLevelWithinRange(pkm.Met_Level))
break;
if (slot.Form != evo.Form && !slot.IsRandomUnspecificForm && slot.Species is not ((int)Species.Burmy or (int)Species.Furfrou))
{
// Only slot that can be form-mismatched via Pressure is Flabébé
if (slot.Species != (int)Species.Flabébé)
break;
var maxLevel = slot.LevelMax;
if (!ExistsPressureSlot(evo, ref maxLevel))
break;
if (maxLevel != pkm.Met_Level)
break;
yield return slot.CreatePressureFormCopy(evo.Form);
break;
}
yield return slot;
break;
}
}
}
private bool ExistsPressureSlot(EvoCriteria evo, ref byte level)
{
bool existsForm = false;
foreach (var z in Slots)
{
if (z.Species != evo.Species)
continue;
if (z.Form == evo.Form)
continue;
if (z.LevelMax < level)
continue;
level = z.LevelMax;
existsForm = true;
}
return existsForm;
}
}
}