mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-03-21 17:48:28 -05:00
Initial gen3 virtual console checks
Disables branching when virtual console is the current save file
This commit is contained in:
parent
5c4d27f7e4
commit
c037829b29
|
|
@ -71,7 +71,7 @@ private static EncounterArea3[] GetRegular([ConstantExpected] string resource, [
|
|||
new(147, 18, FR) { FixedBall = Ball.Poke, Location = 94 }, // Dratini
|
||||
new(137, 26, FR) { FixedBall = Ball.Poke, Location = 94 }, // Porygon
|
||||
|
||||
new(386, 30, FR ) { Location = 187, FatefulEncounter = true, Form = 1 }, // Deoxys @ Birth Island
|
||||
new(386, 30, FR) { Location = 187, FatefulEncounter = true, Form = 1 }, // Deoxys @ Birth Island
|
||||
];
|
||||
|
||||
public static readonly EncounterStatic3[] StaticLG =
|
||||
|
|
@ -88,7 +88,8 @@ private static EncounterArea3[] GetRegular([ConstantExpected] string resource, [
|
|||
new(127, 18, LG) { FixedBall = Ball.Poke, Location = 94 }, // Pinsir
|
||||
new(147, 24, LG) { FixedBall = Ball.Poke, Location = 94 }, // Dratini
|
||||
new(137, 18, LG) { FixedBall = Ball.Poke, Location = 94 }, // Porygon
|
||||
new(386, 30, LG) { Location = 187, FatefulEncounter = true, Form = 2 }, // Deoxys @ Birth Island
|
||||
|
||||
new(386, 30, LG) { Location = 187, FatefulEncounter = true, Form = 2 }, // Deoxys @ Birth Island
|
||||
];
|
||||
|
||||
private static ReadOnlySpan<byte> TradeContest_Cool => [ 30, 05, 05, 05, 05, 10 ];
|
||||
|
|
|
|||
|
|
@ -22,8 +22,14 @@ public static class EncounterVerifier
|
|||
private static CheckResult VerifyEncounter(PKM pk, IEncounterTemplate enc) => enc switch
|
||||
{
|
||||
EncounterShadow3Colo { IsEReader: true } when pk.Language != (int)LanguageID.Japanese => GetInvalid(G3EReader),
|
||||
EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese => GetInvalid(EncUnreleasedEMewJP),
|
||||
EncounterStatic3 { Species: (int)Species.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese => GetInvalid(EncUnreleased),
|
||||
|
||||
// Mew @ Faraway Island (Emerald)
|
||||
EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese
|
||||
=> GetInvalid(EncUnreleasedEMewJP),
|
||||
// Deoxys @ Birth Island (FireRed/LeafGreen) - Never distributed in Japan during GBA Cart era. NX virtual console added for all.
|
||||
EncounterStatic3 { Species: (int)Species.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese && !ParseSettings.AllowGen3EventTicketsAll(pk)
|
||||
=> GetInvalid(EncUnreleased),
|
||||
|
||||
EncounterStatic4 { Species: (int)Species.Shaymin } when pk.Language == (int)LanguageID.Korean => GetInvalid(EncUnreleased),
|
||||
EncounterStatic4 { IsRoaming: true } when pk is G4PKM { MetLocation: 193, GroundTile: GroundTileType.Water } => GetInvalid(G4InvalidTileR45Surf),
|
||||
MysteryGift g => VerifyEncounterEvent(pk, g),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ public sealed class EvolutionGroup3 : IEvolutionGroup
|
|||
private static PersonalTable3 Personal => PersonalTable.E;
|
||||
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
|
||||
|
||||
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup4.Instance : null;
|
||||
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup4.Instance : null; // TODO HOME FR/LG
|
||||
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => null;
|
||||
public void DiscardForOrigin(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc) => EvolutionUtil.Discard(result, Personal);
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,12 @@ private static void CheckEncounterMoves(Span<MoveResult> result, ReadOnlySpan<us
|
|||
|
||||
private static void Check(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, EvoCriteria evo, int stage, MoveSourceType types)
|
||||
{
|
||||
if (!ParseSettings.AllowGBACrossTransferRSE(pk))
|
||||
{
|
||||
CheckNX(result, current, pk, evo, stage, types);
|
||||
return;
|
||||
}
|
||||
|
||||
var rs = LearnSource3RS.Instance;
|
||||
var species = evo.Species;
|
||||
if (!rs.TryGetPersonal(species, evo.Form, out var rp))
|
||||
|
|
@ -137,6 +143,34 @@ private static void Check(Span<MoveResult> result, ReadOnlySpan<ushort> current,
|
|||
}
|
||||
}
|
||||
|
||||
private static void CheckNX(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, EvoCriteria evo, int stage, MoveSourceType types)
|
||||
{
|
||||
var species = evo.Species;
|
||||
var fr = LearnSource3FR.Instance;
|
||||
if (!fr.TryGetPersonal(species, evo.Form, out var fp))
|
||||
return; // should never happen.
|
||||
var lg = LearnSource3LG.Instance;
|
||||
var lp = lg[species];
|
||||
|
||||
for (int i = result.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (result[i].Valid)
|
||||
continue;
|
||||
|
||||
// Level Up moves are different for each game, but TM/HM is shared (use Emerald).
|
||||
var move = current[i];
|
||||
var chk = fr.GetCanLearn(pk, fp, evo, move, types & (MoveSourceType.LevelUp | MoveSourceType.AllTutors));
|
||||
if (chk != default)
|
||||
{
|
||||
result[i] = new(chk, (byte)stage, Context);
|
||||
continue;
|
||||
}
|
||||
chk = lg.GetCanLearn(pk, lp, evo, move, types & MoveSourceType.LevelUp); // Tutors same as FR
|
||||
if (chk != default)
|
||||
result[i] = new(chk, (byte)stage, Context);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetAllMoves(Span<bool> result, PKM pk, EvolutionHistory history, IEncounterTemplate enc, MoveSourceType types = MoveSourceType.All, LearnOption option = LearnOption.Current)
|
||||
{
|
||||
if (types.HasFlag(MoveSourceType.Encounter) && enc.Context == Context)
|
||||
|
|
@ -158,6 +192,12 @@ public void GetAllMoves(Span<bool> result, PKM pk, EvolutionHistory history, IEn
|
|||
|
||||
private static void GetAllMoves(Span<bool> result, PKM pk, EvoCriteria evo, MoveSourceType types)
|
||||
{
|
||||
if (!ParseSettings.AllowGBACrossTransferRSE(pk)) // NX
|
||||
{
|
||||
LearnSource3FR.Instance.GetAllMoves(result, pk, evo, types);
|
||||
LearnSource3LG.Instance.GetAllMoves(result, pk, evo, types & (MoveSourceType.LevelUp));
|
||||
return;
|
||||
}
|
||||
LearnSource3E.Instance.GetAllMoves(result, pk, evo, types);
|
||||
LearnSource3RS.Instance.GetAllMoves(result, pk, evo, types & (MoveSourceType.LevelUp | MoveSourceType.AllTutors));
|
||||
LearnSource3FR.Instance.GetAllMoves(result, pk, evo, types & (MoveSourceType.LevelUp | MoveSourceType.AllTutors));
|
||||
|
|
|
|||
|
|
@ -31,7 +31,17 @@ public static class ParseSettings
|
|||
/// Setting to specify if an analysis should permit data sourced from the physical cartridge era of Game Boy games.
|
||||
/// </summary>
|
||||
/// <remarks>If false, indicates to use Virtual Console rules (which are transferable to Gen7+)</remarks>
|
||||
public static bool AllowGBCartEra { private get; set; }
|
||||
public static bool AllowEraCartGB { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting to specify if an analysis should permit data sourced from the physical cartridge era of Game Boy Advance games.
|
||||
/// </summary>
|
||||
public static bool AllowEraCartGBA { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting to specify if an analysis should permit data sourced from the Nintendo Switch Virtual Console era of Game Boy Advance games.
|
||||
/// </summary>
|
||||
public static bool AllowEraSwitchGBA { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting to specify if an analysis should permit trading a Generation 1 origin file to Generation 2, then back. Useful for checking RBY Metagame rules.
|
||||
|
|
@ -55,27 +65,35 @@ public static class ParseSettings
|
|||
/// <returns>True if Crystal data is allowed</returns>
|
||||
public static bool AllowGen2MoveReminder(PKM pk) => !pk.Korean && AllowGBStadium2;
|
||||
|
||||
public static bool AllowGen2OddEgg(PKM pk) => !pk.Japanese || AllowGBCartEra;
|
||||
public static bool AllowGen2OddEgg(PKM pk) => !pk.Japanese || AllowEraCartGB;
|
||||
|
||||
public static bool AllowGBVirtualConsole3DS => !AllowGBCartEra;
|
||||
public static bool AllowGBEraEvents => AllowGBCartEra;
|
||||
public static bool AllowGBStadium2 => AllowGBCartEra;
|
||||
public static bool AllowGBVirtualConsole3DS => !AllowEraCartGB;
|
||||
public static bool AllowGBEraEvents => AllowEraCartGB;
|
||||
public static bool AllowGBStadium2 => AllowEraCartGB;
|
||||
|
||||
// This logic will likely need to change (format check): TODO HOME FR/LG
|
||||
public static bool AllowGBACrossTransferXD(PKM pk) => AllowEraCartGBA;
|
||||
public static bool AllowGBACrossTransferRSE(PKM pk) => AllowEraCartGBA;
|
||||
public static bool AllowGen3EventTicketsAll(PKM pk) => AllowEraSwitchGBA;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes certain settings
|
||||
/// </summary>
|
||||
/// <param name="sav">Newly loaded save file</param>
|
||||
/// <returns>Save file is Physical GB cartridge save file (not Virtual Console)</returns>
|
||||
public static bool InitFromSaveFileData(SaveFile sav)
|
||||
public static void InitFromSaveFileData(SaveFile sav)
|
||||
{
|
||||
ActiveTrainer = sav;
|
||||
return AllowGBCartEra = sav switch
|
||||
AllowEraCartGB = sav switch
|
||||
{
|
||||
SAV1 { IsVirtualConsole: true } => false,
|
||||
SAV2 { IsVirtualConsole: true } => false,
|
||||
{ Generation: 1 or 2 } => true,
|
||||
_ => false,
|
||||
};
|
||||
var isVirtual3 = sav is SAV3 { IsVirtualConsole: true };
|
||||
AllowEraSwitchGBA = isVirtual3;
|
||||
AllowEraCartGBA = !isVirtual3; // sav.Generation >= 8; TODO HOME FR/LG
|
||||
}
|
||||
|
||||
internal static bool IgnoreTransferIfNoTracker => Settings.HOMETransfer.HOMETransferTrackerNotPresent == Severity.Invalid;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ public void SetMaxContestStats(IEncounterTemplate enc, EvolutionHistory h)
|
|||
|
||||
public static ContestStatGranting GetContestStatRestriction(PKM pk, byte origin, EvolutionHistory h) => origin switch
|
||||
{
|
||||
3 when pk.Format == 3 && !ParseSettings.AllowGBACrossTransferRSE(pk) => None,
|
||||
3 => pk.Format < 6 ? CorrelateSheen : Mixed,
|
||||
4 => pk.Format < 6 ? CorrelateSheen : Mixed,
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ internal void Verify(LegalityAnalysis data, PKM pk)
|
|||
if (pk.Format == 1) // not stored in Gen1 format
|
||||
return;
|
||||
|
||||
if (pk.Format == 3 && !ParseSettings.AllowGBACrossTransferRSE(pk))
|
||||
{
|
||||
VerifyNone(data, pk);
|
||||
return;
|
||||
}
|
||||
|
||||
var strain = pk.PokerusStrain;
|
||||
var days = pk.PokerusDays;
|
||||
var enc = data.Info.EncounterMatch;
|
||||
|
|
@ -22,4 +28,14 @@ internal void Verify(LegalityAnalysis data, PKM pk)
|
|||
if (!Pokerus.IsDurationValid(strain, days, out var max))
|
||||
data.AddLine(GetInvalid(PokerusDaysLEQ_0, (ushort)max));
|
||||
}
|
||||
|
||||
private void VerifyNone(LegalityAnalysis data, PKM pk)
|
||||
{
|
||||
var strain = pk.PokerusStrain;
|
||||
var days = pk.PokerusDays;
|
||||
if (strain != 0)
|
||||
data.AddLine(GetInvalid(PokerusStrainUnobtainable_0, (ushort)strain));
|
||||
if (days != 0)
|
||||
data.AddLine(GetInvalid(PokerusDaysLEQ_0, 0));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
PKHeX.Core/Legality/Verifiers/Misc/MiscVerifierG3.cs
Normal file
25
PKHeX.Core/Legality/Verifiers/Misc/MiscVerifierG3.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.CheckIdentifier;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
internal sealed class MiscVerifierG3 : Verifier
|
||||
{
|
||||
protected override CheckIdentifier Identifier => Misc;
|
||||
|
||||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
if (data.Entity is G3PKM pk)
|
||||
Verify(data, pk);
|
||||
}
|
||||
|
||||
internal void Verify(LegalityAnalysis data, G3PKM pk)
|
||||
{
|
||||
if (ParseSettings.AllowGBACrossTransferRSE(pk))
|
||||
return;
|
||||
|
||||
// Only FR/LG are released. Only can originate from FR/LG.
|
||||
if (pk.Version is not (GameVersion.FR or GameVersion.LG))
|
||||
data.AddLine(GetInvalid(EncUnreleased));
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ public sealed class MiscVerifier : Verifier
|
|||
private static readonly MiscG1Verifier Gen1 = new();
|
||||
private static readonly MiscEvolutionVerifier Evolution = new();
|
||||
private static readonly MiscVerifierSK2 Stadium2 = new();
|
||||
private static readonly MiscVerifierG3 Gen3 = new();
|
||||
private static readonly MiscVerifierG4 Gen4 = new();
|
||||
private static readonly MiscVerifierPK6 Gen6 = new();
|
||||
private static readonly MiscVerifierPK5 Gen5 = new();
|
||||
|
|
@ -41,6 +42,7 @@ public override void Verify(LegalityAnalysis data)
|
|||
// Verify gimmick data
|
||||
switch (pk)
|
||||
{
|
||||
case G3PKM pk3: Gen3.Verify(data, pk3); break;
|
||||
case G4PKM pk4: Gen4.Verify(data, pk4); break;
|
||||
case PK5 pk5: Gen5.Verify(data, pk5); break;
|
||||
case PK6 pk6: Gen6.Verify(data, pk6); break;
|
||||
|
|
|
|||
|
|
@ -324,6 +324,21 @@ public static bool GetValidRibbonStateNational(PKM pk, IEncounterTemplate enc)
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the input can receive the <see cref="IRibbonSetEvent3.RibbonEarth"/> ribbon.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If returns true, can have the ribbon. If returns false, must not have the ribbon.
|
||||
/// </remarks>
|
||||
public static bool IsEarthRibbonAllowed(PKM pk, IEncounterTemplate enc)
|
||||
{
|
||||
if (enc.Generation != 3)
|
||||
return false;
|
||||
if (!ParseSettings.AllowGBACrossTransferXD(pk))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max count values the input can receive for the <see cref="IRibbonSetMemory6.RibbonCountMemoryContest"/> and <see cref="IRibbonSetMemory6.RibbonCountMemoryBattle"/> ribbon counts.
|
||||
/// </summary>
|
||||
|
|
@ -360,9 +375,11 @@ public static (byte Contest, byte Battle) GetMaxMemoryCounts(EvolutionHistory ev
|
|||
/// <summary>
|
||||
/// Checks if the input evolution history could have participated in Generation 3 contests.
|
||||
/// </summary>
|
||||
public static bool IsAllowedContest3(EvolutionHistory evos)
|
||||
public static bool IsAllowedContest3(EvolutionHistory evos, PKM pk)
|
||||
{
|
||||
// Any species can enter contests in Gen3.
|
||||
if (!ParseSettings.AllowGBACrossTransferRSE(pk))
|
||||
return false;
|
||||
return evos.HasVisitedGen3;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public void Parse(in RibbonVerifierArguments args, ref RibbonResultList list)
|
|||
if (!r.RibbonEarth)
|
||||
list.Add(Earth, true);
|
||||
}
|
||||
else if (r.RibbonEarth && enc.Generation != 3)
|
||||
else if (r.RibbonEarth && !RibbonRules.IsEarthRibbonAllowed(args.Entity, enc))
|
||||
{
|
||||
list.Add(Earth);
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ public void Parse(in RibbonVerifierArguments args, ref RibbonResultList list)
|
|||
{
|
||||
// The Earth Ribbon is a ribbon exclusive to Pokémon Colosseum and Pokémon XD: Gale of Darkness
|
||||
// Awarded to all Pokémon on the player's team when they complete the Mt. Battle challenge without switching the team at any point.
|
||||
if (r.RibbonEarth && enc.Generation != 3)
|
||||
if (r.RibbonEarth && !RibbonRules.IsEarthRibbonAllowed(args.Entity, enc))
|
||||
list.Add(Earth);
|
||||
|
||||
var nationalRequired = RibbonRules.GetValidRibbonStateNational(args.Entity, enc);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public static void Parse(this IRibbonSetOnly3 r, in RibbonVerifierArguments args
|
|||
if (r.RibbonWorld)
|
||||
list.Add(RibbonIndex.World);
|
||||
|
||||
if (!RibbonRules.IsAllowedContest3(args.History))
|
||||
if (!RibbonRules.IsAllowedContest3(args.History, args.Entity))
|
||||
FlagContestAny(r, ref list);
|
||||
else
|
||||
FlagContest(r, ref list);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public void Parse(in RibbonVerifierArguments args, ref RibbonResultList list)
|
|||
if (!RibbonRules.IsAllowedBattleFrontier4(evos))
|
||||
FlagAnyAbility(r, ref list);
|
||||
|
||||
if (RibbonRules.IsAllowedContest3(evos))
|
||||
if (RibbonRules.IsAllowedContest3(evos, args.Entity))
|
||||
AddMissingContest3(r, ref list);
|
||||
else
|
||||
FlagAnyContest3(r, ref list);
|
||||
|
|
|
|||
|
|
@ -14,10 +14,13 @@ public abstract class SAV3 : SaveFile, ILangDeviantSave, IEventFlag37, IBoxDetai
|
|||
public sealed override string Extension => ".sav";
|
||||
|
||||
public int SaveRevision => Japanese ? 0 : 1;
|
||||
public string SaveRevisionString => Japanese ? "J" : "U";
|
||||
public string SaveRevisionString => (Japanese ? "J" : "U") + (IsVirtualConsole ? "VC" : "GBA");
|
||||
public bool Japanese { get; }
|
||||
public bool Korean => false;
|
||||
|
||||
public bool IsVirtualConsole => State.Exportable && Metadata.FileName is { } s && s.Contains(".sav")
|
||||
&& (s.StartsWith("FireRed_", StringComparison.Ordinal) || s.StartsWith("LeafGreen_")); // default to Mainline-Era for non-exportable
|
||||
|
||||
// Similar to future games, the Generation 3 Mainline save files are comprised of separate objects:
|
||||
// Object 1 - Small, containing misc configuration data & the Pokédex.
|
||||
// Object 2 - Large, containing everything else that isn't PC Storage system data.
|
||||
|
|
|
|||
|
|
@ -130,7 +130,8 @@ private static void VerifyAll(string folder, string subFolder, bool isValid, boo
|
|||
prefer.IsValid.Should().BeTrue("filename is expected to have a valid extension");
|
||||
|
||||
var dn = fi.DirectoryName ?? string.Empty;
|
||||
ParseSettings.AllowGBCartEra = dn.Contains("GBCartEra");
|
||||
ParseSettings.AllowEraCartGB = dn.Contains("GBCartEra");
|
||||
ParseSettings.AllowEraCartGBA = !dn.Contains("GBAVCEra");
|
||||
ParseSettings.Settings.Tradeback.AllowGen1Tradeback = dn.Contains("1 Tradeback");
|
||||
var pk = EntityFormat.GetFromBytes(data, prefer);
|
||||
pk.Should().NotBeNull($"the PKM '{new FileInfo(file).Name}' should have been loaded");
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user