PKHeX/PKHeX.Core/Legality/RNG/Frame/FrameGenerator.cs
Kurt f632aedd15
Encounter Templates: Searching and Creating (#3955)
We implement simple state machine iterators to iterate through every split type encounter array, and more finely control the path we iterate through. And, by using generics, we can have the compiler generate optimized code to avoid virtual calls.

In addition to this, we shift away from the big-5 encounter types and not inherit from an abstract class. This allows for creating a PK* of a specific type and directly writing properties (no virtual calls). Plus we can now fine-tune each encounter type to call specific code, and not have to worry about future game encounter types bothering the generation routines.
2023-08-12 16:01:16 -07:00

101 lines
3.4 KiB
C#

using System;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
/// <summary>
/// Generator class for Gen3/4 Frame patterns
/// </summary>
public sealed class FrameGenerator
{
public uint Nature { get; init; }
public readonly bool Gendered;
public readonly int GenderHigh;
public readonly int GenderLow;
public readonly bool DPPt;
public readonly bool AllowLeads;
public readonly FrameType FrameType;
public readonly bool Safari3;
public Frame GetFrame(uint seed, LeadRequired lead) => new(seed, FrameType, lead);
public Frame GetFrame(uint seed, LeadRequired lead, ushort esv, uint origin) => GetFrame(seed, lead, esv, esv, origin);
public Frame GetFrame(uint seed, LeadRequired lead, ushort esv, ushort lvl, uint origin) => new(seed, FrameType, lead)
{
RandESV = esv,
RandLevel = lvl,
OriginSeed = origin,
};
/// <summary>
/// Gets the Search Criteria parameters necessary for generating <see cref="SeedInfo"/> and <see cref="Frame"/> objects for Gen3/4 mainline games.
/// </summary>
/// <param name="pk"><see cref="PKM"/> object containing various accessible information required for the encounter.</param>
/// <returns>Object containing search criteria to be passed by reference to search/filter methods.</returns>
public FrameGenerator(PKM pk)
{
var version = (GameVersion)pk.Version;
switch (version)
{
// Method H
case R or S or E or FR or LG:
DPPt = false;
FrameType = FrameType.MethodH;
Safari3 = pk.Ball == 5 && version is not (FR or LG);
if (version != E)
return;
AllowLeads = true;
// Cute Charm waits for gender too!
var gender = pk.Gender;
bool gendered = gender != 2;
if (!gendered)
return;
var gr = pk.PersonalInfo.Gender;
Gendered = true;
GenderLow = GetGenderMinMax(gender, gr, false);
GenderHigh = GetGenderMinMax(gender, gr, true);
return;
// Method J
case D or P or Pt:
DPPt = true;
AllowLeads = true;
FrameType = FrameType.MethodJ;
return;
// Method K
case HG or SS:
DPPt = false;
AllowLeads = true;
FrameType = FrameType.MethodK;
return;
default:
throw new ArgumentOutOfRangeException(nameof(version), version, "Unknown version.");
}
}
/// <summary>
/// Gets the span of values for a given Gender
/// </summary>
/// <param name="gender">Gender</param>
/// <param name="ratio">Gender Ratio</param>
/// <param name="max">Return Max (or Min)</param>
/// <returns>Returns the maximum or minimum gender value that corresponds to the input gender ratio.</returns>
private static int GetGenderMinMax(int gender, int ratio, bool max) => ratio switch
{
PersonalInfo.RatioMagicMale => max ? 255 : 0,
PersonalInfo.RatioMagicFemale => max ? 255 : 0,
PersonalInfo.RatioMagicGenderless => max ? 255 : 0,
_ => gender switch
{
0 => max ? 255 : ratio, // male
1 => max ? ratio - 1 : 0, // female
_ => max ? 255 : 0,
},
};
}