mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Misc tweaks
This commit is contained in:
parent
62df64e6f5
commit
59dc7fb694
21 changed files with 277 additions and 268 deletions
|
@ -17,7 +17,6 @@ public static class Legal
|
|||
internal const int MaxItemID_2 = 255;
|
||||
internal const int MaxAbilityID_2 = 0;
|
||||
|
||||
internal const int MaxSpeciesIndex_3 = 412;
|
||||
internal const int MaxSpeciesID_3 = 386;
|
||||
internal const int MaxMoveID_3 = 354;
|
||||
internal const int MaxItemID_3 = 374;
|
||||
|
|
|
@ -16,27 +16,19 @@ public static class FormInfo
|
|||
/// <param name="form">Entity form</param>
|
||||
/// <param name="format">Current generation format</param>
|
||||
/// <returns>True if it can only exist in a battle, false if it can exist outside of battle.</returns>
|
||||
public static bool IsBattleOnlyForm(ushort species, byte form, int format)
|
||||
public static bool IsBattleOnlyForm(ushort species, byte form, int format) => BattleOnly.Contains(species) && species switch
|
||||
{
|
||||
if (!BattleOnly.Contains(species))
|
||||
return false;
|
||||
|
||||
// Only continue checking if the species is in the list of Battle Only forms.
|
||||
// Some species have battle only forms as well as out-of-battle forms (other than base form).
|
||||
switch (species)
|
||||
{
|
||||
case (int)Slowbro when form == 2 && format >= 8: // this one is OK, Galarian Slowbro (not a Mega)
|
||||
case (int)Darmanitan when form == 2 && format >= 8: // this one is OK, Galarian non-Zen
|
||||
case (int)Zygarde when form < 4: // Zygarde Complete
|
||||
case (int)Mimikyu when form == 2: // Totem disguise Mimikyu
|
||||
case (int)Necrozma when form < 3: // Only mark Ultra Necrozma as Battle Only
|
||||
return false;
|
||||
case (int)Minior: return form < 7; // Minior Shields-Down
|
||||
case (int)Ogerpon: return form >= 4; // Embody Aspect
|
||||
|
||||
default:
|
||||
return form != 0;
|
||||
}
|
||||
}
|
||||
(ushort)Slowbro => form == 1, // Mega
|
||||
(ushort)Darmanitan => (form & 1) == 1, // Zen
|
||||
(ushort)Zygarde => form == 4, // Zygarde Complete
|
||||
(ushort)Minior => form < 7, // Minior Shields-Down
|
||||
(ushort)Mimikyu => (form & 1) == 1, // Busted
|
||||
(ushort)Necrozma => form == 3, // Ultra Necrozma
|
||||
(ushort)Ogerpon => form >= 4, // Embody Aspect
|
||||
_ => form != 0,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Reverts the Battle Form to the form it would have outside of Battle.
|
||||
|
@ -48,10 +40,11 @@ public static class FormInfo
|
|||
/// <returns>Suggested alt form value.</returns>
|
||||
public static byte GetOutOfBattleForm(ushort species, byte form, int format) => species switch
|
||||
{
|
||||
(int)Darmanitan => (byte)(form & 2),
|
||||
(int)Zygarde when format > 6 => 3,
|
||||
(int)Minior => (byte)(form + 7),
|
||||
(int)Ogerpon => (byte)(form & 3),
|
||||
(ushort)Darmanitan => (byte)(form & 2),
|
||||
(ushort)Zygarde when format > 6 => 3,
|
||||
(ushort)Minior => (byte)(form + 7),
|
||||
(ushort)Mimikyu => (byte)(form & 2),
|
||||
(ushort)Ogerpon => (byte)(form & 3),
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
|
@ -65,9 +58,9 @@ public static class FormInfo
|
|||
/// <returns>True if it trading should be disallowed.</returns>
|
||||
public static bool IsUntradable(ushort species, byte form, uint formArg, int format) => species switch
|
||||
{
|
||||
(int)Koraidon or (int)Miraidon when formArg == 1 => true, // Ride-able Box Legend
|
||||
(int)Pikachu when form == 8 && format == 7 => true, // Let's Go Pikachu Starter
|
||||
(int)Eevee when form == 1 && format == 7 => true, // Let's Go Eevee Starter
|
||||
(ushort)Koraidon or (int)Miraidon => formArg == 1, // Ride-able Box Legend
|
||||
(ushort)Pikachu => format == 7 && form == 8, // Let's Go Pikachu Starter
|
||||
(ushort)Eevee => format == 7 && form == 1, // Let's Go Eevee Starter
|
||||
_ => IsFusedForm(species, form, format),
|
||||
};
|
||||
|
||||
|
@ -80,9 +73,9 @@ public static class FormInfo
|
|||
/// <returns>True if it is a fused species-form, false if it is not fused.</returns>
|
||||
public static bool IsFusedForm(ushort species, byte form, int format) => species switch
|
||||
{
|
||||
(int)Kyurem when form != 0 && format >= 5 => true,
|
||||
(int)Necrozma when form != 0 && format >= 7 => true,
|
||||
(int)Calyrex when form != 0 && format >= 8 => true,
|
||||
(ushort)Kyurem => form != 0 && format >= 5,
|
||||
(ushort)Necrozma => form != 0 && format >= 7,
|
||||
(ushort)Calyrex => form != 0 && format >= 8,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
@ -224,18 +217,16 @@ public static class FormInfo
|
|||
(int)Necrozma, // Ultra Necrozma
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Species that have a primal form that cannot exist outside of battle.
|
||||
/// </summary>
|
||||
private static readonly HashSet<ushort> BattlePrimals = new() { (int)Kyogre, (int)Groudon };
|
||||
|
||||
private static readonly HashSet<ushort> BattleOnly = GetBattleFormSet();
|
||||
|
||||
private static HashSet<ushort> GetBattleFormSet()
|
||||
{
|
||||
var hs = new HashSet<ushort>(BattleForms);
|
||||
hs.UnionWith(BattleMegas);
|
||||
hs.UnionWith(BattlePrimals);
|
||||
|
||||
// Primals
|
||||
hs.Add((ushort)Kyogre);
|
||||
hs.Add((ushort)Groudon);
|
||||
return hs;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using static PKHeX.Core.Species;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
@ -8,147 +8,73 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
internal static class AbilityBreedLegality
|
||||
{
|
||||
private static ReadOnlySpan<byte> BanHidden5 => new byte[]
|
||||
{
|
||||
0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||
0x10, 0x10, 0x20, 0x00, 0x01, 0x11, 0x02, 0x00, 0x49, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x90, 0x04,
|
||||
0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x86, 0x80,
|
||||
0x49, 0x00, 0x40, 0x00, 0x48, 0x02, 0x00, 0x00, 0x10, 0x00, 0x12,
|
||||
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x82, 0x24, 0x80, 0x0A, 0x00,
|
||||
0x00, 0x0C, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00, 0xA0, 0x84, 0x80,
|
||||
0x40, 0x08, 0x12,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Species that cannot be bred with a Hidden Ability originating in <see cref="GameVersion.Gen5"/>
|
||||
/// </summary>
|
||||
internal static readonly HashSet<ushort> BanHidden5 = new()
|
||||
public static bool IsHiddenPossible5(ushort species)
|
||||
{
|
||||
// Only males distributed; unable to pass to offspring
|
||||
(int)Bulbasaur, (int)Charmander, (int)Squirtle,
|
||||
(int)Tauros,
|
||||
(int)Chikorita, (int)Cyndaquil, (int)Totodile,
|
||||
(int)Tyrogue,
|
||||
(int)Treecko, (int)Torchic, (int)Mudkip,
|
||||
(int)Turtwig, (int)Chimchar, (int)Piplup,
|
||||
(int)Pansage, (int)Pansear, (int)Panpour,
|
||||
(int)Gothita,
|
||||
|
||||
// Genderless; unable to pass to offspring
|
||||
(int)Magnemite,
|
||||
(int)Voltorb,
|
||||
(int)Staryu,
|
||||
(int)Ditto,
|
||||
(int)Porygon,
|
||||
(int)Beldum,
|
||||
(int)Bronzor,
|
||||
(int)Golett,
|
||||
|
||||
// Not available at all
|
||||
(int)Gastly,
|
||||
(int)Koffing,
|
||||
(int)Misdreavus,
|
||||
(int)Unown,
|
||||
(int)Slakoth,
|
||||
(int)Plusle,
|
||||
(int)Plusle,
|
||||
(int)Lunatone,
|
||||
(int)Solrock,
|
||||
(int)Baltoy,
|
||||
(int)Castform,
|
||||
(int)Kecleon,
|
||||
(int)Duskull,
|
||||
(int)Chimecho,
|
||||
(int)Cherubi,
|
||||
(int)Chingling,
|
||||
(int)Rotom,
|
||||
(int)Phione,
|
||||
(int)Snivy, (int)Tepig, (int)Oshawott,
|
||||
(int)Throh, (int)Sawk,
|
||||
(int)Yamask,
|
||||
(int)Archen,
|
||||
(int)Zorua,
|
||||
(int)Ferroseed,
|
||||
(int)Klink,
|
||||
(int)Tynamo,
|
||||
(int)Litwick,
|
||||
(int)Cryogonal,
|
||||
(int)Rufflet,
|
||||
(int)Deino,
|
||||
(int)Larvesta,
|
||||
};
|
||||
var index = species >> 3;
|
||||
var table = BanHidden5;
|
||||
if (index >= table.Length)
|
||||
return true;
|
||||
return (BanHidden5[index] & (1 << (species & 7))) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Species that cannot be bred with a Hidden Ability originating in <see cref="GameVersion.Gen6"/>
|
||||
/// </summary>
|
||||
internal static readonly HashSet<ushort> BanHidden6 = new()
|
||||
public static bool IsHiddenPossible6(ushort species, byte form) => species switch
|
||||
{
|
||||
// Not available at Friend Safari or Horde Encounter
|
||||
(int)Flabébé + (2 << 11), // Orange
|
||||
(int)Flabébé + (4 << 11), // White
|
||||
|
||||
// Super Size can be obtained as a Pumpkaboo from event distributions
|
||||
(int)Pumpkaboo + (1 << 11), // Small
|
||||
(int)Pumpkaboo + (2 << 11), // Large
|
||||
|
||||
// Same abilities (1/2/H), not available as H
|
||||
(int)Honedge,
|
||||
(int)Carnivine,
|
||||
(int)Cryogonal,
|
||||
(int)Archen,
|
||||
(int)Rotom,
|
||||
(int)Rotom + (1 << 11),
|
||||
(int)Rotom + (2 << 11),
|
||||
(int)Rotom + (3 << 11),
|
||||
(int)Rotom + (4 << 11),
|
||||
(int)Rotom + (5 << 11),
|
||||
(int)Castform => false,
|
||||
(int)Carnivine => false,
|
||||
(int)Rotom => false,
|
||||
(int)Phione => false,
|
||||
(int)Archen => false,
|
||||
(int)Cryogonal => false,
|
||||
|
||||
(int)Castform,
|
||||
(int)Furfrou,
|
||||
(int)Furfrou + (1 << 11),
|
||||
(int)Furfrou + (2 << 11),
|
||||
(int)Furfrou + (3 << 11),
|
||||
(int)Furfrou + (4 << 11),
|
||||
(int)Furfrou + (5 << 11),
|
||||
(int)Furfrou + (6 << 11),
|
||||
(int)Furfrou + (7 << 11),
|
||||
(int)Furfrou + (8 << 11),
|
||||
(int)Furfrou + (9 << 11),
|
||||
(int)Flabébé => form is not (2 or 4), // Orange or White - not available in Friend Safari or Horde
|
||||
(int)Honedge => false,
|
||||
(int)Furfrou => false,
|
||||
(int)Pumpkaboo => form is not (1 or 2), // Previous-Gen: Size & Ability inherit from mother
|
||||
|
||||
_ => true,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Species that cannot be bred with a Hidden Ability originating in <see cref="GameVersion.Gen7"/>
|
||||
/// </summary>
|
||||
internal static readonly HashSet<ushort> BanHidden7 = new()
|
||||
public static bool IsHiddenPossible7(ushort species, byte form) => species switch
|
||||
{
|
||||
// SOS slots have 0 call rate
|
||||
(int)Wimpod,
|
||||
(int)Golisopod,
|
||||
(int)Komala,
|
||||
|
||||
// No Encounter
|
||||
(int)Minior + (07 << 11),
|
||||
(int)Minior + (08 << 11),
|
||||
(int)Minior + (09 << 11),
|
||||
(int)Minior + (10 << 11),
|
||||
(int)Minior + (11 << 11),
|
||||
(int)Minior + (12 << 11),
|
||||
(int)Minior + (13 << 11),
|
||||
|
||||
// Previous-Gen
|
||||
(int)Pumpkaboo + (1 << 11), // Small
|
||||
(int)Pumpkaboo + (2 << 11), // Large
|
||||
|
||||
// Same abilities (1/2/H), not available as H
|
||||
(int)Honedge,
|
||||
(int)Doublade,
|
||||
(int)Aegislash,
|
||||
(int)Carnivine,
|
||||
(int)Cryogonal,
|
||||
(int)Archen,
|
||||
(int)Archeops,
|
||||
(int)Rotom,
|
||||
(int)Rotom + (1 << 11),
|
||||
(int)Rotom + (2 << 11),
|
||||
(int)Rotom + (3 << 11),
|
||||
(int)Rotom + (4 << 11),
|
||||
(int)Rotom + (5 << 11),
|
||||
(int)Carnivine => false,
|
||||
(int)Rotom => false,
|
||||
(int)Phione => false,
|
||||
(int)Archen => false,
|
||||
(int)Cryogonal => false,
|
||||
(int)Honedge => false,
|
||||
(int)Pumpkaboo => form is not (1 or 2), // Previous-Gen: Size & Ability inherit from mother
|
||||
|
||||
(int)Minior => false, // No SOS Encounter
|
||||
(int)Wimpod => false, // SOS slots have 0 call rate
|
||||
(int)Komala => false, // SOS slots have 0 call rate
|
||||
_ => true,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Species that cannot be bred with a Hidden Ability originating in <see cref="GameVersion.BDSP"/>
|
||||
/// </summary>
|
||||
internal static readonly HashSet<ushort> BanHidden8b = new()
|
||||
{
|
||||
(int)Phione,
|
||||
};
|
||||
public static bool IsHiddenPossibleHOME(ushort eggSpecies) => eggSpecies is not (int)Phione; // Everything else can!
|
||||
}
|
||||
|
|
|
@ -341,7 +341,7 @@ public sealed class AbilityVerifier : Verifier
|
|||
// Eggs and Encounter Slots are not yet checked for Hidden Ability potential.
|
||||
return enc switch
|
||||
{
|
||||
EncounterEgg e when pk.AbilityNumber == 4 && AbilityBreedLegality.BanHidden5.Contains(e.Species) => GetInvalid(LAbilityHiddenUnavailable),
|
||||
EncounterEgg e when pk.AbilityNumber == 4 && !AbilityBreedLegality.IsHiddenPossible5(e.Species) => GetInvalid(LAbilityHiddenUnavailable),
|
||||
_ => CheckMatch(data.Entity, abilities, 5, pk.Format == 5 ? AbilityState.MustMatch : AbilityState.CanMismatch, enc),
|
||||
};
|
||||
}
|
||||
|
@ -352,10 +352,9 @@ public sealed class AbilityVerifier : Verifier
|
|||
if (pk.AbilityNumber != 4)
|
||||
return VALID;
|
||||
|
||||
// Eggs and Encounter Slots are not yet checked for Hidden Ability potential.
|
||||
return enc switch
|
||||
{
|
||||
EncounterEgg egg when AbilityBreedLegality.BanHidden6.Contains((ushort)(egg.Species | (egg.Form << 11))) => GetInvalid(LAbilityHiddenUnavailable),
|
||||
EncounterEgg egg when !AbilityBreedLegality.IsHiddenPossible6(egg.Species, egg.Form) => GetInvalid(LAbilityHiddenUnavailable),
|
||||
_ => VALID,
|
||||
};
|
||||
}
|
||||
|
@ -368,7 +367,7 @@ public sealed class AbilityVerifier : Verifier
|
|||
|
||||
return enc switch
|
||||
{
|
||||
EncounterEgg egg when AbilityBreedLegality.BanHidden7.Contains((ushort)(egg.Species | (egg.Form << 11))) => GetInvalid(LAbilityHiddenUnavailable),
|
||||
EncounterEgg egg when !AbilityBreedLegality.IsHiddenPossible7(egg.Species, egg.Form) => GetInvalid(LAbilityHiddenUnavailable),
|
||||
_ => VALID,
|
||||
};
|
||||
}
|
||||
|
@ -381,7 +380,7 @@ public sealed class AbilityVerifier : Verifier
|
|||
|
||||
return enc switch
|
||||
{
|
||||
EncounterEgg egg when AbilityBreedLegality.BanHidden8b.Contains((ushort)(egg.Species | (egg.Form << 11))) => GetInvalid(LAbilityHiddenUnavailable),
|
||||
EncounterEgg egg when !AbilityBreedLegality.IsHiddenPossibleHOME(egg.Species) => GetInvalid(LAbilityHiddenUnavailable),
|
||||
_ => VALID,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -44,19 +44,26 @@ public sealed class FormVerifier : Verifier
|
|||
switch ((Species)species)
|
||||
{
|
||||
case Pikachu when Info.Generation == 6: // Cosplay
|
||||
bool isStatic = enc is EncounterStatic6;
|
||||
bool validCosplay = form == (isStatic ? enc.Form : 0);
|
||||
if (!validCosplay)
|
||||
return GetInvalid(isStatic ? LFormPikachuCosplayInvalid : LFormPikachuCosplay);
|
||||
if (enc is not EncounterStatic6 s6)
|
||||
{
|
||||
if (form == 0)
|
||||
break; // Regular Pikachu, OK.
|
||||
return GetInvalid(LFormPikachuCosplay);
|
||||
}
|
||||
if (form != s6.Form)
|
||||
return GetInvalid(LFormPikachuCosplayInvalid);
|
||||
if (pk.Format != 6)
|
||||
return GetInvalid(LTransferBad); // Can't transfer.
|
||||
break;
|
||||
|
||||
// LGP/E: Can't get the other game's Starter form.
|
||||
case Pikachu when form is not 0 && ParseSettings.ActiveTrainer is SAV7b {Version:GameVersion.GE}:
|
||||
case Eevee when form is not 0 && ParseSettings.ActiveTrainer is SAV7b {Version:GameVersion.GP}:
|
||||
return GetInvalid(LFormBattle);
|
||||
|
||||
case Pikachu when Info.Generation >= 7: // Cap
|
||||
bool validCap = form == (enc is EncounterInvalid or EncounterEgg ? 0 : enc.Form);
|
||||
if (!validCap)
|
||||
var expectForm = enc is EncounterInvalid or EncounterEgg ? 0 : enc.Form;
|
||||
if (form != expectForm)
|
||||
{
|
||||
bool gift = enc is MysteryGift g && g.Form != form;
|
||||
var msg = gift ? LFormPikachuEventInvalid : LFormInvalidGame;
|
||||
|
@ -77,10 +84,8 @@ public sealed class FormVerifier : Verifier
|
|||
return GetInvalid(LFormItemInvalid);
|
||||
|
||||
case Arceus:
|
||||
{
|
||||
var arceus = FormItem.GetFormArceus(pk.HeldItem, pk.Format);
|
||||
return arceus != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
||||
}
|
||||
case Keldeo when enc.Generation != 5 || pk.Format >= 8:
|
||||
// can mismatch in gen5 via BW tutor and transfer up
|
||||
// can mismatch in gen8+ as the form activates in battle when knowing the move; outside of battle can be either state.
|
||||
|
@ -91,10 +96,8 @@ public sealed class FormVerifier : Verifier
|
|||
return GetInvalid(LMoveKeldeoMismatch);
|
||||
break;
|
||||
case Genesect:
|
||||
{
|
||||
var genesect = FormItem.GetFormGenesect(pk.HeldItem);
|
||||
return genesect != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
||||
}
|
||||
case Greninja:
|
||||
if (form > 1) // Ash Battle Bond active
|
||||
return GetInvalid(LFormBattle);
|
||||
|
@ -141,10 +144,8 @@ public sealed class FormVerifier : Verifier
|
|||
return GetInvalid(LGenderInvalidNone);
|
||||
|
||||
case Silvally:
|
||||
{
|
||||
var silvally = FormItem.GetFormSilvally(pk.HeldItem);
|
||||
return silvally != form ? GetInvalid(LFormItemInvalid) : GetValid(LFormItem);
|
||||
}
|
||||
|
||||
// Form doesn't exist in SM; cannot originate from that game.
|
||||
case Rockruff when enc.Generation == 7 && form == 1 && pk.SM:
|
||||
|
@ -153,12 +154,10 @@ public sealed class FormVerifier : Verifier
|
|||
|
||||
// Toxel encounters have already been checked for the nature-specific evolution criteria.
|
||||
case Toxtricity when enc.Species == (int)Toxtricity:
|
||||
{
|
||||
// The game enforces the Nature for Toxtricity encounters too!
|
||||
if (pk.Form != ToxtricityUtil.GetAmpLowKeyResult(pk.Nature))
|
||||
return GetInvalid(LFormInvalidNature);
|
||||
break;
|
||||
}
|
||||
|
||||
// Ogerpon's form changes depending on its held mask
|
||||
case Ogerpon when (form & 3) != FormItem.GetFormOgerpon(pk.HeldItem):
|
||||
|
|
|
@ -14,9 +14,8 @@ public sealed class IndividualValueVerifier : Verifier
|
|||
{
|
||||
switch (data.EncounterMatch)
|
||||
{
|
||||
case EncounterSlot7GO:
|
||||
case EncounterSlot8GO:
|
||||
VerifyIVsGoTransfer(data);
|
||||
case IPogoSlot s:
|
||||
VerifyIVsGoTransfer(data, s);
|
||||
break;
|
||||
case IFlawlessIVCount s:
|
||||
VerifyIVsFlawless(data, s);
|
||||
|
@ -83,9 +82,9 @@ public sealed class IndividualValueVerifier : Verifier
|
|||
data.AddLine(GetInvalid(string.Format(LIVF_COUNT0_31, count)));
|
||||
}
|
||||
|
||||
private void VerifyIVsGoTransfer(LegalityAnalysis data)
|
||||
private void VerifyIVsGoTransfer(LegalityAnalysis data, IPogoSlot g)
|
||||
{
|
||||
if (data.EncounterMatch is IPogoSlot g && !g.GetIVsValid(data.Entity))
|
||||
if (!g.GetIVsValid(data.Entity))
|
||||
data.AddLine(GetInvalid(LIVNotCorrect));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -799,9 +799,10 @@ public sealed class MiscVerifier : Verifier
|
|||
data.AddLine(GetInvalid(LStatNatureInvalid));
|
||||
}
|
||||
|
||||
private static string GetMoveName<T>(T pk, int index) where T : PKM, ITechRecord => ParseSettings.MoveStrings[pk.Permit.RecordPermitIndexes[index]];
|
||||
|
||||
private void VerifyTechRecordSWSH<T>(LegalityAnalysis data, T pk) where T : PKM, ITechRecord
|
||||
{
|
||||
string GetMoveName(int index) => ParseSettings.MoveStrings[pk.Permit.RecordPermitIndexes[index]];
|
||||
var evos = data.Info.EvoChainsAllGens.Gen8;
|
||||
if (evos.Length == 0)
|
||||
{
|
||||
|
@ -810,7 +811,7 @@ public sealed class MiscVerifier : Verifier
|
|||
{
|
||||
if (!pk.GetMoveRecordFlag(i))
|
||||
continue;
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(i))));
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -836,7 +837,7 @@ public sealed class MiscVerifier : Verifier
|
|||
continue;
|
||||
}
|
||||
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(i))));
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -849,7 +850,6 @@ public sealed class MiscVerifier : Verifier
|
|||
|
||||
private void VerifyTechRecordSV(LegalityAnalysis data, PK9 pk)
|
||||
{
|
||||
string GetMoveName(int index) => ParseSettings.MoveStrings[pk.Permit.RecordPermitIndexes[index]];
|
||||
var evos = data.Info.EvoChainsAllGens.Gen9;
|
||||
if (evos.Length == 0)
|
||||
{
|
||||
|
@ -858,7 +858,7 @@ public sealed class MiscVerifier : Verifier
|
|||
{
|
||||
if (!pk.GetMoveRecordFlag(i))
|
||||
continue;
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(i))));
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -884,7 +884,7 @@ public sealed class MiscVerifier : Verifier
|
|||
break;
|
||||
}
|
||||
if (!preEvoHas)
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(i))));
|
||||
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, GetMoveName(pk, i))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using static PKHeX.Core.Species;
|
||||
using static PKHeX.Core.EntityContext;
|
||||
|
||||
|
@ -907,11 +908,26 @@ public static class FormConverter
|
|||
|
||||
public static string GetGigantamaxName(IReadOnlyList<string> forms) => forms[Gigantamax];
|
||||
|
||||
private const byte AlcremieCountDecoration = 7;
|
||||
private const byte AlcremieCountForms = 9;
|
||||
private const byte AlcremieCountDifferent = AlcremieCountDecoration * AlcremieCountForms;
|
||||
|
||||
/// <summary>
|
||||
/// Used to enumerate the possible combinations of Alcremie forms and decorations.
|
||||
/// </summary>
|
||||
/// <param name="forms">Form names</param>
|
||||
/// <remarks>
|
||||
/// Used for Pokédex display listings.
|
||||
/// </remarks>>
|
||||
public static string[] GetAlcremieFormList(IReadOnlyList<string> forms)
|
||||
{
|
||||
const int deco = 7;
|
||||
const byte fc = 9;
|
||||
var result = new string[deco * fc]; // 63
|
||||
var result = new string[AlcremieCountDifferent]; // 63
|
||||
SetAlcremieFormList(forms, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void SetAlcremieFormList(IReadOnlyList<string> forms, Span<string> result)
|
||||
{
|
||||
SetDecorations(result, 0, forms[(int)Alcremie]); // Vanilla Cream
|
||||
SetDecorations(result, 1, forms[RubyCream]);
|
||||
SetDecorations(result, 2, forms[MatchaCream]);
|
||||
|
@ -922,12 +938,12 @@ public static class FormConverter
|
|||
SetDecorations(result, 7, forms[CaramelSwirl]);
|
||||
SetDecorations(result, 8, forms[RainbowSwirl]);
|
||||
|
||||
return result;
|
||||
return;
|
||||
|
||||
static void SetDecorations(string[] result, int f, string baseName)
|
||||
static void SetDecorations(Span<string> result, [ConstantExpected] byte f, string baseName)
|
||||
{
|
||||
int start = f * deco;
|
||||
var slice = result.AsSpan(start, deco);
|
||||
int start = f * AlcremieCountDecoration;
|
||||
var slice = result.Slice(start, AlcremieCountDecoration);
|
||||
for (int i = 0; i < slice.Length; i++)
|
||||
slice[i] = $"{baseName} ({(AlcremieDecoration)i})";
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace PKHeX.Core;
|
|||
/// </remarks>
|
||||
public enum EntityContext : byte
|
||||
{
|
||||
// Generation numerically so we can cast to and from int for most cases.
|
||||
None = 0,
|
||||
Gen1 = 1,
|
||||
Gen2 = 2,
|
||||
|
@ -24,16 +25,28 @@ public enum EntityContext : byte
|
|||
Gen8 = 8,
|
||||
Gen9 = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Internal separator to pivot between adjacent contexts.
|
||||
/// </summary>
|
||||
SplitInvalid,
|
||||
/// <summary> Let's Go, Pikachu! & Let's Go, Eevee! </summary>
|
||||
Gen7b,
|
||||
/// <summary> Legends: Arceus </summary>
|
||||
Gen8a,
|
||||
/// <summary> Brilliant Diamond & Shining Pearl </summary>
|
||||
Gen8b,
|
||||
|
||||
/// <summary>
|
||||
/// Internal separator to bounds check count.
|
||||
/// </summary>
|
||||
MaxInvalid,
|
||||
}
|
||||
|
||||
public static class EntityContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the generation number of the context.
|
||||
/// </summary>
|
||||
public static int Generation(this EntityContext value) => value < SplitInvalid ? (int)value : value switch
|
||||
{
|
||||
Gen7b => 7,
|
||||
|
@ -42,8 +55,16 @@ public static class EntityContextExtensions
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the context is a defined value assigned to a valid context.
|
||||
/// </summary>
|
||||
/// <returns>True if the context is valid.</returns>
|
||||
public static bool IsValid(this EntityContext value) => value is not (0 or SplitInvalid) and < MaxInvalid;
|
||||
|
||||
/// <summary>
|
||||
/// Get a pre-defined single game version associated with the context.
|
||||
/// </summary>
|
||||
/// <remarks>Game ID choice here is the developer's choice; if multiple game sets exist for a context, one from the most recent was chosen.</remarks>
|
||||
public static GameVersion GetSingleGameVersion(this EntityContext value) => value switch
|
||||
{
|
||||
Gen1 => GameVersion.RD,
|
||||
|
@ -63,6 +84,9 @@ public static class EntityContextExtensions
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Get the game console associated with the context.
|
||||
/// </summary>
|
||||
public static GameConsole GetConsole(this EntityContext value) => value switch
|
||||
{
|
||||
Gen1 or Gen2 => GameConsole.GB,
|
||||
|
@ -74,8 +98,15 @@ public static class EntityContextExtensions
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets all <see cref="GameVersion"/> values that fall within the context.
|
||||
/// </summary>
|
||||
public static GameVersion[] GetVersionsWithin(this EntityContext value, GameVersion[] source) => value.GetVersionLump().GetVersionsWithinRange(source);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding lumped <see cref="GameVersion"/> value for the context.
|
||||
/// </summary>
|
||||
/// <remarks>Shouldn't really use this; see <see cref="GetVersionsWithin"/>.</remarks>
|
||||
public static GameVersion GetVersionLump(this EntityContext value) => value switch
|
||||
{
|
||||
Gen1 => GameVersion.Gen1,
|
||||
|
@ -95,6 +126,9 @@ public static class EntityContextExtensions
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corresponding <see cref="EntityContext"/> value for the <see cref="GameVersion"/>.
|
||||
/// </summary>
|
||||
public static EntityContext GetContext(this GameVersion version) => version switch
|
||||
{
|
||||
GameVersion.GP or GameVersion.GE or GameVersion.GO => Gen7b,
|
||||
|
|
|
@ -3,7 +3,10 @@ namespace PKHeX.Core;
|
|||
/// <summary>
|
||||
/// Hardware console types.
|
||||
/// </summary>
|
||||
/// <remarks>Related to <see cref="EntityContext"/>; no need to specify side-game consoles like the N64 as they're tied to the mainline console.</remarks>
|
||||
/// <remarks>
|
||||
/// Related to <see cref="EntityContext"/>; no need to specify side-game consoles like the N64 as they're tied to the mainline console.
|
||||
/// Console revisions (like GameBoy Color) or 3DS-XL are not included, again, only care about console limitations that run the games.
|
||||
/// </remarks>
|
||||
public enum GameConsole : byte
|
||||
{
|
||||
/// <summary> Invalid console type. </summary>
|
||||
|
|
|
@ -321,9 +321,14 @@ public sealed class SAV3GCMemoryCard
|
|||
int offset = (DirectoryBlock_Used * BLOCK_SIZE) + (EntrySelected * DENTRY_SIZE);
|
||||
string GameCode = EncodingType.GetString(Data, offset, 4);
|
||||
string Makercode = EncodingType.GetString(Data, offset + 0x04, 2);
|
||||
string FileName = EncodingType.GetString(Data, offset + 0x08, DENTRY_STRLEN);
|
||||
|
||||
return $"{Makercode}-{GameCode}-{Util.TrimFromZero(FileName)}.gci";
|
||||
Span<char> FileName = stackalloc char[DENTRY_STRLEN];
|
||||
EncodingType.GetString(Data.AsSpan(offset + 0x08, DENTRY_STRLEN));
|
||||
var zero = FileName.IndexOf('\0');
|
||||
if (zero >= 0)
|
||||
FileName = FileName[..zero];
|
||||
|
||||
return $"{Makercode}-{GameCode}-{FileName}.gci";
|
||||
}
|
||||
|
||||
public ReadOnlyMemory<byte> ReadSaveGameData()
|
||||
|
|
|
@ -51,8 +51,18 @@ public sealed class GP1 : IEncounterInfo, IFixedAbilityNumber, IScaledSizeReadOn
|
|||
|
||||
public static void InitializeBlank(Span<byte> data) => Blank20.CopyTo(data);
|
||||
|
||||
public string Username1 => Util.TrimFromZero(Encoding.ASCII.GetString(Data.AsSpan(0x00, 0x10)));
|
||||
public string Username2 => Util.TrimFromZero(Encoding.ASCII.GetString(Data.AsSpan(0x10, 0x10)));
|
||||
private static ReadOnlySpan<byte> GetLength(ReadOnlySpan<byte> buffer)
|
||||
{
|
||||
var length = buffer.IndexOf((byte)0);
|
||||
if (length == -1)
|
||||
return buffer;
|
||||
return buffer[..length];
|
||||
}
|
||||
|
||||
private static string GetString(ReadOnlySpan<byte> buffer) => Encoding.ASCII.GetString(GetLength(buffer));
|
||||
|
||||
public string Username1 => GetString(Data.AsSpan(0x00, 0x10));
|
||||
public string Username2 => GetString(Data.AsSpan(0x10, 0x10));
|
||||
|
||||
public ushort Species => ReadUInt16LittleEndian(Data.AsSpan(0x28)); // s32, just read as u16
|
||||
public int CP => ReadInt32LittleEndian(Data.AsSpan(0x2C));
|
||||
|
@ -104,9 +114,9 @@ public sealed class GP1 : IEncounterInfo, IFixedAbilityNumber, IScaledSizeReadOn
|
|||
public int Move1 => ReadInt32LittleEndian(Data.AsSpan(0x74)); // uses Go Indexes
|
||||
public int Move2 => ReadInt32LittleEndian(Data.AsSpan(0x78)); // uses Go Indexes
|
||||
|
||||
public string GeoCityName => Util.TrimFromZero(Encoding.ASCII.GetString(Data, 0x7C, 0x60)); // dunno length
|
||||
public string GeoCityName => GetString(Data.AsSpan(0x7C, 0x60)); // dunno length
|
||||
|
||||
public string Nickname => Util.TrimFromZero(Encoding.ASCII.GetString(Data, 0x12D, 0x20)); // dunno length
|
||||
public string Nickname => GetString(Data.AsSpan(0x12D, 0x20)); // dunno length
|
||||
|
||||
public static readonly IReadOnlyList<string> Genders = GameInfo.GenderSymbolASCII;
|
||||
public string GenderString => (uint) Gender >= Genders.Count ? string.Empty : Genders[Gender];
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
@ -20,20 +19,30 @@ namespace PKHeX.Core;
|
|||
/// </summary>
|
||||
public static class QR7
|
||||
{
|
||||
private static readonly HashSet<ushort> GenderDifferences = new()
|
||||
public const int SIZE = 0x1A2;
|
||||
|
||||
private static ReadOnlySpan<byte> GenderDifferences => new byte[]
|
||||
{
|
||||
003, 012, 019, 020, 025, 026, 041, 042, 044, 045,
|
||||
064, 065, 084, 085, 097, 111, 112, 118, 119, 123,
|
||||
129, 130, 154, 165, 166, 178, 185, 186, 190, 194,
|
||||
195, 198, 202, 203, 207, 208, 212, 214, 215, 217,
|
||||
221, 224, 229, 232, 255, 256, 257, 267, 269, 272,
|
||||
274, 275, 307, 308, 315, 316, 317, 322, 323, 332,
|
||||
350, 369, 396, 397, 398, 399, 400, 401, 402, 403,
|
||||
404, 405, 407, 415, 417, 418, 419, 424, 443, 444,
|
||||
445, 449, 450, 453, 454, 456, 457, 459, 460, 461,
|
||||
464, 465, 473, 521, 592, 593, 668, 678,
|
||||
0x08, 0x10, 0x18, 0x06, 0x00, 0x36, 0x00, 0x00, 0x03, 0x00,
|
||||
0x30, 0x00, 0x02, 0x80, 0xC1, 0x08, 0x06, 0x00, 0x00, 0x04,
|
||||
0x60, 0x00, 0x04, 0x46, 0x4C, 0x8C, 0xD1, 0x22, 0x21, 0x01,
|
||||
0x00, 0x80, 0x03, 0x28, 0x0D, 0x00, 0x00, 0x00, 0x18, 0x38,
|
||||
0x0C, 0x10, 0x00, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xF0,
|
||||
0xBF, 0x80, 0x0E, 0x01, 0x00, 0x38, 0x66, 0x3B, 0x03, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x10, 0x40,
|
||||
};
|
||||
|
||||
private static bool IsGenderDifferent(ushort species)
|
||||
{
|
||||
var index = species >> 3;
|
||||
var table = GenderDifferences;
|
||||
if (index >= table.Length)
|
||||
return false;
|
||||
return (table[index] & (1 << (species & 7))) != 0;
|
||||
}
|
||||
|
||||
private static void GetRawQR(Span<byte> dest, ushort species, byte form, bool shiny, byte gender)
|
||||
{
|
||||
dest[..6].Fill(0xFF);
|
||||
|
@ -48,7 +57,7 @@ public static class QR7
|
|||
else if (pi.Genderless)
|
||||
gender = 2;
|
||||
else
|
||||
biGender = !GenderDifferences.Contains(species);
|
||||
biGender = !IsGenderDifferent(species);
|
||||
|
||||
dest[0x2A] = form;
|
||||
dest[0x2B] = gender;
|
||||
|
@ -58,21 +67,21 @@ public static class QR7
|
|||
|
||||
public static byte[] GenerateQRData(PK7 pk7, int box = 0, int slot = 0, int num_copies = 1)
|
||||
{
|
||||
if (box > 31)
|
||||
box = 31;
|
||||
if (slot > 29)
|
||||
slot = 29;
|
||||
if (box < 0)
|
||||
box = 0;
|
||||
if (slot < 0)
|
||||
slot = 0;
|
||||
if (num_copies < 0)
|
||||
num_copies = 1;
|
||||
byte[] data = new byte[SIZE];
|
||||
SetQRData(pk7, data, box, slot, num_copies);
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void SetQRData(PK7 pk7, Span<byte> span, int box = 0, int slot = 0, int num_copies = 1)
|
||||
{
|
||||
box = Math.Clamp(box, 0, 31);
|
||||
slot = Math.Clamp(slot, 0, 29);
|
||||
num_copies = Math.Min(num_copies, 1);
|
||||
if (span.Length < SIZE)
|
||||
throw new ArgumentException($"Span must be at least {SIZE} bytes long.", nameof(span));
|
||||
|
||||
byte[] data = new byte[0x1A2];
|
||||
var span = data.AsSpan();
|
||||
WriteUInt32LittleEndian(span, 0x454B4F50); // POKE magic
|
||||
data[0x4] = 0xFF; // QR Type
|
||||
span[0x4] = 0xFF; // QR Type
|
||||
WriteInt32LittleEndian(span[0x08..], box);
|
||||
WriteInt32LittleEndian(span[0x0C..], slot);
|
||||
WriteInt32LittleEndian(span[0x10..], num_copies); // No need to check max num_copies, payload parser handles it on-console.
|
||||
|
@ -82,6 +91,5 @@ public static class QR7
|
|||
|
||||
var chk = Checksums.CRC16Invert(span[..0x1A0]);
|
||||
WriteUInt16LittleEndian(span[0x1A0..], chk);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
@ -141,10 +140,22 @@ public static partial class Util
|
|||
|
||||
private const string HexChars = "0123456789ABCDEF";
|
||||
|
||||
/// <summary>
|
||||
/// Converts the byte array into a hex string (non-spaced, bytes in reverse order).
|
||||
/// </summary>
|
||||
public static string GetHexStringFromBytes(ReadOnlySpan<byte> data)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(data.Length is (4 or 8 or 12 or 16));
|
||||
Span<char> result = stackalloc char[data.Length * 2];
|
||||
GetHexStringFromBytes(data, result);
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetHexStringFromBytes(ReadOnlySpan{byte})"/>
|
||||
public static void GetHexStringFromBytes(ReadOnlySpan<byte> data, Span<char> result)
|
||||
{
|
||||
if (result.Length != data.Length * 2)
|
||||
throw new ArgumentException("Result buffer must be twice the size of the input buffer.");
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
// Write tuples from the opposite side of the result buffer.
|
||||
|
@ -152,7 +163,6 @@ public static partial class Util
|
|||
result[offset + 0] = HexChars[data[i] >> 4];
|
||||
result[offset + 1] = HexChars[data[i] & 0xF];
|
||||
}
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -164,14 +174,21 @@ public static partial class Util
|
|||
if (str.IsWhiteSpace())
|
||||
return string.Empty;
|
||||
|
||||
int ctr = 0;
|
||||
Span<char> result = stackalloc char[str.Length];
|
||||
int ctr = GetOnlyHex(str, ref result);
|
||||
return new string(result[..ctr]);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="GetOnlyHex(ReadOnlySpan{char})"/>
|
||||
public static int GetOnlyHex(ReadOnlySpan<char> str, ref Span<char> result)
|
||||
{
|
||||
int ctr = 0;
|
||||
foreach (var c in str)
|
||||
{
|
||||
if (char.IsAsciiHexDigit(c))
|
||||
result[ctr++] = c;
|
||||
}
|
||||
return new string(result[..ctr]);
|
||||
return ctr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -184,6 +201,13 @@ public static partial class Util
|
|||
return string.Empty;
|
||||
|
||||
Span<char> result = stackalloc char[span.Length];
|
||||
ToTitleCase(span, result);
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ToTitleCase(ReadOnlySpan{char})"/>
|
||||
public static void ToTitleCase(ReadOnlySpan<char> span, Span<char> result)
|
||||
{
|
||||
// Add each word to the string builder. Continue from the first index that isn't a space.
|
||||
// Add the first character as uppercase, then add each successive character as lowercase.
|
||||
bool first = true;
|
||||
|
@ -205,7 +229,6 @@ public static partial class Util
|
|||
}
|
||||
result[i] = c;
|
||||
}
|
||||
return new string(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -213,12 +236,15 @@ public static partial class Util
|
|||
/// </summary>
|
||||
/// <param name="input">String to trim.</param>
|
||||
/// <returns>Trimmed string.</returns>
|
||||
public static string TrimFromZero(string input) => TrimFromFirst(input, '\0');
|
||||
public static ReadOnlySpan<char> TrimFromZero(ReadOnlySpan<char> input) => TrimFromFirst(input, '\0');
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static string TrimFromFirst(string input, char c)
|
||||
private static ReadOnlySpan<char> TrimFromFirst(ReadOnlySpan<char> input, char c)
|
||||
{
|
||||
int index = input.IndexOf(c);
|
||||
return index < 0 ? input : input[..index];
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="TrimFromZero(ReadOnlySpan{char})"/>
|
||||
public static string TrimFromZero(string input) => TrimFromZero(input.AsSpan()).ToString();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace PKHeX.Drawing.Misc;
|
||||
|
@ -11,7 +11,7 @@ public static class QRImageUtil
|
|||
var foreground = new Bitmap(preview.Width + 4, preview.Height + 4);
|
||||
using (Graphics gfx = Graphics.FromImage(foreground))
|
||||
{
|
||||
gfx.FillRectangle(new SolidBrush(Color.White), 0, 0, foreground.Width, foreground.Height);
|
||||
gfx.FillRectangle(Brushes.White, 0, 0, foreground.Width, foreground.Height);
|
||||
int x = (foreground.Width / 2) - (preview.Width / 2);
|
||||
int y = (foreground.Height / 2) - (preview.Height / 2);
|
||||
gfx.DrawImage(preview, x, y);
|
||||
|
@ -25,17 +25,17 @@ public static class QRImageUtil
|
|||
}
|
||||
}
|
||||
|
||||
public static Bitmap GetQRImageExtended(Font font, Image qr, Image pk, int width, int height, string[] lines, string extraText)
|
||||
public static Bitmap GetQRImageExtended(Font font, Image qr, Image pk, int width, int height, ReadOnlySpan<string> lines, string extraText)
|
||||
{
|
||||
var pic = GetQRImage(qr, pk);
|
||||
return ExtendImage(font, qr, width, height, pic, lines, extraText);
|
||||
}
|
||||
|
||||
private static Bitmap ExtendImage(Font font, Image qr, int width, int height, Image pic, string[] lines, string extraText)
|
||||
private static Bitmap ExtendImage(Font font, Image qr, int width, int height, Image pic, ReadOnlySpan<string> lines, string extraText)
|
||||
{
|
||||
var newpic = new Bitmap(width, height);
|
||||
using Graphics g = Graphics.FromImage(newpic);
|
||||
g.FillRectangle(new SolidBrush(Color.White), 0, 0, newpic.Width, newpic.Height);
|
||||
g.FillRectangle(Brushes.White, 0, 0, newpic.Width, newpic.Height);
|
||||
g.DrawImage(pic, 0, 0);
|
||||
|
||||
g.DrawString(GetLine(lines, 0), font, Brushes.Black, new PointF(18, qr.Height - 5));
|
||||
|
@ -46,5 +46,5 @@ public static class QRImageUtil
|
|||
return newpic;
|
||||
}
|
||||
|
||||
private static string GetLine(string[] lines, int line) => lines.Length <= line ? string.Empty : lines[line];
|
||||
private static string GetLine(ReadOnlySpan<string> lines, int line) => lines.Length <= line ? string.Empty : lines[line];
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ public partial class SAV_PokedexGG : Form
|
|||
return false;
|
||||
}
|
||||
|
||||
// sanity check forms -- SM does not have totem form dex bits
|
||||
// sanity check forms -- GG does not have totem form dex bits
|
||||
int count = SAV.Personal[bspecies].FormCount;
|
||||
if (count < ds.Count)
|
||||
ds.RemoveAt(count); // remove last
|
||||
|
|
|
@ -167,8 +167,8 @@ public partial class SAV_PokedexLA : Form
|
|||
if (!hasForms)
|
||||
return false;
|
||||
|
||||
var ds = FormConverter.GetFormList(species, GameInfo.Strings.types, GameInfo.Strings.forms, Main.GenderSymbols, SAV.Context).ToList();
|
||||
if (ds.Count == 1 && string.IsNullOrEmpty(ds[0]))
|
||||
var ds = FormConverter.GetFormList(species, GameInfo.Strings.types, GameInfo.Strings.forms, Main.GenderSymbols, SAV.Context);
|
||||
if (ds.Length == 1 && string.IsNullOrEmpty(ds[0]))
|
||||
{
|
||||
// empty
|
||||
LB_Forms.Enabled = CB_DisplayForm.Enabled = false;
|
||||
|
|
|
@ -143,7 +143,7 @@ public partial class SAV_PokedexSWSH : Form
|
|||
var s = GameInfo.Strings;
|
||||
if (species == (int)Species.Alcremie)
|
||||
return FormConverter.GetAlcremieFormList(s.forms);
|
||||
return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen8).ToArray();
|
||||
return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen8);
|
||||
}
|
||||
|
||||
private void SetEntry(int index)
|
||||
|
|
|
@ -179,7 +179,7 @@ public partial class SAV_PokedexSV : Form
|
|||
var s = GameInfo.Strings;
|
||||
if (species == (int)Species.Alcremie)
|
||||
return FormConverter.GetAlcremieFormList(s.forms);
|
||||
return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen9).ToArray();
|
||||
return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen9);
|
||||
}
|
||||
|
||||
private void SetEntry(int index)
|
||||
|
|
|
@ -239,7 +239,7 @@ public partial class SAV_PokedexSVKitakami : Form
|
|||
var s = GameInfo.Strings;
|
||||
if (species == (int)Species.Alcremie)
|
||||
return FormConverter.GetAlcremieFormList(s.forms);
|
||||
return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen9).ToArray();
|
||||
return FormConverter.GetFormList(species, s.Types, s.forms, GameInfo.GenderSymbolASCII, EntityContext.Gen9);
|
||||
}
|
||||
|
||||
private void SetEntry(int index)
|
||||
|
|
|
@ -15,32 +15,26 @@ public class RaidTests
|
|||
byte[] data = raw.ToByteArray();
|
||||
var pk8 = new PK8(data);
|
||||
|
||||
bool found = false;
|
||||
var seeds = new XoroMachineSkip(pk8.EncryptionConstant, pk8.PID);
|
||||
foreach (var s in seeds)
|
||||
{
|
||||
if (s != seed)
|
||||
continue;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
bool found = IsPotentialRaidSeed(pk8.EncryptionConstant, pk8.PID, seed);
|
||||
found.Should().BeTrue();
|
||||
|
||||
var la = new LegalityAnalysis(pk8);
|
||||
var enc = la.EncounterMatch;
|
||||
|
||||
var compare = enc switch
|
||||
{
|
||||
EncounterStatic8N r => r.Verify(pk8, seed),
|
||||
EncounterStatic8ND r => r.Verify(pk8, seed),
|
||||
EncounterStatic8NC r => r.Verify(pk8, seed),
|
||||
EncounterStatic8U r => r.Verify(pk8, seed),
|
||||
_ => throw new ArgumentException(nameof(enc)),
|
||||
};
|
||||
compare.Should().BeTrue();
|
||||
|
||||
var s64 = (ISeedCorrelation64<Core.PKM>)enc;
|
||||
if (enc is not ISeedCorrelation64<Core.PKM> s64)
|
||||
throw new ArgumentException(nameof(enc));
|
||||
s64.TryGetSeed(pk8, out ulong detected).Should().BeTrue();
|
||||
detected.Should().Be(seed);
|
||||
}
|
||||
|
||||
private static bool IsPotentialRaidSeed(uint ec, uint pid, ulong expect)
|
||||
{
|
||||
var seeds = new XoroMachineSkip(ec, pid);
|
||||
foreach (var seed in seeds)
|
||||
{
|
||||
if (seed != expect)
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue