PKHeX/PKHeX.Core/Legality/Encounters/Generator/EncounterFinder.cs
Kurt 02420d3e93
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases

Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization

* Handle bits more obviously without null

* Make SaveFile.BAK explicitly readonly again

* merge constructor methods to have readonly fields

* Inline some properties

* More nullable handling

* Rearrange box actions

define straightforward classes to not have any null properties

* Make extrabyte reference array immutable

* Move tooltip creation to designer

* Rearrange some logic to reduce nesting

* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum

* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case

* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable

* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator

* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever

* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)

* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-16 18:47:31 -07:00

123 lines
5.3 KiB
C#

using System.Linq;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core
{
/// <summary>
/// Finds matching <see cref="IEncounterable"/> data and relevant <see cref="LegalInfo"/> for a <see cref="PKM"/>.
/// </summary>
public static class EncounterFinder
{
/// <summary>
/// Iterates through all possible encounters until a sufficient match is found
/// </summary>
/// <remarks>
/// The iterator lazily finds matching encounters, then verifies secondary checks to weed out any nonexact matches.
/// </remarks>
/// <param name="pkm">Source data to find a match for</param>
/// <returns>
/// Information containing the matched encounter and any parsed checks.
/// If no clean match is found, the last checked match is returned.
/// If no match is found, an invalid encounter object is returned.
/// </returns>
public static LegalInfo FindVerifiedEncounter(PKM pkm)
{
var info = new LegalInfo(pkm);
var encounters = EncounterGenerator.GetEncounters(pkm, info);
using var encounter = new PeekEnumerator<IEncounterable>(encounters);
if (!encounter.PeekIsNext())
return VerifyWithoutEncounter(pkm, info);
var EncounterValidator = EncounterVerifier.GetEncounterVerifierMethod(pkm);
while (encounter.MoveNext())
{
info.EncounterMatch = encounter.Current;
var e = EncounterValidator(pkm, info);
if (!e.Valid && encounter.PeekIsNext())
{
info.Reject(e);
continue;
}
info.Parse.Add(e);
if (VerifySecondaryChecks(pkm, info, encounter))
break; // passes
}
if (!info.FrameMatches && info.EncounterMatch is EncounterSlot && pkm.Version != (int)GameVersion.CXD) // if false, all valid RNG frame matches have already been consumed
info.Parse.Add(new CheckResult(ParseSettings.RNGFrameNotFound, LEncConditionBadRNGFrame, CheckIdentifier.PID)); // todo for further confirmation
if (!info.PIDIVMatches) // if false, all valid PIDIV matches have already been consumed
info.Parse.Add(new CheckResult(Severity.Invalid, LPIDTypeMismatch, CheckIdentifier.PID));
return info;
}
/// <summary>
/// Checks supplementary info to see if the encounter is still valid.
/// </summary>
/// <remarks>
/// When an encounter is initially validated, only encounter-related checks are performed.
/// By checking Moves, Evolution, and <see cref="PIDIV"/> data, a best match encounter can be found.
/// If the encounter is not valid, the method will not reject it unless another encounter is available to check.
/// </remarks>
/// <param name="pkm">Source data to check the match for</param>
/// <param name="info">Information containing the matched encounter</param>
/// <param name="iterator">Peekable iterator </param>
/// <returns>Indication whether or not the encounter passes secondary checks</returns>
private static bool VerifySecondaryChecks(PKM pkm, LegalInfo info, PeekEnumerator<IEncounterable> iterator)
{
if (pkm.Format >= 6)
{
info.Relearn = VerifyRelearnMoves.VerifyRelearn(pkm, info);
if (info.Relearn.Any(z => !z.Valid) && iterator.PeekIsNext())
return false;
}
else
{
for (int i = 0; i < 4; i++)
info.Relearn[i] = new CheckResult(CheckIdentifier.RelearnMove);
}
info.Moves = VerifyCurrentMoves.VerifyMoves(pkm, info);
if (info.Moves.Any(z => !z.Valid) && iterator.PeekIsNext())
return false;
var evo = EvolutionVerifier.VerifyEvolution(pkm, info);
if (!evo.Valid && iterator.PeekIsNext())
return false;
info.Parse.Add(evo);
return true;
}
/// <summary>
/// Returns legality info for an unmatched encounter scenario, including a hint as to what the actual match could be.
/// </summary>
/// <param name="pkm">Source data to check the match for</param>
/// <param name="info">Information containing the unmatched encounter</param>
/// <returns>Updated information pertaining to the unmatched encounter</returns>
private static LegalInfo VerifyWithoutEncounter(PKM pkm, LegalInfo info)
{
info.EncounterMatch = new EncounterInvalid(pkm);
string hint = GetHintWhyNotFound(pkm);
info.Parse.Add(new CheckResult(Severity.Invalid, hint, CheckIdentifier.Encounter));
info.Relearn = VerifyRelearnMoves.VerifyRelearn(pkm, info);
info.Moves = VerifyCurrentMoves.VerifyMoves(pkm, info);
return info;
}
private static string GetHintWhyNotFound(PKM pkm)
{
if (pkm.WasGiftEgg)
return LEncGift;
if (pkm.WasEventEgg)
return LEncGiftEggEvent;
if (pkm.WasEvent)
return LEncGiftNotFound;
return LEncInvalid;
}
}
}