mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-23 12:33:06 +00:00
fa9a809751
* Extract/encapsulate inventory legal arrays to interface+class Hiding them behind methods allows these to be left as ReadOnlySpan<ushort> and thus never allocate on the heap. Also refactors out some logic for checking if an item is legal. End result feels more maintainable and is less bloaty (no more passing in a nullable func) Batch editing * Add HasType filter ``` =HasType=11 .HeldItem=Meadow Plate ``` slaps a meadow plate on any pkm with grass type Use `=PersonalType1=11` for only primary grass types; only-secondary-type grass will not match it.
169 lines
5.6 KiB
C#
169 lines
5.6 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, IMemorySpeciesArea
|
|
{
|
|
public readonly EncounterSlot6XY[] 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 = ReadInt16LittleEndian(data);
|
|
Type = (SlotType)data[2];
|
|
|
|
Slots = ReadSlots(data);
|
|
}
|
|
|
|
private EncounterSlot6XY[] LoadSafariSlots()
|
|
{
|
|
const int SpeciesFormSlots = 4;
|
|
|
|
// Single form species
|
|
var species = AllSpecies;
|
|
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 static ReadOnlySpan<ushort> AllSpecies => new 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,
|
|
|
|
// Handle Floette (670) and Vivillon (666) separately
|
|
};
|
|
|
|
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<EncounterSlot6XY> GetMatchingSlots(PKM pk, EvoCriteria[] chain)
|
|
{
|
|
foreach (var slot in Slots)
|
|
{
|
|
foreach (var evo in chain)
|
|
{
|
|
if (slot.Species != evo.Species)
|
|
continue;
|
|
|
|
if (!slot.IsLevelWithinRange(pk.Met_Level))
|
|
break;
|
|
|
|
if (slot.Form != evo.Form && slot is { IsRandomUnspecificForm: false, Species: 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 != pk.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;
|
|
}
|
|
|
|
public bool HasSpecies(ushort species)
|
|
{
|
|
foreach (var slot in Slots)
|
|
{
|
|
if (slot.Species == species)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|