mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-07-13 11:20:49 -05:00
Misc tweaks
no functional change
This commit is contained in:
parent
ec4cfc807f
commit
fd72b6ea46
|
|
@ -6,8 +6,14 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// Indicates the group of game(s) that the move was learned in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Each unique set of learnsets has a unique <see cref="LearnEnvironment"/> value.
|
||||
/// </remarks>
|
||||
public enum LearnEnvironment : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Sentinel value indicating no environment specified/initial environment.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/* Gen1 */ RB, YW,
|
||||
|
|
@ -27,7 +33,14 @@ public enum LearnEnvironment : byte
|
|||
/// </summary>
|
||||
public static class LearnEnvironmentExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether the <see cref="LearnEnvironment"/> is specified (not <see cref="None"/>), and thus worth indicating.
|
||||
/// </summary>
|
||||
public static bool IsSpecified(this LearnEnvironment value) => value is not None;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the generation number (1-n) for the given <see cref="LearnEnvironment"/>.
|
||||
/// </summary>
|
||||
public static byte GetGeneration(this LearnEnvironment value) => value switch
|
||||
{
|
||||
RB or YW => 1,
|
||||
|
|
@ -42,6 +55,9 @@ public static class LearnEnvironmentExtensions
|
|||
_ => 0,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the evolution criteria for the given <see cref="LearnEnvironment"/> from the provided <see cref="EvolutionHistory"/>.
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<EvoCriteria> GetEvolutions(this LearnEnvironment value, EvolutionHistory history) => value switch
|
||||
{
|
||||
RB or YW => history.Gen1,
|
||||
|
|
|
|||
|
|
@ -13,12 +13,18 @@ namespace PKHeX.Core;
|
|||
/// <param name="Argument">Conditions in which the move was learned</param>
|
||||
public readonly record struct MoveLearnInfo(LearnMethod Method, LearnEnvironment Environment, byte Argument = 0)
|
||||
{
|
||||
/// <inheritdoc cref="Summarize(StringBuilder, ReadOnlySpan{char})"/>
|
||||
public void Summarize(StringBuilder sb)
|
||||
{
|
||||
var localized = GetLocalizedMethod();
|
||||
Summarize(sb, localized);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Summarizes the move learn info into a human-readable format and appends it to the provided <see cref="StringBuilder"/>.
|
||||
/// </summary>
|
||||
/// <param name="sb">The <see cref="StringBuilder"/> to append the summary to.</param>
|
||||
/// <param name="localizedMethod">The localized string representing the learning method.</param>
|
||||
private void Summarize(StringBuilder sb, ReadOnlySpan<char> localizedMethod)
|
||||
{
|
||||
if (Environment.IsSpecified())
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
|
@ -271,21 +270,3 @@ public static uint SeekInitialSeedForIVs(ReadOnlySpan<int> ivs, uint year, uint
|
|||
return best;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores the components of an initial seed from Generation 4.
|
||||
/// </summary>
|
||||
public readonly record struct InitialSeedComponents4
|
||||
{
|
||||
[Range(0, 99)] public required byte Year { get; init; }
|
||||
[Range(1, 12)] public required byte Month { get; init; }
|
||||
[Range(1, 31)] public required byte Day { get; init; }
|
||||
[Range(0, 23)] public required byte Hour { get; init; }
|
||||
[Range(0, 59)] public required byte Minute { get; init; }
|
||||
[Range(0, 59)] public required byte Second { get; init; }
|
||||
|
||||
public required ushort Delay { get; init; } // essentially XXX-65535, but can overflow. Not that anyone waits the 30+ minutes to do that since other initial seeds are more efficient.
|
||||
|
||||
public uint ToSeed() => ClassicEraRNG.GetInitialSeed(Year, Month, Day, Hour, Minute, Second, Delay);
|
||||
public bool IsInvalid() => Month == 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,16 @@ public static uint GetPIDMale(byte genderRatio, uint nature)
|
|||
{
|
||||
// pid >= ratio is male
|
||||
// get the lowest PID that will be male for Hardy (0)
|
||||
var basePID = NatureCount * ((genderRatio / NatureCount) + 1);
|
||||
var basePID = GetBufferedBasePID(genderRatio);
|
||||
// add the desired nature to the base PID
|
||||
return basePID + nature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the starting PID for a male gender by rounding up to the next multiple of <see cref="NatureCount"/> (25).
|
||||
/// </summary>
|
||||
private static uint GetBufferedBasePID(byte genderRatio) => NatureCount * ((genderRatio / NatureCount) + 1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the PID of a Female wild encounter when the player leads a Male Pokémon with Cute Charm.
|
||||
/// </summary>
|
||||
|
|
@ -39,6 +44,15 @@ private static byte GetGenderRatio(ushort species)
|
|||
: EntityGender.GetGenderRatio(species); // fallback (don't bother trying to devolve to Gen1-4 encounter species)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the PID is within the range created by the buffered PID algorithm.
|
||||
/// </summary>
|
||||
/// <param name="pid">Obtained PID</param>
|
||||
/// <returns>True if the PID is within the Azurill male range.</returns>
|
||||
/// <remarks>
|
||||
/// The game starts with a PID of 200, and then adds nature (0-24) to it, resulting in a range of 200-224.
|
||||
/// 200 is via <see cref="GetBufferedBasePID"/> with the 3F:1M gender ratio of Azurill (191).
|
||||
/// </remarks>
|
||||
private static bool IsAzurillMale(uint pid) => pid is >= 0xC8 and <= 0xE0;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores the components of an initial seed from Generation 4.
|
||||
/// </summary>
|
||||
public readonly record struct InitialSeedComponents4
|
||||
{
|
||||
[Range(0, 99)] public required byte Year { get; init; }
|
||||
[Range(1, 12)] public required byte Month { get; init; }
|
||||
[Range(1, 31)] public required byte Day { get; init; }
|
||||
[Range(0, 23)] public required byte Hour { get; init; }
|
||||
[Range(0, 59)] public required byte Minute { get; init; }
|
||||
[Range(0, 59)] public required byte Second { get; init; }
|
||||
|
||||
public required ushort Delay { get; init; } // essentially XXX-65535, but can overflow. Not that anyone waits the 30+ minutes to do that since other initial seeds are more efficient.
|
||||
|
||||
public uint ToSeed() => ClassicEraRNG.GetInitialSeed(Year, Month, Day, Hour, Minute, Second, Delay);
|
||||
public bool IsInvalid() => Month == 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,9 @@ public sealed class GenderVerifier : Verifier
|
|||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
var pi = pk.PersonalInfo;
|
||||
if (pi.Genderless != (pk.Gender == 2))
|
||||
var pi = data.PersonalInfo;
|
||||
var gender = pk.Gender;
|
||||
if (pi.Genderless != (gender == 2))
|
||||
{
|
||||
// D/P/Pt & HG/SS Shedinja glitch -- only generation 4 spawns
|
||||
bool ignore = pk is { Format: 4, Species: (int)Species.Shedinja } && pk.MetLevel != pk.CurrentLevel;
|
||||
|
|
@ -36,7 +37,7 @@ public override void Verify(LegalityAnalysis data)
|
|||
}
|
||||
|
||||
// Check fixed gender cases
|
||||
if ((pi.OnlyFemale && pk.Gender != 1) || (pi.OnlyMale && pk.Gender != 0))
|
||||
if ((pi.OnlyFemale && gender != 1) || (pi.OnlyMale && gender != 0))
|
||||
data.AddLine(GetInvalid(LGenderInvalidNone));
|
||||
}
|
||||
|
||||
|
|
@ -65,22 +66,22 @@ private static bool IsValidGenderPID(LegalityAnalysis data)
|
|||
return true;
|
||||
}
|
||||
|
||||
private static bool IsValidFixedGenderFromBiGender(PKM pk, ushort original)
|
||||
private static bool IsValidFixedGenderFromBiGender(PKM pk, ushort originalSpecies)
|
||||
{
|
||||
var current = pk.Gender;
|
||||
if (current == 2) // shedinja, genderless
|
||||
return true;
|
||||
var gender = EntityGender.GetFromPID(original, pk.EncryptionConstant);
|
||||
var gender = EntityGender.GetFromPID(originalSpecies, pk.EncryptionConstant);
|
||||
return gender == current;
|
||||
}
|
||||
|
||||
private static bool IsValidGenderMismatch(PKM pk) => pk.Species switch
|
||||
{
|
||||
// Shedinja evolution gender glitch, should match original Gender
|
||||
(int) Species.Shedinja when pk.Format == 4 => pk.Gender == EntityGender.GetFromPIDAndRatio(pk.EncryptionConstant, 0x7F), // 50M-50F
|
||||
(int) Species.Shedinja when pk.Format == 4 => pk.Gender == EntityGender.GetFromPIDAndRatio(pk.EncryptionConstant, EntityGender.HH), // 1:1
|
||||
|
||||
// Evolved from Azurill after transferring to keep gender
|
||||
(int) Species.Marill or (int) Species.Azumarill when pk.Format >= 6 => pk.Gender == 1 && (pk.EncryptionConstant & 0xFF) > 0x3F,
|
||||
(int) Species.Marill or (int) Species.Azumarill when pk.Format >= 6 => pk.Gender == 1 && (pk.EncryptionConstant & 0xFF) > EntityGender.MM, // 3:1
|
||||
|
||||
_ => false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ public sealed class GroundTileVerifier : Verifier
|
|||
|
||||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
// Only specific encounters in Generation 4 set a GroundTile value.
|
||||
// This value is retained after transferring to Gen5, but not beyond.
|
||||
// Gen3 and Gen5 encounters should never have a Tile value.
|
||||
if (data.Entity is not IGroundTile e)
|
||||
return;
|
||||
var enc = data.EncounterMatch;
|
||||
|
|
@ -27,7 +30,12 @@ public override void Verify(LegalityAnalysis data)
|
|||
/// <returns>True if stored ground tile value is permitted.</returns>
|
||||
public static bool IsGroundTileValid(IEncounterTemplate enc, IGroundTile e)
|
||||
{
|
||||
var type = enc is IGroundTypeTile t ? t.GroundTile : GroundTileAllowed.None;
|
||||
return type.Contains(e.GroundTile);
|
||||
if (enc is not IGroundTypeTile t)
|
||||
return e.GroundTile is GroundTileType.None;
|
||||
|
||||
var allow = t.GroundTile;
|
||||
if (allow is GroundTileAllowed.None)
|
||||
return e.GroundTile is GroundTileType.None;
|
||||
return allow.Contains(e.GroundTile);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,26 +21,34 @@ public override void Verify(LegalityAnalysis data)
|
|||
{
|
||||
VerifyMiscEggCommon(data);
|
||||
|
||||
// No egg have contest stats from the encounter.
|
||||
if (pk is IContestStatsReadOnly s && s.HasContestStats())
|
||||
data.AddLine(GetInvalid(LEggContest, Egg));
|
||||
|
||||
// Cannot transfer eggs across contexts (must be hatched).
|
||||
var e = data.EncounterOriginal;
|
||||
if (e.Context != pk.Context)
|
||||
data.AddLine(GetInvalid(LTransferEggVersion, Egg));
|
||||
|
||||
switch (pk)
|
||||
{
|
||||
case SK2 or CK3 or XK3 or BK4 or RK4 or PA8: // Side Game: No Eggs
|
||||
// Side Game: No Eggs
|
||||
case SK2 or CK3 or XK3 or BK4 or RK4 when e.Context == pk.Context:
|
||||
data.AddLine(GetInvalid(LTransferEggVersion, Egg));
|
||||
break;
|
||||
case PK5 pk5 when pk5.PokeStarFame != 0:
|
||||
data.AddLine(GetInvalid(LEggShinyPokeStar, Egg));
|
||||
|
||||
// All Eggs are Japanese and flagged specially for localized string
|
||||
case PK3 when pk.Language != 1:
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, LanguageID.Japanese, (LanguageID)pk.Language), Egg));
|
||||
break;
|
||||
|
||||
// Cannot obtain Shiny Leaf or Pokeathlon Stats as Egg
|
||||
case PK4 pk4:
|
||||
if (pk4.ShinyLeaf != 0)
|
||||
data.AddLine(GetInvalid(LEggShinyLeaf, Egg));
|
||||
if (pk4.PokeathlonStat != 0)
|
||||
data.AddLine(GetInvalid(LEggPokeathlon, Egg));
|
||||
break;
|
||||
case PK3 when pk.Language != 1: // All Eggs are Japanese and flagged specially for localized string
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, LanguageID.Japanese, (LanguageID)pk.Language), Egg));
|
||||
break;
|
||||
}
|
||||
|
||||
if (pk is IHomeTrack { HasTracker: true })
|
||||
|
|
@ -59,8 +67,8 @@ public override void Verify(LegalityAnalysis data)
|
|||
case PK9 pk9: VerifyStats9(data, pk9); break;
|
||||
}
|
||||
|
||||
if (pk.Format >= 6)
|
||||
VerifyFullness(data, pk);
|
||||
if (pk is IFullnessEnjoyment fe) // 6-8
|
||||
VerifyFullness(data, pk, fe);
|
||||
|
||||
var enc = data.EncounterMatch;
|
||||
if (enc is IEncounterServerDate { IsDateRestricted: true } encounterDate)
|
||||
|
|
@ -209,6 +217,12 @@ private void VerifyIsMovesetAllowed(LegalityAnalysis data, SK2 sk2)
|
|||
private static void VerifyStats5(LegalityAnalysis data, PK5 pk5)
|
||||
{
|
||||
var enc = data.EncounterMatch;
|
||||
|
||||
// Cannot participate in Pokestar Studios as Egg
|
||||
if (pk5.IsEgg && pk5.PokeStarFame != 0)
|
||||
data.AddLine(GetInvalid(LEggShinyPokeStar, Egg));
|
||||
|
||||
// Ensure NSparkle is only present on N's encounters.
|
||||
if (enc is EncounterStatic5N)
|
||||
{
|
||||
if (!pk5.NSparkle)
|
||||
|
|
@ -351,7 +365,7 @@ private static bool IsObedienceLevelValid(PKM pk, byte current, int expectObey)
|
|||
private void VerifyMiscPokerus(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
if (pk.Format == 1)
|
||||
if (pk.Format == 1) // not stored in Gen1 format
|
||||
return;
|
||||
|
||||
var strain = pk.PokerusStrain;
|
||||
|
|
@ -622,10 +636,8 @@ public void VerifyVersionEvolution(LegalityAnalysis data)
|
|||
}
|
||||
}
|
||||
|
||||
private static void VerifyFullness(LegalityAnalysis data, PKM pk)
|
||||
private static void VerifyFullness(LegalityAnalysis data, PKM pk, IFullnessEnjoyment fe)
|
||||
{
|
||||
if (pk is not IFullnessEnjoyment fe)
|
||||
return;
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
if (fe.Fullness != 0)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user