PKHeX/PKHeX.Core/Legality/Encounters/EncounterStatic/EncounterStatic3.cs
Kurt 9166d0eb64
Refactoring: Move Source (Legality) (#3560)
Rewrites a good amount of legality APIs pertaining to:
* Legal moves that can be learned
* Evolution chains & cross-generation paths
* Memory validation with forgotten moves

In generation 8, there are 3 separate contexts an entity can exist in: SW/SH, BD/SP, and LA. Not every entity can cross between them, and not every entity from generation 7 can exist in generation 8 (Gogoat, etc). By creating class models representing the restrictions to cross each boundary, we are able to better track and validate data.

The old implementation of validating moves was greedy: it would iterate for all generations and evolutions, and build a full list of every move that can be learned, storing it on the heap. Now, we check one game group at a time to see if the entity can learn a move that hasn't yet been validated. End result is an algorithm that requires 0 allocation, and a smaller/quicker search space.

The old implementation of storing move parses was inefficient; for each move that was parsed, a new object is created and adjusted depending on the parse. Now, move parse results are `struct` and store the move parse contiguously in memory. End result is faster parsing and 0 memory allocation.

* `PersonalTable` objects have been improved with new API methods to check if a species+form can exist in the game.
* `IEncounterTemplate` objects have been improved to indicate the `EntityContext` they originate in (similar to `Generation`).
* Some APIs have been extended to accept `Span<T>` instead of Array/IEnumerable
2022-08-03 16:15:27 -07:00

85 lines
2.5 KiB
C#

using System;
using System.Linq;
namespace PKHeX.Core;
/// <summary>
/// Generation 3 Static Encounter
/// </summary>
/// <inheritdoc cref="EncounterStatic"/>
public sealed record EncounterStatic3 : EncounterStatic
{
public override int Generation => 3;
public override EntityContext Context => EntityContext.Gen3;
public bool Roaming { get; init; }
public EncounterStatic3(ushort species, byte level, GameVersion game) : base(game)
{
Species = species;
Level = level;
}
protected override bool IsMatchEggLocation(PKM pk)
{
if (pk.Format == 3)
return !pk.IsEgg || EggLocation == 0 || EggLocation == pk.Met_Location;
return base.IsMatchEggLocation(pk);
}
protected override bool IsMatchLevel(PKM pk, EvoCriteria evo)
{
if (pk.Format != 3) // Met Level lost on PK3=>PK4
return Level <= evo.LevelMax;
if (EggEncounter)
return pk.Met_Level == 0 && pk.CurrentLevel >= 5; // met level 0, origin level 5
return pk.Met_Level == Level;
}
protected override bool IsMatchLocation(PKM pk)
{
if (EggEncounter)
return true;
if (pk.Format != 3)
return true; // transfer location verified later
var met = pk.Met_Location;
if (!Roaming)
return Location == met;
var table = Version <= GameVersion.E ? Roaming_MetLocation_RSE : Roaming_MetLocation_FRLG;
return table.Contains(met);
}
protected override bool IsMatchPartial(PKM pk)
{
if (Gift && pk.Ball != Ball)
return true;
return base.IsMatchPartial(pk);
}
protected override void SetMetData(PKM pk, int level, DateTime today)
{
pk.Met_Level = level;
pk.Met_Location = !Roaming ? Location : (Version <= GameVersion.E ? Roaming_MetLocation_RSE : Roaming_MetLocation_FRLG)[0];
}
private static readonly int[] Roaming_MetLocation_FRLG =
{
// Route 1-25 encounter is possible either in grass or on water
101,102,103,104,105,106,107,108,109,110,
111,112,113,114,115,116,117,118,119,120,
121,122,123,124,125,
};
private static readonly int[] Roaming_MetLocation_RSE =
{
// Roaming encounter is possible in tall grass and on water
// Route 101-138
16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47, 48, 49,
};
}