PKHeX/PKHeX.Core/Legality/Areas/EncounterAreaGB.cs
Kurt 734aa33898 Split encounter areas, relocate slot finding to obj
Now logic is reasonably split, and each format of area has its own way
of yielding slots

Too much junk with checking flute boosts or catch combo applicability;
just let the area dictate how slots match.
2019-09-12 23:20:52 -07:00

132 lines
No EOL
5.3 KiB
C#

using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
/// <inheritdoc />
/// <summary>
/// <see cref="GameVersion.GBCartEraOnly"/> encounter area
/// </summary>
public abstract class EncounterAreaGB : EncounterArea
{
/// <summary>
/// RBY Format Slot Getter from data.
/// </summary>
/// <param name="data">Byte array containing complete slot data table.</param>
/// <param name="ofs">Offset to start reading from.</param>
/// <param name="count">Amount of slots to read.</param>
/// <param name="t">Type of encounter slot.</param>
/// <param name="rate">Slot type encounter rate.</param>
/// <returns>Array of encounter slots.</returns>
protected static EncounterSlot1[] ReadSlots1(byte[] data, ref int ofs, int count, SlotType t, int rate)
{
EncounterSlot1[] slots = new EncounterSlot1[count];
for (int i = 0; i < count; i++)
{
int lvl = data[ofs++];
int spec = data[ofs++];
slots[i] = new EncounterSlot1
{
LevelMax = t == SlotType.Surf ? lvl + 4 : lvl,
LevelMin = lvl,
Species = spec,
Type = t,
Rate = rate,
SlotNumber = i,
};
}
return slots;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<DexLevel> vs, int minLevel = 0)
{
if (minLevel == 0) // any
return Slots.Where(slot => vs.Any(evo => evo.Species == slot.Species));
var Gen1Version = GameVersion.RBY;
bool RBDragonair = false;
if (minLevel != 0 && !FilterGBSlotsCatchRate(pkm, ref vs, ref Gen1Version, ref RBDragonair))
return Enumerable.Empty<EncounterSlot>();
var encounterSlots = GetMatchFromEvoLevel(pkm, vs, minLevel);
return GetFilteredSlots(pkm, encounterSlots, Gen1Version, RBDragonair).OrderBy(slot => slot.LevelMin); // prefer lowest levels
}
private static bool FilterGBSlotsCatchRate(PKM pkm, ref IReadOnlyList<DexLevel> vs, ref GameVersion Gen1Version, ref bool RBDragonair)
{
if (!(pkm is PK1 pk1) || !pkm.Gen1_NotTradeback)
return true;
// Pure gen 1, slots can be filter by catch rate
var rate = pk1.Catch_Rate;
switch (pkm.Species)
{
// Pikachu
case (int)Species.Pikachu when rate == 163:
case (int)Species.Raichu when rate == 163:
return false; // Yellow Pikachu is not a wild encounter
// Kadabra (YW)
case (int)Species.Kadabra when rate == 96:
case (int)Species.Alakazam when rate == 96:
vs = vs.Where(s => s.Species == (int)Species.Kadabra).ToArray();
Gen1Version = GameVersion.YW;
return true;
// Kadabra (RB)
case (int)Species.Kadabra when rate == 100:
case (int)Species.Alakazam when rate == 100:
vs = vs.Where(s => s.Species == (int)Species.Kadabra).ToArray();
Gen1Version = GameVersion.RB;
return true;
// Dragonair (YW)
case (int)Species.Dragonair when rate == 27:
case (int)Species.Dragonite when rate == 27:
vs = vs.Where(s => s.Species == (int)Species.Dragonair).ToArray(); // Yellow Dragonair, ignore Dratini encounters
Gen1Version = GameVersion.YW;
return true;
// Dragonair (RB)
case (int)Species.Dragonair:
case (int)Species.Dragonite:
// Red blue dragonair have the same catch rate as dratini, it could also be a dratini from any game
vs = vs.Where(s => rate == PersonalTable.RB[s.Species].CatchRate).ToArray();
RBDragonair = true;
return true;
default:
vs = vs.Where(s => rate == PersonalTable.RB[s.Species].CatchRate).ToArray();
return true;
}
}
private static IEnumerable<EncounterSlot> GetFilteredSlots(PKM pkm, IEnumerable<EncounterSlot> slots,
GameVersion Gen1Version, bool RBDragonair)
{
int gen = pkm.GenNumber;
switch (gen)
{
case 1:
if (Gen1Version != GameVersion.RBY)
slots = slots.Where(slot => Gen1Version.Contains(slot.Version));
// Red Blue dragonair or dratini from any gen 1 games
if (RBDragonair)
return slots.Where(slot => GameVersion.RB.Contains(slot.Version) || slot.Species == 147);
return slots;
case 2:
if (pkm is PK2 pk2 && pk2.Met_TimeOfDay != 0)
return slots.Where(slot => ((EncounterSlot1)slot).Time.Contains(pk2.Met_TimeOfDay));
return slots;
default:
return slots;
}
}
}
}