mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-09-30 16:47:08 -05:00
Misc tweaks
No functional change
This commit is contained in:
@@ -216,6 +216,14 @@ private bool IsMatchEggLocation(PKM pk)
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Language obtained by the trainer will be of a foreign language ID.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Does NOT indicate for bugged D/P English origin, which is Japanese.
|
||||
/// </remarks>
|
||||
public bool IsLanguageSwap => Species is (ushort)Core.Species.Magikarp or (ushort)Core.Species.Pikachu;
|
||||
|
||||
public int DetectOriginalLanguage(PKM pk)
|
||||
{
|
||||
int lang = pk.Language;
|
||||
|
||||
@@ -24,12 +24,22 @@ namespace PKHeX.Core;
|
||||
public bool IsDiscardRequired(byte format) => format <= 2 && Options.HasFlag(OriginOptions.ForceDiscard);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options for <see cref="EvolutionOrigin"/> to modify logic checks based on the source of the <see cref="EvolutionOrigin"/> constructor input.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum OriginOptions : byte
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <inheritdoc cref="EvolutionOrigin.SkipChecks"/>
|
||||
SkipChecks = 1 << 0,
|
||||
|
||||
/// <inheritdoc cref="EvolutionOrigin.IsDiscardRequired"/>
|
||||
ForceDiscard = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Options relevant when checking for an encounter template, which bypasses logic checks against an Entity.
|
||||
/// </summary>
|
||||
EncounterTemplate = SkipChecks | ForceDiscard,
|
||||
}
|
||||
|
||||
@@ -18,10 +18,13 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
if (pk.OriginalTrainerGender == 1)
|
||||
data.AddLine(GetInvalid(LG3OTGender, CheckIdentifier.Trainer));
|
||||
|
||||
// Trainer ID is checked in another verifier. Don't duplicate it here.
|
||||
}
|
||||
|
||||
private static void VerifyStarterXD(LegalityAnalysis data)
|
||||
{
|
||||
// The starter in XD must have the correct PIDIV type.
|
||||
var info = data.Info.PIDIV;
|
||||
if (info.Type is not (PIDType.CXD or PIDType.CXD_ColoStarter))
|
||||
return; // already flagged as invalid
|
||||
|
||||
@@ -17,17 +17,19 @@ public override void Verify(LegalityAnalysis data)
|
||||
data.AddLine(GetInvalid(LItemEgg));
|
||||
|
||||
if (!ItemRestrictions.IsHeldItemAllowed(item, context: pk.Context))
|
||||
{
|
||||
data.AddLine(GetInvalid(LItemUnreleased));
|
||||
}
|
||||
else if (pk.Format == 3 && item == 175) // Enigma Berry
|
||||
{
|
||||
// A Pokémon holding this Berry cannot be traded to Pokémon Colosseum or Pokémon XD: Gale of Darkness, nor can it be stored in Pokémon Box Ruby & Sapphire.
|
||||
if (pk is CK3 or XK3)
|
||||
data.AddLine(GetInvalid(LItemUnreleased));
|
||||
else
|
||||
VerifyEReaderBerry(data);
|
||||
}
|
||||
else if (item == 175 && pk is G3PKM g3) // Enigma Berry
|
||||
VerifyEnigmaGen3(data, g3);
|
||||
}
|
||||
|
||||
private void VerifyEnigmaGen3(LegalityAnalysis data, G3PKM g3)
|
||||
{
|
||||
// A Pokémon holding this Berry cannot be traded to Pokémon Colosseum or Pokémon XD: Gale of Darkness,
|
||||
// nor can it be stored in Pokémon Box Ruby & Sapphire.
|
||||
if (g3 is CK3 or XK3 || ParseSettings.ActiveTrainer is SAV3RSBox)
|
||||
data.AddLine(GetInvalid(LItemUnreleased));
|
||||
else
|
||||
VerifyEReaderBerry(data);
|
||||
}
|
||||
|
||||
private void VerifyEReaderBerry(LegalityAnalysis data)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.GameVersion;
|
||||
using static PKHeX.Core.LanguageID;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -13,21 +15,21 @@ public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
var pk = data.Entity;
|
||||
var originalGeneration = data.Info.Generation;
|
||||
var currentLanguage = pk.Language;
|
||||
var maxLanguageID = Legal.GetMaxLanguageID(originalGeneration);
|
||||
var currentLanguage = (LanguageID)pk.Language;
|
||||
var maxLanguageID = (LanguageID)Legal.GetMaxLanguageID(originalGeneration);
|
||||
var enc = data.EncounterMatch;
|
||||
if (!IsValidLanguageID(currentLanguage, maxLanguageID, pk, enc))
|
||||
{
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"<={(LanguageID)maxLanguageID}", (LanguageID)currentLanguage)));
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"<={maxLanguageID}", currentLanguage)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Korean Gen4 games can not trade with other Gen4 languages, but can use Pal Park with any Gen3 game/language.
|
||||
if (pk.Format == 4 && enc.Generation == 4 && !IsValidGen4Korean(currentLanguage)
|
||||
&& enc is not EncounterTrade4PID {Species: (int)Species.Pikachu or (int)Species.Magikarp} // ger magikarp / eng pikachu
|
||||
&& enc is not EncounterTrade4PID { IsLanguageSwap: true } // ger magikarp / eng pikachu
|
||||
)
|
||||
{
|
||||
bool kor = currentLanguage == (int)LanguageID.Korean;
|
||||
bool kor = currentLanguage == Korean;
|
||||
var msgpkm = kor ? L_XKorean : L_XKoreanNon;
|
||||
var msgsav = kor ? L_XKoreanNon : L_XKorean;
|
||||
data.AddLine(GetInvalid(string.Format(LTransferOriginFInvalid0_1, msgpkm, msgsav)));
|
||||
@@ -37,24 +39,24 @@ public override void Verify(LegalityAnalysis data)
|
||||
if (originalGeneration <= 2)
|
||||
{
|
||||
// Korean Crystal does not exist, neither do Korean VC1
|
||||
if (pk is { Korean: true, Version: not (GameVersion.GD or GameVersion.SI) })
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"!={(LanguageID)currentLanguage}", (LanguageID)currentLanguage)));
|
||||
if (pk is { Korean: true, Version: not (GD or SI) })
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, $"!={currentLanguage}", currentLanguage)));
|
||||
|
||||
// Japanese VC is language locked; cannot obtain Japanese-Blue version as other languages.
|
||||
if (pk is { Japanese: false, Version: GameVersion.BU })
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, nameof(LanguageID.Japanese), (LanguageID)currentLanguage)));
|
||||
if (pk is { Japanese: false, Version: BU })
|
||||
data.AddLine(GetInvalid(string.Format(LOTLanguage, nameof(Japanese), currentLanguage)));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsValidLanguageID(int currentLanguage, int maxLanguageID, PKM pk, IEncounterTemplate enc)
|
||||
public static bool IsValidLanguageID(LanguageID currentLanguage, LanguageID maxLanguageID, PKM pk, IEncounterTemplate enc)
|
||||
{
|
||||
if (currentLanguage == (int)LanguageID.UNUSED_6)
|
||||
if (currentLanguage == UNUSED_6)
|
||||
return false; // Language ID 6 is unused.
|
||||
|
||||
if (currentLanguage > maxLanguageID)
|
||||
return false; // Language not available (yet)
|
||||
|
||||
if (currentLanguage <= (int)LanguageID.None && !(enc is EncounterTrade5BW && EncounterTrade5BW.IsValidMissingLanguage(pk)))
|
||||
if (currentLanguage == 0 && !(enc is EncounterTrade5BW && EncounterTrade5BW.IsValidMissingLanguage(pk)))
|
||||
return false; // Missing Language value is not obtainable
|
||||
|
||||
return true; // Language is possible
|
||||
@@ -67,18 +69,18 @@ public static bool IsValidLanguageID(int currentLanguage, int maxLanguageID, PKM
|
||||
/// Korean Gen4 games can not trade with other Gen4 languages, but can use Pal Park with any Gen3 game/language.
|
||||
/// Anything with Gen4 origin cannot exist in the other language save file.
|
||||
/// </remarks>
|
||||
public static bool IsValidGen4Korean(int pkmLanguage)
|
||||
public static bool IsValidGen4Korean(LanguageID pkmLanguage)
|
||||
{
|
||||
if (ParseSettings.ActiveTrainer is not SAV4 tr)
|
||||
return true; // ignore
|
||||
return IsValidGen4Korean(pkmLanguage, tr);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IsValidGen4Korean(int)"/>
|
||||
public static bool IsValidGen4Korean(int pkmLanguage, SAV4 tr)
|
||||
/// <inheritdoc cref="IsValidGen4Korean(LanguageID)"/>
|
||||
public static bool IsValidGen4Korean(LanguageID pkmLanguage, SAV4 tr)
|
||||
{
|
||||
bool savKOR = tr.Language == (int)LanguageID.Korean;
|
||||
bool pkmKOR = pkmLanguage == (int)LanguageID.Korean;
|
||||
bool savKOR = (LanguageID)tr.Language == Korean;
|
||||
bool pkmKOR = pkmLanguage == Korean;
|
||||
return savKOR == pkmKOR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ private void CheckScalars(LegalityAnalysis data, PA8 pa)
|
||||
|
||||
private static void CheckLearnset(LegalityAnalysis data, PA8 pa)
|
||||
{
|
||||
var moveCount = GetMoveCount(pa);
|
||||
var moveCount = pa.MoveCount;
|
||||
if (moveCount == 4)
|
||||
return;
|
||||
|
||||
@@ -166,16 +166,6 @@ private static int AddMasteredMissing(PA8 pa, Span<ushort> current, int ctr, Lea
|
||||
return ctr;
|
||||
}
|
||||
|
||||
private static int GetMoveCount(PA8 pa)
|
||||
{
|
||||
var count = 0;
|
||||
if (pa.Move1 != 0) count++;
|
||||
if (pa.Move2 != 0) count++;
|
||||
if (pa.Move3 != 0) count++;
|
||||
if (pa.Move4 != 0) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
private void CheckMastery(LegalityAnalysis data, PA8 pa)
|
||||
{
|
||||
var permit = pa.Permit;
|
||||
|
||||
@@ -37,7 +37,7 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
var reqEXP = enc is EncounterStatic2 { DizzyPunchEgg: true }
|
||||
? 125 // Gen2 Dizzy Punch gifts always have 125 EXP, even if it's more than the Lv5 exp required.
|
||||
: Experience.GetEXP(enc.LevelMin, pk.PersonalInfo.EXPGrowth);
|
||||
: Experience.GetEXP(enc.LevelMin, data.PersonalInfo.EXPGrowth);
|
||||
if (reqEXP != pk.EXP)
|
||||
data.AddLine(GetInvalid(LEggEXP));
|
||||
return;
|
||||
@@ -46,14 +46,14 @@ public override void Verify(LegalityAnalysis data)
|
||||
var lvl = pk.CurrentLevel;
|
||||
if (lvl >= 100)
|
||||
{
|
||||
var expect = Experience.GetEXP(100, pk.PersonalInfo.EXPGrowth);
|
||||
var expect = Experience.GetEXP(100, data.PersonalInfo.EXPGrowth);
|
||||
if (pk.EXP != expect)
|
||||
data.AddLine(GetInvalid(LLevelEXPTooHigh));
|
||||
}
|
||||
|
||||
if (lvl < pk.MetLevel)
|
||||
data.AddLine(GetInvalid(LLevelMetBelow));
|
||||
else if (!enc.IsWithinEncounterRange(pk) && lvl != 100 && pk.EXP == Experience.GetEXP(lvl, pk.PersonalInfo.EXPGrowth))
|
||||
else if (!enc.IsWithinEncounterRange(pk) && lvl != 100 && pk.EXP == Experience.GetEXP(lvl, data.PersonalInfo.EXPGrowth))
|
||||
data.AddLine(Get(LLevelEXPThreshold, Severity.Fishy));
|
||||
else
|
||||
data.AddLine(GetValid(LLevelMetSane));
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using static PKHeX.Core.LegalityCheckStrings;
|
||||
using static PKHeX.Core.StorageSlotType;
|
||||
using static PKHeX.Core.Species;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
@@ -27,12 +29,12 @@ public override void Verify(LegalityAnalysis data)
|
||||
|
||||
public static bool IsSourceValid(PKM pk, StorageSlotType source) => source switch
|
||||
{
|
||||
StorageSlotType.FusedKyurem => pk.Species is (int)Species.Reshiram or (int)Species.Zekrom,
|
||||
StorageSlotType.FusedNecrozmaS => pk.Species is (int)Species.Solgaleo,
|
||||
StorageSlotType.FusedNecrozmaM => pk.Species is (int)Species.Lunala,
|
||||
StorageSlotType.FusedCalyrex => pk.Species is (int)Species.Glastrier or (int)Species.Spectrier,
|
||||
FusedKyurem => pk.Species is (int)Reshiram or (int)Zekrom,
|
||||
FusedNecrozmaS => pk.Species is (int)Solgaleo,
|
||||
FusedNecrozmaM => pk.Species is (int)Lunala,
|
||||
FusedCalyrex => pk.Species is (int)Glastrier or (int)Spectrier,
|
||||
|
||||
StorageSlotType.Ride => pk.Species is (int)Species.Koraidon or (int)Species.Miraidon
|
||||
Ride => pk.Species is (int)Koraidon or (int)Miraidon
|
||||
&& pk is PK9 {FormArgument: EncounterStatic9.RideLegendFormArg },
|
||||
_ => true,
|
||||
};
|
||||
@@ -40,9 +42,9 @@ public override void Verify(LegalityAnalysis data)
|
||||
public static bool IsSourceValidEgg(PKM pk, StorageSlotType source) => source switch
|
||||
{
|
||||
// Eggs should normally only be in Box or Party.
|
||||
StorageSlotType.Box or StorageSlotType.Party => true,
|
||||
StorageSlotType.Resort => true, // Poké Pelago can incubate eggs
|
||||
StorageSlotType.Daycare when pk.Format == 2 => true, // ignore the "current egg" slot
|
||||
Box or Party => true,
|
||||
Resort => true, // Poké Pelago can incubate eggs
|
||||
Daycare when pk.Format == 2 => true, // ignore the "current egg" slot
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ private void VerifyVCNatureEXP(LegalityAnalysis data)
|
||||
}
|
||||
if (met <= 2) // Not enough EXP to have every nature -- check for exclusions!
|
||||
{
|
||||
var pi = pk.PersonalInfo;
|
||||
var pi = data.PersonalInfo;
|
||||
var growth = pi.EXPGrowth;
|
||||
var nature = pk.Nature;
|
||||
bool valid = VerifyVCNature(growth, nature);
|
||||
|
||||
@@ -408,6 +408,7 @@ public void SetIVs(uint iv32)
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetIVs(Span{int})"/>
|
||||
/// <remarks>Returns the combined 30-bit representation commonly used as IV32.</remarks>
|
||||
public uint GetIVs()
|
||||
{
|
||||
uint iv32 = 0;
|
||||
@@ -466,6 +467,12 @@ public ushort[] Moves
|
||||
set => SetMoves(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to add a move to the moveset of the PKM.
|
||||
/// </summary>
|
||||
/// <param name="move">Move ID to add.</param>
|
||||
/// <param name="pushOut">If the current moveset is full, whether to push out the oldest move (index 0) to add the new one.</param>
|
||||
/// <returns></returns>
|
||||
public bool AddMove(ushort move, bool pushOut = true)
|
||||
{
|
||||
if (move == 0 || move >= MaxMoveID || HasMove(move))
|
||||
@@ -483,6 +490,9 @@ public bool AddMove(ushort move, bool pushOut = true)
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Count of non-zero moves in the moveset.
|
||||
/// </summary>
|
||||
public int MoveCount => Convert.ToInt32(Move1 != 0) + Convert.ToInt32(Move2 != 0) + Convert.ToInt32(Move3 != 0) + Convert.ToInt32(Move4 != 0);
|
||||
|
||||
public void GetMoves(Span<ushort> value)
|
||||
|
||||
@@ -36,24 +36,22 @@ public uint EXP
|
||||
public override string ToString() => $"{(Species) Species} 0x{PID:X8} {Purification}";
|
||||
}
|
||||
|
||||
public sealed class ShadowInfoEntry3J : ShadowInfoEntryXD
|
||||
public sealed class ShadowInfoEntry3J(Memory<byte> data) : ShadowInfoEntryXD(data)
|
||||
{
|
||||
internal const int SIZE_ENTRY = 60; // -12 from U
|
||||
|
||||
public ShadowInfoEntry3J() : base(new byte[SIZE_ENTRY]) { }
|
||||
public ShadowInfoEntry3J(byte[] data) : base(data) { }
|
||||
public ShadowInfoEntry3J() : this(new byte[SIZE_ENTRY]) { }
|
||||
|
||||
public override byte Index { get => Data[0x35]; set => Data[0x35] = value; }
|
||||
|
||||
public override string ToString() => $"{(Species)Species} 0x{PID:X8} {Purification}";
|
||||
}
|
||||
|
||||
public sealed class ShadowInfoEntry3U : ShadowInfoEntryXD
|
||||
public sealed class ShadowInfoEntry3U(Memory<byte> data) : ShadowInfoEntryXD(data)
|
||||
{
|
||||
internal const int SIZE_ENTRY = 72; // -12 from U
|
||||
|
||||
public ShadowInfoEntry3U() : base(new byte[SIZE_ENTRY]) { }
|
||||
public ShadowInfoEntry3U(byte[] data) : base(data) { }
|
||||
public ShadowInfoEntry3U() : this(new byte[SIZE_ENTRY]) { }
|
||||
|
||||
public override byte Index { get => Data[0x3F]; set => Data[0x3F] = value; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user