Compare commits

..

1 Commits

Author SHA1 Message Date
Kurt
1891925fe7 Update 26.03.06 2026-03-06 23:13:40 -06:00
7783 changed files with 16309 additions and 23259 deletions

View File

@ -10,7 +10,7 @@ Die folgenden Dateien werden unterstützt:
* Einzelne Pokémon (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* Wunderkarten (\*.pgt, \*.pcd, \*.pgf, .wc\*), inklusive Konvertierung zu .pk\*
* Import von GO Park Pokémon (\*.gp1) inklusive Konvertierung zu .pb7
* Import von Teams aus entschlüsselten 3DS Kampfvideos.
* Import von Teams aus entschlüsselten 3DS Battle Videos.
* Transfer von einer Generation zur anderen, mit automatischer Umwandlung des Formats.
Alle Daten werden so angezeigt, dass sie bearbeitet und gespeichert werden können.

View File

@ -10,7 +10,7 @@ Soporta los siguientes archivos:
* Archivos de entidades individuales de Pokémon (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* Archivos de Regalos Misteriosos (\*.pgt, \*.pcd, \*.pgf, .wc\*) incluyendo conversión a .pk\*
* Importar archivos de entidades de GO Park (\*.gp1) incluyendo conversión a .pb7
* Importar equipos desde archivos de Vídeos de Combate de 3DS desencriptados.
* Importar equipos desde archivos Decrypted 3DS Battle Videos
* Pasar de una generación a la siguiente, convirtiendo los archivos en el proceso.
Los datos son visualizados en una forma que permite modificarlos y guardarlos.
@ -42,7 +42,7 @@ La generación de códigos QR de PKHeX es la de [QRCoder](https://github.com/cod
La colección de sprites de Pokémons Shiny de PKHeX fue tomada de [pokesprite](https://github.com/msikma/pokesprite), licenciado bajo [la licencia MIT](https://github.com/msikma/pokesprite/blob/master/LICENSE).
La colección de sprites de Leyendas Pokémon: Arceus de PKHeX proviene del proyecto [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) y su gran cantidad de colaboradores y contribuyentes.
PKHeX's Pokémon Legends: Arceus sprite collection is taken from the [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) project and its abundance of collaborators and contributors.
### IDE

View File

@ -10,7 +10,7 @@ PKHeX(포케헥스)
* 개별 포켓몬 엔티티 파일 (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
* 이상한소포 파일(\*.pgt, \*.pcd, \*.pgf, .wc\*)을 .pk로 변환하는 기능 포함
* GO 파크 엔티티 가져오기 (\*.gp1) .pb7로 변환 포함
* 복호화된 3DS 배틀비디오에서 팀 가져오기
* Decrypted 3DS Battle Videos에서 팀 가져오기
* 한 세대에서 다른 세대로 이동하면서 그 과정에서 형식이 변환됩니다.
데이터는 편집하고 저장할 수 있는 보기로 표시됩니다.
@ -42,7 +42,7 @@ PKHeX의 QR 코드 생성 코드는 [the MIT license](https://github.com/codebud
PKHeX의 이로치(색이다른) 스프라이트 컬렉션은 [the MIT license](https://github.com/msikma/pokesprite/blob/master/LICENSE)에 따라 라이선스가 부여된 [pokesprite](https://github.com/msikma/pokesprite)에서 가져왔습니다.
PKHeX의 Pokémon LEGENDS 아르세우스 스프라이트 컬렉션은 [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) 프로젝트와 수많은 협력자 및 기여자의 도움을 받아 만들어졌습니다.
PKheX의 Pokémon LEGENDS 아르세우스 스프라이트 컬렉션은 [National Pokédex - Icon Dex](https://www.deviantart.com/pikafan2000/art/National-Pokedex-Version-Delta-Icon-Dex-824897934) 프로젝트와 수많은 협력자 및 기여자의 도움을 받아 만들어졌습니다.
### IDE(통합 개발 환경)

View File

@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>26.04.11</Version>
<Version>26.03.06</Version>
<LangVersion>14</LangVersion>
<Nullable>enable</Nullable>
<NeutralLanguage>en</NeutralLanguage>

View File

@ -1,5 +1,3 @@
using System;
namespace PKHeX.Core;
/// <summary>
@ -72,9 +70,4 @@ public interface IBattleTemplate : ISpeciesForm, IGigantamaxReadOnly, IDynamaxLe
/// <see cref="PKM.Moves"/> of the Set entity.
/// </summary>
ushort[] Moves { get; }
/// <summary>
/// Checks if the properties are probably from a Pokémon Champions set.
/// </summary>
bool IsChampions => Level == 50 && !IVs.ContainsAnyExcept(31) && EffortValues.IsChampions(EVs);
}

View File

@ -342,12 +342,12 @@ private bool ParseLineNature(ReadOnlySpan<char> input, ReadOnlySpan<string> natu
return false;
var nature = (Nature)index;
if (!nature.IsFixed)
if (!nature.IsFixed())
{
LogError(NatureUnrecognized, input);
return false;
}
if (Nature.IsFixed && Nature != nature)
if (Nature != Nature.Random && Nature != nature)
{
LogError(NatureAlreadySpecified, input);
return false;
@ -627,7 +627,7 @@ private void AddEVs(List<string> result, in BattleTemplateExportSettings setting
BattleTemplateToken.EVsAppendNature => GetStringStatsNatureAmp(EVs, 0, nameEVs, Nature),
_ => GetStringStats(EVs, 0, nameEVs),
};
if (token is BattleTemplateToken.EVsAppendNature && Nature.IsFixed)
if (token is BattleTemplateToken.EVsAppendNature && Nature.IsFixed())
line += $" ({settings.Localization.Strings.natures[(int)Nature]})";
result.Add(cfg.Push(BattleTemplateToken.EVs, line));
}
@ -1081,7 +1081,7 @@ private bool ParseLineEVs(ReadOnlySpan<char> line, BattleTemplateLocalization lo
return false; // invalid line
}
if (Nature.IsFixed) // specified in a separate Nature line
if (Nature != Nature.Random) // specified in a separate Nature line
LogError(NatureEffortAmpAlreadySpecified, natureName);
else
Nature = (Nature)natureIndex;
@ -1100,7 +1100,7 @@ private bool ParseLineEVs(ReadOnlySpan<char> line, BattleTemplateLocalization lo
result.TreatAmpsAsSpeedNotLast();
var ampNature = AdjustNature(result.Plus, result.Minus);
success &= ampNature;
if (ampNature && currentNature.IsFixed && currentNature != Nature)
if (ampNature && currentNature != Nature.Random && currentNature != Nature)
{
LogError(NatureEffortAmpConflictNature);
Nature = currentNature; // revert to original

View File

@ -205,10 +205,10 @@ public ModifyResult TryModify(TObject entity, IEnumerable<StringInstruction> fil
return result;
}
private static Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[] GetPropertyDictionaries(ReadOnlySpan<Type> types, int expectedMax)
private static Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[] GetPropertyDictionaries(IReadOnlyList<Type> types, int expectedMax)
{
var result = new Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[types.Length];
for (int i = 0; i < types.Length; i++)
var result = new Dictionary<string, PropertyInfo>.AlternateLookup<ReadOnlySpan<char>>[types.Count];
for (int i = 0; i < types.Count; i++)
result[i] = GetPropertyDictionary(types[i], ReflectUtil.GetAllPropertyInfoPublic, expectedMax).GetAlternateLookup<ReadOnlySpan<char>>();
return result;
}

View File

@ -30,10 +30,8 @@ public void SetNickname(string nick)
pk.ClearNickname();
return;
}
pk.PrepareNickname();
pk.Nickname = nick;
pk.IsNicknamed = true;
pk.Nickname = nick;
}
/// <summary>
@ -139,7 +137,7 @@ public bool SetUnshiny()
/// <param name="nature">Desired <see cref="PKM.Nature"/> value to set.</param>
public void SetNature(Nature nature)
{
if (!nature.IsFixed)
if (!nature.IsFixed())
nature = 0; // default valid
var format = pk.Format;
@ -195,11 +193,6 @@ public void ApplySetDetails(IBattleTemplate set)
}
else
{
// Champions revises EV behavior to be /8.
// If the user is requesting a Champions-like set import, apply EVs that way.
if (set.IsChampions)
pk.SetEVsChampions(evs);
else
pk.SetEVs(evs);
}
@ -271,13 +264,6 @@ public void ApplySetDetails(IBattleTemplate set)
pk.RefreshChecksum();
}
private void SetEVsChampions(ReadOnlySpan<int> evs)
{
Span<int> final = stackalloc int[6];
EffortValues.ConvertFromChampions(evs, final);
pk.SetEVs(final);
}
/// <summary>
/// Sets the <see cref="PKM.HeldItem"/> value depending on the current format and the provided item index &amp; format.
/// </summary>

View File

@ -100,13 +100,7 @@ public class EntitySummary : IFatefulEncounterReadOnly // do NOT seal, allow inh
public string Relearn2 => Get(Strings.movelist, Entity.RelearnMove2);
public string Relearn3 => Get(Strings.movelist, Entity.RelearnMove3);
public string Relearn4 => Get(Strings.movelist, Entity.RelearnMove4);
public ushort Checksum => Entity switch
{
ISanityChecksum s => s.Checksum,
PK1 gb => gb.GetSingleListChecksum(),
PK2 gb => gb.GetSingleListChecksum(),
_ => Checksums.CRC16_CCITT(Entity.Data[..Entity.SIZE_STORED]),
};
public ushort Checksum => Entity is ISanityChecksum s ? s.Checksum : Checksums.CRC16_CCITT(Entity.Data[Entity.SIZE_STORED..]);
public int Friendship => Entity.OriginalTrainerFriendship;
public int EggYear => Entity.EggMetDate.GetValueOrDefault().Year;
public int EggMonth => Entity.EggMetDate.GetValueOrDefault().Month;

View File

@ -40,8 +40,7 @@ public static string GetMessage(PKM pk)
return GetMessage(pk7);
var server = GetExploitURLPrefixPKM(pk.Format);
Span<byte> data = stackalloc byte[pk.SIZE_STORED];
pk.WriteEncryptedDataStored(data);
var data = pk.EncryptedBoxData;
return GetMessageBase64(data, server);
}

View File

@ -17,9 +17,6 @@ public sealed class AdvancedSettings
[LocalizedDescription("Hide event variable names for that contain any of the comma-separated substrings below. Removes event values from the GUI that the user doesn't care to view.")]
public string HideEvent8Contains { get; set; } = string.Empty;
[LocalizedDescription("Minimum distance threshold that mouse movement must exceed before a drag operation is started from a slot.")]
public int DragStartThreshold { get; set; } = 0;
[Browsable(false)]
public string[] GetExclusionList8() => Array.ConvertAll(HideEvent8Contains.Split(',', StringSplitOptions.RemoveEmptyEntries), z => z.Trim());
}

View File

@ -4,10 +4,6 @@ public sealed class SoundSettings
{
[LocalizedDescription("Play Sound when loading a new Save File")]
public bool PlaySoundSAVLoad { get; set; } = true;
[LocalizedDescription("Play Sound when popping up Legality Report")]
public bool PlaySoundLegalityCheck { get; set; } = true;
[LocalizedDescription("Play Sound when performing any other action that would be reasonable to sound alert.")]
public bool PlaySoundOther { get; set; } = true;
}

View File

@ -1,12 +1,21 @@
using System;
using System;
namespace PKHeX.Core;
/// <summary>
/// Base class for defining a manipulation of box data.
/// </summary>
public abstract record BoxManipBase(BoxManipType Type, Func<SaveFile, bool> Usable) : IBoxManip
public abstract class BoxManipBase : IBoxManip
{
public BoxManipType Type { get; }
public Func<SaveFile, bool> Usable { get; }
protected BoxManipBase(BoxManipType type, Func<SaveFile, bool> usable)
{
Type = type;
Usable = usable;
}
public abstract string GetPrompt(bool all);
public abstract string GetFail(bool all);
public abstract string GetSuccess(bool all);

View File

@ -5,7 +5,7 @@ namespace PKHeX.Core;
/// <summary>
/// Clears contents of boxes by deleting all that satisfy a criteria.
/// </summary>
public sealed record BoxManipClear(BoxManipType Type, Func<PKM, bool> Criteria, Func<SaveFile, bool> Usable) : BoxManipBase(Type, Usable)
public sealed class BoxManipClear(BoxManipType Type, Func<PKM, bool> criteria, Func<SaveFile, bool> Usable) : BoxManipBase(Type, Usable)
{
public BoxManipClear(BoxManipType Type, Func<PKM, bool> Criteria) : this(Type, Criteria, _ => true) { }
@ -18,6 +18,6 @@ public override int Execute(SaveFile sav, BoxManipParam param)
var (start, stop, reverse) = param;
return sav.ClearBoxes(start, stop, Method);
bool Method(PKM p) => reverse ^ Criteria(p);
bool Method(PKM p) => reverse ^ criteria(p);
}
}

View File

@ -5,7 +5,7 @@ namespace PKHeX.Core;
/// <summary>
/// Clears contents of boxes by deleting all that satisfy a criteria based on a <see cref="SaveFile"/>.
/// </summary>
public sealed record BoxManipClearComplex(BoxManipType Type, Func<PKM, SaveFile, bool> Criteria, Func<SaveFile, bool> Usable) : BoxManipBase(Type, Usable)
public sealed class BoxManipClearComplex(BoxManipType Type, Func<PKM, SaveFile, bool> criteria, Func<SaveFile, bool> Usable) : BoxManipBase(Type, Usable)
{
public BoxManipClearComplex(BoxManipType Type, Func<PKM, SaveFile, bool> Criteria) : this(Type, Criteria, _ => true) { }
@ -18,6 +18,6 @@ public override int Execute(SaveFile sav, BoxManipParam param)
var (start, stop, reverse) = param;
return sav.ClearBoxes(start, stop, Method);
bool Method(PKM p) => reverse ^ Criteria(p, sav);
bool Method(PKM p) => reverse ^ criteria(p, sav);
}
}

View File

@ -7,7 +7,7 @@ namespace PKHeX.Core;
/// Clears contents of boxes by deleting all but the first duplicate detected.
/// </summary>
/// <typeparam name="T">Base type of the "is duplicate" hash for the duplicate detection.</typeparam>
public sealed record BoxManipClearDuplicate<T> : BoxManipBase
public sealed class BoxManipClearDuplicate<T> : BoxManipBase
{
private readonly HashSet<T> HashSet = [];
private readonly Func<PKM, bool> Criteria;

View File

@ -5,8 +5,8 @@ namespace PKHeX.Core;
/// <summary>
/// Modifies contents of boxes by using an <see cref="Action"/> to change data.
/// </summary>
public sealed record BoxManipModify(BoxManipType Type, Action<PKM> Action, Func<SaveFile, bool> Usable)
: BoxManipBase(Type, Usable)
public sealed class BoxManipModify(BoxManipType type, Action<PKM> Action, Func<SaveFile, bool> Usable)
: BoxManipBase(type, Usable)
{
public BoxManipModify(BoxManipType type, Action<PKM> Action) : this(type, Action, _ => true) { }

View File

@ -5,7 +5,7 @@ namespace PKHeX.Core;
/// <summary>
/// Modifies contents of boxes by using an <see cref="Action"/> (referencing a Save File) to change data.
/// </summary>
public sealed record BoxManipModifyComplex(BoxManipType Type, Action<PKM, SaveFile> Action, Func<SaveFile, bool> Usable)
public sealed class BoxManipModifyComplex(BoxManipType Type, Action<PKM, SaveFile> Action, Func<SaveFile, bool> Usable)
: BoxManipBase(Type, Usable)
{
public BoxManipModifyComplex(BoxManipType Type, Action<PKM, SaveFile> Action) : this(Type, Action, _ => true) { }

View File

@ -6,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// Sorts contents of boxes by using a Sorter to determine the order.
/// </summary>
public sealed record BoxManipSort(BoxManipType Type, Func<IEnumerable<PKM>, IEnumerable<PKM>> Sorter, Func<SaveFile, bool> Usable) : BoxManipBase(Type, Usable)
public sealed class BoxManipSort(BoxManipType Type, Func<IEnumerable<PKM>, IEnumerable<PKM>> Sorter, Func<SaveFile, bool> Usable) : BoxManipBase(Type, Usable)
{
public BoxManipSort(BoxManipType Type, Func<IEnumerable<PKM>, IEnumerable<PKM>> Sorter) : this(Type, Sorter, _ => true) { }

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
namespace PKHeX.Core;
@ -6,7 +6,7 @@ namespace PKHeX.Core;
/// <summary>
/// Sorts contents of boxes by using a <see cref="Sorter"/> (referencing a Save File) to determine the order.
/// </summary>
public sealed record BoxManipSortComplex : BoxManipBase
public sealed class BoxManipSortComplex : BoxManipBase
{
private readonly Func<IEnumerable<PKM>, SaveFile, int, IEnumerable<PKM>> Sorter;
public BoxManipSortComplex(BoxManipType type, Func<IEnumerable<PKM>, SaveFile, IEnumerable<PKM>> sorter) : this(type, sorter, _ => true) { }

View File

@ -47,10 +47,10 @@ public sealed class FakeSaveFile : SaveFile
protected override void SetChecksums() { }
public override GameVersion Version { get => GameVersion.R; set { } }
public override Type PKMType => typeof(PK3);
protected override PK3 GetPKM(Memory<byte> data) => BlankPKM;
protected override void DecryptPKM(Span<byte> data) { }
protected override PK3 GetPKM(byte[] data) => BlankPKM;
protected override byte[] DecryptPKM(byte[] data) => data;
public override PK3 BlankPKM => new();
public override EntityContext Context => EntityContext.Gen3;
public override int SIZE_STORED => 0;
public override int SIZE_PARTY => 0;
protected override int SIZE_STORED => 0;
protected override int SIZE_PARTY => 0;
}

View File

@ -86,9 +86,7 @@ public static int Export(SaveFile sav, string destPath, IFileNamer<PKM> namer, B
int count = GetSlotCountForBox(boxSlotCount, box, total);
int ctr = 0;
// Export each slot in the box with party stats, to be nice to any external analysis.
bool isPartyFormat = sav.SIZE_BOXSLOT == sav.SIZE_PARTY;
Span<byte> data = stackalloc byte[sav.SIZE_PARTY];
// Export each slot in the box.
for (int slot = 0; slot < count; slot++)
{
var pk = sav.GetBoxSlotAtIndex(box, slot);
@ -100,13 +98,7 @@ public static int Export(SaveFile sav, string destPath, IFileNamer<PKM> namer, B
var fileName = GetFileName(pk, settings.FileIndexPrefix, namer, box, slot, boxSlotCount);
var fn = Path.Combine(destPath, fileName);
// Assume that all PKM read for the loop all are the same shape; the if-else will always travel one path.
// We don't have to worry about lingering party data from a previous loop iteration.
if (!isPartyFormat)
pk.ForcePartyData(); // Rather than export all-zero party stats, calculate what they would be.
pk.WriteDecryptedDataParty(data);
File.WriteAllBytes(fn, data);
File.WriteAllBytes(fn, pk.DecryptedPartyData);
ctr++;
}
return ctr;

View File

@ -61,11 +61,11 @@ private static List<SlotInfoMisc> GetExtraSlots2(SAV2 sav)
private static List<SlotInfoMisc> GetExtraSlots3(SAV3 sav)
{
if (sav is not SAV3FRLG frlg)
if (sav is not SAV3FRLG)
return None;
return
[
new(frlg.LargeBlock.SingleDaycareRoute5, 0) {Type = StorageSlotType.Daycare},
new(sav.LargeBuffer[0x3C98..], 0) {Type = StorageSlotType.Daycare},
];
}
@ -254,7 +254,7 @@ private static List<SlotInfoMisc> GetExtraSlots9a(SAV9ZA sav)
{
const int size = 0x1F0;
var ofs = (i * size) + 8;
var entry = shinyCache.Raw.Slice(ofs, PokeCrypto.SIZE_8PARTY);
var entry = shinyCache.Raw.Slice(ofs, PokeCrypto.SIZE_9PARTY);
if (EntityDetection.IsPresent(entry.Span))
list.Add(new(entry, i, true) { Type = StorageSlotType.Shiny, HideLegality = true }); // no OT info
else
@ -266,7 +266,7 @@ private static List<SlotInfoMisc> GetExtraSlots9a(SAV9ZA sav)
{
const int size = 0x1A8;
var ofs = (i * size) + 8;
var entry = giveAway.Raw.Slice(ofs, PokeCrypto.SIZE_8PARTY);
var entry = giveAway.Raw.Slice(ofs, PokeCrypto.SIZE_9PARTY);
if (EntityDetection.IsPresent(entry.Span))
list.Add(new(entry, i, true, Mutable: true) { Type = StorageSlotType.Scripted });
else

View File

@ -55,7 +55,7 @@ public static class NatureUtil
/// Checks if the provided <see cref="value"/> is a valid stored <see cref="Nature"/> value.
/// </summary>
/// <returns>True if value is an actual nature.</returns>
public bool IsFixed => value != Nature.Random;
public bool IsFixed() => value < Nature.Random;
/// <summary>
/// Checks if the provided <see cref="value"/> is a possible mint nature.
@ -63,12 +63,12 @@ public static class NatureUtil
/// <remarks>
/// The only valid mint natures are those which have a stat amp applied, or neutral nature being Serious.
/// </remarks>
public bool IsMint => (value.IsFixed && (byte)value % 6 != 0) || value == Nature.Serious;
public bool IsMint() => (value.IsFixed() && (byte)value % 6 != 0) || value == Nature.Serious;
/// <summary>
/// Checks if the provided <see cref="value"/> is a neutral nature which has no stat amps applied.
/// </summary>
public bool IsNeutral => value.IsFixed && (byte)value % 6 == 0;
public bool IsNeutral() => value.IsFixed() && (byte)value % 6 == 0;
/// <summary>
/// Converts the provided <see cref="value"/> to a neutral nature.

View File

@ -100,7 +100,6 @@ public GameDataSource(GameStrings s)
/// <remarks>Most recent games are at the top, loosely following Generation groups.</remarks>
private static ReadOnlySpan<byte> OrderedVersionArray =>
[
53, // Champions
52, // 9 Z-A
50, 51, // 9 S/V
47, // 8 PLA

View File

@ -448,10 +448,6 @@ private void SanitizeMetLocations()
Gen6.Met4[35] += " (-)";
Gen7.Met4[38] += " (-)";
Gen7b.Met4[27] += " (-)";
// only a duplicate in LATAM Spanish
if (Language is SpanishL)
Gen5.Met4[47] += " (-)";
}
if (Language is Korean)

View File

@ -146,7 +146,7 @@ private EntityContext GetContextInternal()
BD or SP => EntityContext.Gen8b,
SW or SH => EntityContext.Gen8,
SL or VL => EntityContext.Gen9,
ZA or CP => EntityContext.Gen9a,
ZA => EntityContext.Gen9a,
_ => 0
};
@ -248,7 +248,7 @@ public bool Contains(GameVersion g2)
Gen8 => version1 is SW or SH or BD or SP or SWSH or BDSP or PLA,
SV => version1 is SL or VL,
Gen9 => version1 is SL or VL or SV or ZA or CP,
Gen9 => version1 is SL or VL or SV or ZA,
_ => false,
};

View File

@ -6,6 +6,8 @@ namespace PKHeX.Core;
public sealed class PlayerBag3E : PlayerBag, IPlayerBag3
{
private const int BaseOffset = 0x0498;
public override IReadOnlyList<InventoryPouch3> Pouches { get; } = GetPouches(ItemStorage3E.Instance);
public override ItemStorage3E Info => ItemStorage3E.Instance;
@ -19,7 +21,7 @@ public sealed class PlayerBag3E : PlayerBag, IPlayerBag3
new(0x000, 50, 999, info, PCItems),
];
public PlayerBag3E(SAV3E sav) : this(sav.LargeBlock.Inventory, sav.SmallBlock.SecurityKey) { }
public PlayerBag3E(SAV3E sav) : this(sav.Large[BaseOffset..], sav.SecurityKey) { }
public PlayerBag3E(ReadOnlySpan<byte> data, uint security)
{
UpdateSecurityKey(security);
@ -27,7 +29,7 @@ public PlayerBag3E(ReadOnlySpan<byte> data, uint security)
}
public override void CopyTo(SaveFile sav) => CopyTo((SAV3E)sav);
public void CopyTo(SAV3E sav) => CopyTo(sav.LargeBlock.Inventory);
public void CopyTo(SAV3E sav) => CopyTo(sav.Large[BaseOffset..]);
public void CopyTo(Span<byte> data) => Pouches.SaveAll(data);
public override int GetMaxCount(InventoryType type, int itemIndex)

View File

@ -6,6 +6,7 @@ namespace PKHeX.Core;
public sealed class PlayerBag3FRLG(bool VC) : PlayerBag, IPlayerBag3
{
private const int BaseOffset = 0x0298;
public override IItemStorage Info => GetInfo(VC);
private static IItemStorage GetInfo(bool vc) => vc ? ItemStorage3FRLG_VC.Instance : ItemStorage3FRLG.Instance;
public override IReadOnlyList<InventoryPouch3> Pouches { get; } = GetPouches(GetInfo(VC));
@ -20,7 +21,7 @@ public sealed class PlayerBag3FRLG(bool VC) : PlayerBag, IPlayerBag3
new(0x000, 30, 999, info, PCItems),
];
public PlayerBag3FRLG(SAV3FRLG sav) : this(sav.LargeBlock.Inventory, sav.SmallBlock.SecurityKey, sav.IsVirtualConsole) { }
public PlayerBag3FRLG(SAV3FRLG sav) : this(sav.Large[BaseOffset..], sav.SecurityKey, sav.IsVirtualConsole) { }
public PlayerBag3FRLG(ReadOnlySpan<byte> data, uint security, bool vc) : this(vc)
{
UpdateSecurityKey(security);
@ -28,7 +29,7 @@ public PlayerBag3FRLG(ReadOnlySpan<byte> data, uint security, bool vc) : this(vc
}
public override void CopyTo(SaveFile sav) => CopyTo((SAV3FRLG)sav);
public void CopyTo(SAV3FRLG sav) => CopyTo(sav.LargeBlock.Inventory);
public void CopyTo(SAV3FRLG sav) => CopyTo(sav.Large[BaseOffset..]);
public void CopyTo(Span<byte> data) => Pouches.SaveAll(data);
public override int GetMaxCount(InventoryType type, int itemIndex)

View File

@ -6,6 +6,8 @@ namespace PKHeX.Core;
public sealed class PlayerBag3RS : PlayerBag
{
private const int BaseOffset = 0x0498;
public override IReadOnlyList<InventoryPouch3> Pouches { get; } = GetPouches(ItemStorage3RS.Instance);
public override ItemStorage3RS Info => ItemStorage3RS.Instance;
@ -19,11 +21,11 @@ public sealed class PlayerBag3RS : PlayerBag
new(0x000, 50, 999, info, PCItems),
];
public PlayerBag3RS(SAV3RS sav) : this(sav.LargeBlock.Inventory) { }
public PlayerBag3RS(SAV3RS sav) : this(sav.Large[BaseOffset..]) { }
public PlayerBag3RS(ReadOnlySpan<byte> data) => Pouches.LoadAll(data);
public override void CopyTo(SaveFile sav) => CopyTo((SAV3RS)sav);
public void CopyTo(SAV3RS sav) => CopyTo(sav.LargeBlock.Inventory);
public void CopyTo(SAV3RS sav) => CopyTo(sav.Large[BaseOffset..]);
public void CopyTo(Span<byte> data) => Pouches.SaveAll(data);
public override int GetMaxCount(InventoryType type, int itemIndex)

View File

@ -13,9 +13,9 @@ public sealed class ItemStorage3E : IItemStorage
public static ReadOnlySpan<ushort> Key =>
[
// R/S
259, 260, 261, 262, 263, 264, 265, 266, 268, 269, 270, 271, 272, 273, 274, 275, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
259, 260, 261, 262, 263, 264, 265, 266, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
// FR/LG
370, 371, 372,
349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374,
// E
375, 376,
];

View File

@ -13,7 +13,7 @@ public sealed class ItemStorage3FRLG : IItemStorage
public static ReadOnlySpan<ushort> Key =>
[
// R/S
260, 261, 262, 263, 264, 265,
259, 260, 261, 262, 263, 264, 265, 266, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
// FR/LG
349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374,
];

View File

@ -168,9 +168,7 @@ public static InventoryType GetInventoryPouch(ushort itemIndex)
}
public static bool IsMegaStone(ushort item) => MegaStones.Contains(item);
public static bool IsUniqueHeldItem(ushort item) => IsMegaStone(item) || IsOrb(item);
public static bool IsOrb(ushort item) => item is (0534 or 0535); // Primal Orbs
public static bool IsUniqueHeldItem(ushort item) => IsMegaStone(item) || item is (0534 or 0535); // Primal Orbs
public static ushort[] GetAllUniqueHeldItems() => [..MegaStones, 0534, 0535];
/// <summary>

View File

@ -32,20 +32,6 @@ private static void AnalyzeInventory(BulkAnalysis input, SAV9ZA za)
continue;
var item = items.GetItem(stone);
if (ItemStorage9ZA.IsOrb(stone))
{
// Handled via Other items (give, unique), not Mega Stones (loan).
if (item.IsNew) // Not acquired/given by the save file, thus not able to be held.
input.AddLine(slot, Identifier, BulkCheckResult.NoIndex, BulkHeldItemInventoryNotAcquired_0, stone);
else if (seenStones.TryGetValue(stone, out var otherIndex)) // Already given to another slot.
input.AddLine(slot, input.AllData[otherIndex], Identifier, i, index2: otherIndex, BulkHeldItemInventoryMultipleSlots_0, stone);
else // First time seeing this item, all good.
seenStones[stone] = i;
continue;
}
// Mega Stone
if (item.Count == 0) // Not acquired by the save file, thus not able to be held.
input.AddLine(slot, Identifier, BulkCheckResult.NoIndex, BulkHeldItemInventoryNotAcquired_0, stone);
else if (!item.IsHeld) // Not marked as held, so it's still "in the bag" (not given).

View File

@ -95,26 +95,15 @@ internal static class Encounters2
new(202, 15, C) { Location = 016 }, // Wobbuffet @ Goldenrod City (Game Corner)
];
private static IndividualValueSet AllZero => new(00, 00, 00, 00, 00, 00);
private static IndividualValueSet Shiny2 => new(00, 02, 10, 10, 10, 10);
public static readonly EncounterStatic2[] StaticOddEggC =
[
new(172, 05, C) { IsEgg = true, Gender = 1, IVs = AllZero, Shiny = Shiny.Never, Moves = new((int)Move.ThunderShock,(int)Move.Charm, (int)Move.DizzyPunch)}, // Pichu
new(173, 05, C) { IsEgg = true, Gender = 1, IVs = AllZero, Shiny = Shiny.Never, Moves = new((int)Move.Pound, (int)Move.Charm, (int)Move.DizzyPunch)}, // Cleffa
new(174, 05, C) { IsEgg = true, Gender = 1, IVs = AllZero, Shiny = Shiny.Never, Moves = new((int)Move.Sing, (int)Move.Charm, (int)Move.DizzyPunch)}, // Igglybuff
new(236, 05, C) { IsEgg = true, Gender = 0, IVs = AllZero, Shiny = Shiny.Never, Moves = new((int)Move.Tackle, (int)Move.DizzyPunch)}, // Tyrogue
new(238, 05, C) { IsEgg = true, Gender = 1, IVs = AllZero, Shiny = Shiny.Never, Moves = new((int)Move.Pound, (int)Move.Lick, (int)Move.DizzyPunch)}, // Smoochum
new(239, 05, C) { IsEgg = true, Gender = 1, IVs = AllZero, Shiny = Shiny.Never, Moves = new((int)Move.QuickAttack, (int)Move.Leer, (int)Move.DizzyPunch)}, // Elekid
new(240, 05, C) { IsEgg = true, Gender = 1, IVs = AllZero, Shiny = Shiny.Never, Moves = new((int)Move.Ember, (int)Move.DizzyPunch)}, // Magby
new(172, 05, C) { IsEgg = true, Gender = 1, IVs = Shiny2, Shiny = Shiny.Always, Moves = new((int)Move.ThunderShock,(int)Move.Charm, (int)Move.DizzyPunch)}, // Shiny Pichu
new(173, 05, C) { IsEgg = true, Gender = 1, IVs = Shiny2, Shiny = Shiny.Always, Moves = new((int)Move.Pound, (int)Move.Charm, (int)Move.DizzyPunch)}, // Shiny Cleffa
new(174, 05, C) { IsEgg = true, Gender = 1, IVs = Shiny2, Shiny = Shiny.Always, Moves = new((int)Move.Sing, (int)Move.Charm, (int)Move.DizzyPunch)}, // Shiny Igglybuff
new(236, 05, C) { IsEgg = true, Gender = 0, IVs = Shiny2, Shiny = Shiny.Always, Moves = new((int)Move.Tackle, (int)Move.DizzyPunch)}, // Shiny Tyrogue
new(238, 05, C) { IsEgg = true, Gender = 1, IVs = Shiny2, Shiny = Shiny.Always, Moves = new((int)Move.Pound, (int)Move.Lick, (int)Move.DizzyPunch)}, // Shiny Smoochum
new(239, 05, C) { IsEgg = true, Gender = 1, IVs = Shiny2, Shiny = Shiny.Always, Moves = new((int)Move.QuickAttack, (int)Move.Leer, (int)Move.DizzyPunch)}, // Shiny Elekid
new(240, 05, C) { IsEgg = true, Gender = 1, IVs = Shiny2, Shiny = Shiny.Always, Moves = new((int)Move.Ember, (int)Move.DizzyPunch)}, // Shiny Magby
new(172, 05, C) { IsEgg = true, Moves = new((int)Move.ThunderShock,(int)Move.Charm, (int)Move.DizzyPunch)}, // Pichu
new(173, 05, C) { IsEgg = true, Moves = new((int)Move.Pound, (int)Move.Charm, (int)Move.DizzyPunch)}, // Cleffa
new(174, 05, C) { IsEgg = true, Moves = new((int)Move.Sing, (int)Move.Charm, (int)Move.DizzyPunch)}, // Igglybuff
new(236, 05, C) { IsEgg = true, Moves = new((int)Move.Tackle, (int)Move.DizzyPunch)}, // Tyrogue
new(238, 05, C) { IsEgg = true, Moves = new((int)Move.Pound, (int)Move.Lick, (int)Move.DizzyPunch)}, // Smoochum
new(239, 05, C) { IsEgg = true, Moves = new((int)Move.QuickAttack, (int)Move.Leer, (int)Move.DizzyPunch)}, // Elekid
new(240, 05, C) { IsEgg = true, Moves = new((int)Move.Ember, (int)Move.DizzyPunch)}, // Magby
];
internal static readonly EncounterStatic2 CelebiVC = new(251, 30, C) { Location = 014 }; // Celebi @ Ilex Forest (VC)
@ -132,7 +121,7 @@ internal static class Encounters2
new(TradeNames, 7, 178, 15, 15616) { Gender = 0, IVs = new(08, 09, 06, 08, 06, 06) }, // Xatu @ Pewter City for Haunter [wild]
new(TradeNames, 8, 082, 05, 50082) { Gender = 2, IVs = new(08, 09, 06, 06, 06, 06) }, // Magneton @ Power Plant for Dugtrio [traded for Lickitung]
new(TradeNames, 9, 021, 10, 01001) { Shiny = Shiny.Random }, // Spearow @ Goldenrod City for free
new(TradeNames, 10, 213, 15, 00518) { Shiny = Shiny.Random }, // Shuckle @ Cianwood City for free
new(TradeNames, 9, 021, 10, 01001), // Spearow @ Goldenrod City for free
new(TradeNames, 10, 213, 15, 00518), // Shuckle @ Cianwood City for free
];
}

View File

@ -61,7 +61,6 @@ private static EncounterArea3[] GetSwarm([ConstantExpected] string resource, [Le
// Stationary
new(352, 30, RSE) { Location = 034 }, // Kecleon @ Route 119
new(352, 30, RSE) { Location = 035 }, // Kecleon @ Route 120
new(100, 25, RSE) { Location = 062 }, // Voltorb @ New Mauville
// Stationary Lengendary
new(377, 40, RSE) { Location = 082 }, // Regirock @ Desert Ruins

View File

@ -255,9 +255,5 @@ public static class EncounterServerDate
{0102, new(2025, 10, 23, 2026, 02, 01, +2)}, // Slowpoke PokéCenter Gift
{0101, new(2025, 10, 31, 2027, 02, 01)}, // PokéCenter Audino Birthday Gift
{1607, new(2025, 12, 09, 2026, 01, 20)}, // Alpha Charizard
{9031, new(2026, 04, 02)}, // Alpha Chikorita
{9032, new(2026, 04, 02)}, // Alpha Tepig
{9033, new(2026, 04, 02)}, // Alpha Totodile
};
}

View File

@ -112,7 +112,7 @@ public EncounterCriteria()
/// Determines whether a specific Nature is specified in the criteria or if complex nature mutations are allowed.
/// </summary>
/// <returns>><see langword="true"/> if a Nature is specified or complex nature mutations are allowed; otherwise, <see langword="false"/>.</returns>
public bool IsSpecifiedNature() => Nature.IsFixed || Mutations.IsComplexNature();
public bool IsSpecifiedNature() => Nature != Nature.Random || Mutations.IsComplexNature();
/// <summary>
/// Determines whether a level range is specified in the criteria.
@ -126,12 +126,6 @@ public EncounterCriteria()
/// <returns>><see langword="true"/> if an Ability is specified; otherwise, <see langword="false"/>.</returns>
public bool IsSpecifiedAbility() => Ability != Any12H;
/// <summary>
/// Determines whether the shiny value is explicitly specified rather than set to random.
/// </summary>
/// <returns>><see langword="true"/> if a Shiny is specified; otherwise, <see langword="false"/>.</returns>
public bool IsSpecifiedShiny() => Shiny != Shiny.Random;
/// <summary>
/// Determines whether all IVs are specified in the criteria.
/// </summary>
@ -189,20 +183,6 @@ public int GetCountSpecifiedIVs() => Convert.ToInt32(IV_HP != RandomIV)
_ => throw new ArgumentOutOfRangeException(nameof(ability), ability, null),
};
/// <summary>
/// Determines whether the specified shiny properties satisfy the shiny criteria based on the current <see cref="Shiny"/> setting.
/// </summary>
/// <returns>><see langword="true"/> if the index satisfies the shiny criteria; otherwise, <see langword="false"/>.</returns>
public bool IsSatisfiedShiny(uint xor, uint cmp) => Shiny switch
{
Shiny.Random => true,
Shiny.Never => xor > cmp, // not shiny
Shiny.AlwaysSquare => xor == 0, // square shiny
Shiny.AlwaysStar => xor < cmp && xor != 0, // star shiny
Shiny.Always => xor < cmp, // shiny
_ => false, // shouldn't be set
};
/// <summary>
/// Determines whether the specified Nature satisfies the criteria.
/// </summary>
@ -211,7 +191,7 @@ public int GetCountSpecifiedIVs() => Convert.ToInt32(IV_HP != RandomIV)
public bool IsSatisfiedNature(Nature nature)
{
if (Mutations.HasFlag(AllowOnlyNeutralNature))
return nature.IsNeutral;
return nature.IsNeutral();
if (Nature == Nature.Random)
return true;
return nature == Nature || Mutations.HasFlag(CanMintNature);
@ -320,7 +300,7 @@ public Nature GetNature(Nature encValue)
/// </summary>
public Nature GetNature()
{
if (Nature.IsFixed)
if (Nature != Nature.Random)
return Nature;
var result = (Nature)Util.Rand.Next(25);
if (Mutations.HasFlag(AllowOnlyNeutralNature))

View File

@ -64,8 +64,7 @@ public static class EncounterGenerator
9 => version switch
{
GameVersion.ZA => EncounterGenerator9a.Instance,
GameVersion.SL or GameVersion.VL => EncounterGenerator9.Instance,
_ => EncounterGeneratorDummy.Instance, // Champions
_ => EncounterGenerator9.Instance,
},
_ => EncounterGeneratorDummy.Instance,
};

View File

@ -12,8 +12,6 @@ public enum PogoType : byte
// Pokémon captured in the wild.
Wild,
WildLevel20,
WildLevel25,
// Pokémon hatched from Eggs.
Egg,
@ -139,8 +137,6 @@ public static class PogoTypeExtensions
public byte LevelMin => encounterType switch
{
Wild => 1,
WildLevel20 => 20,
WildLevel25 => 25,
Egg => 1,
Egg12km => 8,
Raid => 20,
@ -208,8 +204,6 @@ public static class PogoTypeExtensions
public int MinimumIV => encounterType switch
{
Wild => 0,
WildLevel20 => 0,
WildLevel25 => 0,
RaidMythical => 10,
RaidShadowMythical => 8,
RaidShadowMythicalGOWA => 8,
@ -330,6 +324,6 @@ public bool IsBallValid(Ball ball)
_ => Ball.None, // Poké, Great, Ultra
};
public bool IsSpecialResearch => encounterType is SpecialResearch or >= SpecialMythical and < TimedMythical;
public bool IsSpecialResearch => encounterType is >= SpecialMythical and < TimedMythical;
}
}

View File

@ -48,25 +48,27 @@ public EncounterTrade1(ReadOnlySpan<string[]> names, byte index, ushort species,
LevelMinGSC = levelMinGSC;
}
/// <summary>
/// When transferred to Gen7+ via Bank, the nickname for a Japanese Dugtrio in Hiragana changes from "ぐリお" to "ぐりお".
/// </summary>
public const string HiraganaDugtrio7 = "ぐりお";
private bool IsNicknameValid(PKM pk, ReadOnlySpan<char> nick)
{
// Nicknames can be from any of the languages it can trade between.
if (!pk.Japanese)
return DetectLanguage(nick, Nicknames.Span, 2) >= 2;
if (pk.Format <= 2)
return IsNicknameAnyMatch(nick);
// Converted Japanese strings 1/2->7 can mutate from an exact match.
// Special consideration for Hiragana strings that are transferred: only Dugtrio's nickname changes when transferred to Gen7+.
if (pk.Format > 2 && Version == GameVersion.YW && Species == (int)Core.Species.Dugtrio)
return nick is HiraganaDugtrio7;
// Otherwise, must match the Japanese nickname exactly.
return Nicknames.Span[(int)LanguageID.Japanese].SequenceEqual(nick);
// Converted string 1/2->7 to language specific value
// Nicknames can be from any of the languages it can trade between.
int lang = pk.Language;
if (lang == 1)
{
// Special consideration for Hiragana strings that are transferred
if (Version == GameVersion.YW && Species == (int)Core.Species.Dugtrio)
return nick is "ぐりお";
return nick.SequenceEqual(Nicknames.Span[(int)LanguageID.Japanese]);
}
return GetNicknameIndex(nick) >= 2;
}
private bool IsNicknameAnyMatch(ReadOnlySpan<char> current) => GetNicknameIndex(current) >= 0;
private static bool IsTrainerNameValid(PKM pk)
{
if (pk.Format <= 2)
@ -81,12 +83,12 @@ private static bool IsTrainerNameValid(PKM pk)
return trainer.SequenceEqual(expect);
}
private static int DetectLanguage(ReadOnlySpan<char> name, ReadOnlySpan<string> arr, int start = 1)
private int GetNicknameIndex(ReadOnlySpan<char> nickname) => GetIndex(nickname, Nicknames.Span);
private static int GetIndex(ReadOnlySpan<char> name, ReadOnlySpan<string> arr)
{
for (int i = start; i < arr.Length; i++)
for (int i = 0; i < arr.Length; i++)
{
if (i == (int)LanguageID.UNUSED_6)
continue;
if (name.SequenceEqual(arr[i]))
return i;
}

View File

@ -16,6 +16,7 @@ public sealed record EncounterTrade2 : IEncounterable, IEncounterMatch, IEncount
public bool IsEgg => false;
public Ball FixedBall => Ball.Poke;
public AbilityPermission Ability => AbilityPermission.OnlyHidden;
public Shiny Shiny => Shiny.Random;
public bool IsShiny => false;
public ushort EggLocation => 0;
public bool IsFixedTrainer => true;
@ -32,7 +33,6 @@ public sealed record EncounterTrade2 : IEncounterable, IEncounterMatch, IEncount
private readonly ReadOnlyMemory<string> TrainerNames;
private readonly ReadOnlyMemory<string> Nicknames;
public Shiny Shiny { get; init; } = Shiny.Never;
public byte Gender { get; init; }
public byte OTGender { get; init; }
public IndividualValueSet IVs { get; init; }
@ -168,8 +168,8 @@ private int DetectLanguage(PKM pk, ReadOnlySpan<char> trainer, ReadOnlySpan<char
return -1;
return (int)LanguageID.Korean;
}
// Skip languages that are not-transferable to International games.
for (int i = 2; i < (int)LanguageID.Korean; i++)
for (int i = 2; i < TrainerNames.Length; i++)
{
if (i == (int)LanguageID.UNUSED_6)
continue;

View File

@ -79,14 +79,14 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
// Get a random PID that matches gender/nature/ability criteria
var pi = PersonalTable.E[Species];
var gr = pi.Gender;
var pid = GetRandomPID(criteria, gr, tr.ID32);
var pid = GetRandomPID(criteria, gr);
pk.PID = pid;
pk.RefreshAbility((int)(pid % 2));
return pk;
}
private uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id32)
private uint GetRandomPID(in EncounterCriteria criteria, byte gr)
{
var seed = Util.Rand32();
while (true)
@ -109,10 +109,6 @@ private uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id32)
if (!Daycare3.IsValidProcPID(pid, Version))
continue; // 0-value PID is invalid
var shiny = ShinyUtil.GetIsShiny3(id32, pid);
if (criteria.Shiny.IsShiny() != shiny)
continue;
return pid;
}
}

View File

@ -84,7 +84,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
// Get a random PID that matches gender/nature/ability criteria
var pi = PersonalTable.HGSS[Species];
var gr = pi.Gender;
var pid = GetRandomPID(criteria, gr, tr.ID32, out var gender);
var pid = GetRandomPID(criteria, gr, out var gender);
pk.PID = pid;
pk.Gender = gender;
pk.RefreshAbility((int)(pid & 1));
@ -92,7 +92,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
return pk;
}
private uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id32, out byte gender)
private uint GetRandomPID(in EncounterCriteria criteria, byte gr, out byte gender)
{
var seed = Util.Rand32();
while (true)
@ -115,10 +115,6 @@ private uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id32, out
// PID is rolled forward upon picking up the egg.
// Not worth skipping 0-value PIDs. Too rare to be worth trying again, since it can be a valid PID.
var shiny = ShinyUtil.GetIsShiny3(id32, pid);
if (criteria.Shiny.IsShiny() != shiny)
continue;
return pid;
}
}

View File

@ -72,7 +72,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
var pi = PersonalTable.B2W2[Species];
var gr = pi.Gender;
var ability = criteria.GetAbilityFromNumber(Ability);
var pid = GetRandomPID(criteria, gr, tr.ID32, out var gender);
var pid = GetRandomPID(criteria, gr, out var gender);
pid = (pid & 0xFFFEFFFFu) | (uint)(ability & 1) << 16; // 0x00000000 or 0x00010000
pk.PID = pid;
pk.Gender = gender;
@ -81,7 +81,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
return pk;
}
private static uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id32, out byte gender)
private static uint GetRandomPID(in EncounterCriteria criteria, byte gr, out byte gender)
{
var seed = Util.Rand32();
while (true)
@ -91,9 +91,6 @@ private static uint GetRandomPID(in EncounterCriteria criteria, byte gr, uint id
gender = EntityGender.GetFromPIDAndRatio(pid, gr);
if (criteria.IsSpecifiedGender() && !criteria.IsSatisfiedGender(gender))
continue;
var shiny = ShinyUtil.GetIsShiny3(id32, pid);
if (criteria.Shiny.IsShiny() != shiny)
continue;
return pid;
}
}

View File

@ -155,7 +155,7 @@ private bool IsMatchNatureGenderShiny(PKM pk)
return false;
if (Gender != FixedGenderUtil.GenderRandom && Gender != pk.Gender)
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
return true;
}

View File

@ -153,7 +153,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
return false;
if (IVs.IsSpecified && !Legal.GetIsFixedIVSequenceValidSkipRand(IVs, pk))
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
if (FlawlessIVCount != 0 && pk.FlawlessIVCount < FlawlessIVCount)
return false;

View File

@ -167,7 +167,7 @@ private bool IsMatchNatureGenderShiny(PKM pk)
return false;
if (Gender != pk.Gender)
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
return true;
}

View File

@ -145,7 +145,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
return false;
if (IVs.IsSpecified && !Legal.GetIsFixedIVSequenceValidSkipRand(IVs, pk))
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
if (FlawlessIVCount != 0 && pk.FlawlessIVCount < FlawlessIVCount)
return false;

View File

@ -166,7 +166,7 @@ private bool IsMatchNatureGenderShiny(PKM pk)
return false;
if (Gender != pk.Gender)
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
return true;
}

View File

@ -104,9 +104,6 @@ public PK8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.SetMoves(Moves);
else
EncounterUtil.SetEncounterMoves(pk, version, Level);
if (Relearn.HasMoves)
pk.SetRelearnMoves(Relearn);
pk.ResetPartyStats();
return pk;

View File

@ -122,6 +122,8 @@ protected virtual void SetPINGA(PK8 pk, in EncounterCriteria criteria, PersonalI
}
FinishCorrelation(pk, seed);
if (criteria.IsSpecifiedNature() && criteria.Nature != pk.Nature && criteria.Nature.IsMint())
pk.StatNature = criteria.Nature;
}
protected GenerateParam8 GetParam() => GetParam(Info);

View File

@ -57,10 +57,6 @@ protected override void SetPINGA(PK8 pk, in EncounterCriteria criteria, Personal
{
Span<int> iv = stackalloc int[6];
// Honor a shiny request only at the end; generate as never-shiny to avoid shiny PID rejection in main RNG method.
var isShinyRequested = criteria.Shiny.IsShiny();
var iterCriteria = criteria with { Shiny = ShinyMethod };
int ctr = 0;
var rand = new Xoroshiro128Plus(Util.Rand.Rand64());
var param = GetParam(pi);
@ -68,22 +64,23 @@ protected override void SetPINGA(PK8 pk, in EncounterCriteria criteria, Personal
const int max = 100_000;
do
{
if (TryApply(pk, seed = rand.Next(), iv, param, iterCriteria))
if (TryApply(pk, seed = rand.Next(), iv, param, criteria))
break;
} while (++ctr < max);
if (ctr == max) // fail
{
iterCriteria = iterCriteria.WithoutIVs();
if (!TryApply(pk, seed = rand.Next(), iv, param, iterCriteria))
if (!TryApply(pk, seed = rand.Next(), iv, param, criteria.WithoutIVs()))
{
iterCriteria = EncounterCriteria.Unrestricted with { Shiny = ShinyMethod };
while (!TryApply(pk, seed = rand.Next(), iv, param, iterCriteria)) { }
var tmp = EncounterCriteria.Unrestricted with { Shiny = ShinyMethod };
while (!TryApply(pk, seed = rand.Next(), iv, param, tmp)) { }
}
}
FinishCorrelation(pk, seed);
if (isShinyRequested)
if (criteria.IsSpecifiedNature() && criteria.Nature != pk.Nature && criteria.Nature.IsMint())
pk.StatNature = criteria.Nature;
if (criteria.Shiny.IsShiny())
pk.PID = ShinyUtil.GetShinyPID(pk.TID16, pk.SID16, pk.PID, ShinyXor);
}

View File

@ -209,7 +209,7 @@ private bool IsMatchNatureGenderShiny(PKM pk)
{
if (!Shiny.IsValid(pk))
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
if (Gender != FixedGenderUtil.GenderRandom && pk.Gender != Gender)
return false;

View File

@ -154,7 +154,7 @@ private void SetPINGA(PK9 pk, in EncounterCriteria criteria, PersonalInfo9SV pi)
if (Gender != FixedGenderUtil.GenderRandom)
pk.Gender = Gender;
if (Nature.IsFixed)
if (Nature != Nature.Random)
pk.Nature = pk.StatNature = Nature;
}
#endregion
@ -178,7 +178,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
return false;
if (TeraType != GemType.Random && pk is ITeraType t && !Tera9RNG.IsMatchTeraType(TeraType, Species, Form, (byte)t.TeraTypeOriginal))
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
return true;

View File

@ -158,7 +158,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
return false;
if (FlawlessIVCount != 0 && pk.FlawlessIVCount < FlawlessIVCount)
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
if (pk is IAlphaReadOnly a && a.IsAlpha != IsAlpha)
return false;

View File

@ -135,7 +135,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
return false;
if (FlawlessIVCount != 0 && pk.FlawlessIVCount < FlawlessIVCount)
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
if (pk is IAlphaReadOnly a && a.IsAlpha != IsAlpha)
return false;

View File

@ -155,7 +155,7 @@ private bool IsMatchNatureGenderShiny(PKM pk)
return false;
if (pk.Gender != Gender)
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
return true;
}
@ -192,7 +192,7 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
return false;
if (FlawlessIVCount != 0 && pk.FlawlessIVCount < FlawlessIVCount)
return false;
if (Nature.IsFixed && pk.Nature != Nature)
if (Nature != Nature.Random && pk.Nature != Nature)
return false;
if (pk is IAlphaReadOnly a && a.IsAlpha != IsAlpha)
return false;

View File

@ -36,10 +36,10 @@ public static bool GenerateData(PA9 pk, in GenerateParam9a enc, in EncounterCrit
{
var rand = new Xoroshiro128Plus(seed);
pk.EncryptionConstant = (uint)rand.NextInt(uint.MaxValue);
var pid = GetAdaptedPID(ref rand, pk, enc);
if (enc.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(ShinyUtil.GetShinyXor(pid, pk.ID32), 16))
pk.PID = GetAdaptedPID(ref rand, pk, enc);
if (enc.Shiny is Shiny.Random && criteria.Shiny.IsShiny() != pk.IsShiny)
return false;
pk.PID = pid;
Span<int> ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET];
if (enc.IVs.IsSpecified)
@ -104,7 +104,7 @@ public static bool GenerateData(PA9 pk, in GenerateParam9a enc, in EncounterCrit
return false;
pk.Gender = gender;
var nature = enc.Nature.IsFixed ? enc.Nature
var nature = enc.Nature != Nature.Random ? enc.Nature
: (Nature)rand.NextInt(25);
// Compromise on Nature -- some are fixed, some are random. If the request wants a specific nature, just mint it.
@ -264,7 +264,7 @@ private static bool IsMatchIVsAndFollowing(PKM pk, in GenerateParam9a enc, Xoros
if (pk.Gender != gender)
return false;
var nature = enc.Nature.IsFixed ? enc.Nature
var nature = enc.Nature != Nature.Random ? enc.Nature
: (Nature)rand.NextInt(25);
if (pk.Nature != nature)
return false;

View File

@ -13,5 +13,5 @@ public interface IFixedNature
/// <summary>
/// Indicates if the nature is a single value (not random).
/// </summary>
bool IsFixedNature => Nature.IsFixed;
bool IsFixedNature => Nature != Nature.Random;
}

View File

@ -15,9 +15,10 @@ public sealed class EvolutionGroupHOME : IEvolutionGroup
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc)
{
if (pk is { Format: <= 9, Context: not EntityContext.Gen9a })
return null;
return EvolutionGroupHOME2.Instance;
return null; // TODO HOME ZA2: Re-enable when we have more info.
// if (pk.Format <= 9 && pk.Context is not EntityContext.Gen9a)
// return null;
// return EvolutionGroupHOME.Instance;
}
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc)
@ -49,16 +50,7 @@ public void DiscardForOrigin(Span<EvoCriteria> result, PKM pk, EvolutionOrigin e
/// </summary>
/// <returns>True if we should check all adjacent evolution sources.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool CheckAllAdjacent(PKM pk, EvolutionOrigin enc)
{
if (enc.SkipChecks)
return true;
if (IsOutsideContext(pk.Context))
return true; // transferred through HOME already
return pk is IHomeTrack { HasTracker: true } || !ParseSettings.IgnoreTransferIfNoTracker;
}
private static bool IsOutsideContext(EntityContext context) => context is not (EntityContext.Gen8 or EntityContext.Gen8a or EntityContext.Gen8b or EntityContext.Gen9);
private static bool CheckAllAdjacent(PKM pk, EvolutionOrigin enc) => enc.SkipChecks || pk is IHomeTrack { HasTracker: true } || !ParseSettings.IgnoreTransferIfNoTracker;
public int Devolve(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc)
{

View File

@ -15,14 +15,15 @@ public sealed class EvolutionGroupHOME2 : IEvolutionGroup
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc)
{
if (enc is { Generation: > 9} or { Context: EntityContext.Gen9a })
return null;
return EvolutionGroupHOME.Instance;
return null; // TODO HOME ZA2: Re-enable when we have more info.
// if (enc.Generation > 9 || enc.Context is EntityContext.Gen9a)
// return null;
// return EvolutionGroupHOME.Instance;
}
public void DiscardForOrigin(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc)
{
if (pk.ZA)
if (pk.ZA) // TODO HOME ZA2: did they force realign everything and fix their bug?
{
var table = PersonalTable.ZA;
if (enc.Options.HasFlag(OriginOptions.SkipChecks))
@ -32,10 +33,8 @@ public void DiscardForOrigin(Span<EvoCriteria> result, PKM pk, EvolutionOrigin e
}
// Check if ability was possibly realigned by form change; if not, discard anything that doesn't have the ability.
// If touched by HOME, HOME realigns tracker, so we can't discard in that case.
var index = pk.AbilityNumber >> 1;
if (index is 0 or 1 && pk is not IHomeTrack { HasTracker: true })
if (index is 0 or 1)
{
var didRealignAbility = false;
var ability = pk.Ability;

View File

@ -138,22 +138,4 @@ private static int GetSpeciesIndex(ReadOnlySpan<EvoCriteria> array, ushort speci
}
return -1;
}
public bool HasVisitedExcept(params ReadOnlySpan<EntityContext> context)
{
if (HasVisitedZA && !context.Contains(EntityContext.Gen9a)) return true;
if (HasVisitedGen9 && !context.Contains(EntityContext.Gen9)) return true;
if (HasVisitedBDSP && !context.Contains(EntityContext.Gen8a)) return true;
if (HasVisitedPLA && !context.Contains(EntityContext.Gen8b)) return true;
if (HasVisitedSWSH && !context.Contains(EntityContext.Gen8)) return true;
if (HasVisitedLGPE && !context.Contains(EntityContext.Gen7b)) return true;
if (HasVisitedGen7 && !context.Contains(EntityContext.Gen7)) return true;
if (HasVisitedGen6 && !context.Contains(EntityContext.Gen6)) return true;
if (HasVisitedGen5 && !context.Contains(EntityContext.Gen5)) return true;
if (HasVisitedGen4 && !context.Contains(EntityContext.Gen4)) return true;
if (HasVisitedGen3 && !context.Contains(EntityContext.Gen3)) return true;
if (HasVisitedGen2 && !context.Contains(EntityContext.Gen2)) return true;
if (HasVisitedGen1 && !context.Contains(EntityContext.Gen1)) return true;
return false;
}
}

View File

@ -20,8 +20,7 @@ public static class Legal
internal const int MaxSpeciesID_3 = 386;
internal const int MaxMoveID_3 = 354;
internal const int MaxItemID_3_RS = 346;
internal const int MaxItemID_3_FRLG = 374;
internal const int MaxItemID_3 = 374;
internal const int MaxItemID_3_E = 376;
internal const int MaxItemID_3_COLO = 547;
internal const int MaxItemID_3_XD = 593;
@ -152,7 +151,7 @@ public static class Legal
internal const int MaxAbilityID_9a_MD = MaxAbilityID_9a_IK;
internal const int MaxBallID_9 = (int)Ball.LAOrigin;
internal const GameVersion MaxGameID_HOME = GameVersion.VL;
internal const GameVersion MaxGameID_HOME = GameVersion.VL; // TODO HOME ZA - Replace with ZA when HOME; if backwards transfer is allowed. If prevented, rename epoch as HOME1.
internal const GameVersion MaxGameID_HOME2 = GameVersion.ZA;
internal static readonly ushort[] HeldItems_GSC = ItemStorage2.GetAllHeld();

View File

@ -18,446 +18,451 @@ public sealed class LegalityCheckLocalization
#region General Strings
/// <summary>Default text for indicating validity.</summary>
public string Valid { get; init; } = "Valid.";
public string Valid { get; set; } = "Valid.";
/// <summary>Default text for indicating legality.</summary>
public string Legal { get; init; } = "Legal!";
public string Legal { get; set; } = "Legal!";
/// <summary>Default text for indicating an error has occurred.</summary>
public string Error { get; init; } = "Internal error.";
public string Error { get; set; } = "Internal error.";
/// <summary>Analysis not available for the <see cref="PKM"/></summary>
public string AnalysisUnavailable { get; init; } = "Analysis not available for this Pokémon.";
public string AnalysisUnavailable { get; set; } = "Analysis not available for this Pokémon.";
/// <summary>Format text for exporting a legality check result.</summary>
public string F0_1 { get; init; } = "{0}: {1}";
public string F0_1 { get; set; } = "{0}: {1}";
/// <summary>Severity string for <see cref="Severity.Invalid"/></summary>
public string SInvalid { get; init; } = "Invalid";
public string SInvalid { get; set; } = "Invalid";
/// <summary>Severity string for <see cref="Severity.Fishy"/></summary>
public string SFishy { get; init; } = "Fishy";
public string SFishy { get; set; } = "Fishy";
/// <summary>Severity string for <see cref="Severity.Valid"/></summary>
public string SValid { get; init; } = "Valid";
public string SValid { get; set; } = "Valid";
/// <summary>Severity string for anything not implemented.</summary>
public string NotImplemented { get; init; } = "Not Implemented";
public string NotImplemented { get; set; } = "Not Implemented";
public string AbilityCapsuleUsed { get; init; } = "Ability available with Ability Capsule.";
public string AbilityPatchUsed { get; init; } = "Ability available with Ability Patch.";
public string AbilityPatchRevertUsed { get; init; } = "Ability available with Ability Patch Revert.";
public string AbilityFlag { get; init; } = "Ability matches ability number.";
public string AbilityHiddenFail { get; init; } = "Hidden Ability mismatch for encounter type.";
public string AbilityHiddenUnavailable { get; init; } = "Hidden Ability not available.";
public string AbilityMismatch { get; init; } = "Ability mismatch for encounter.";
public string AbilityMismatch3 { get; init; } = "Ability does not match Generation 3 species ability.";
public string AbilityMismatchFlag { get; init; } = "Ability does not match ability number.";
public string AbilityMismatchGift { get; init; } = "Ability does not match Mystery Gift.";
public string AbilityMismatchPID { get; init; } = "Ability does not match PID.";
public string AbilityUnexpected { get; init; } = "Ability is not valid for species/form.";
public string AbilityCapsuleUsed { get; set; } = "Ability available with Ability Capsule.";
public string AbilityPatchUsed { get; set; } = "Ability available with Ability Patch.";
public string AbilityPatchRevertUsed { get; set; } = "Ability available with Ability Patch Revert.";
public string AbilityFlag { get; set; } = "Ability matches ability number.";
public string AbilityHiddenFail { get; set; } = "Hidden Ability mismatch for encounter type.";
public string AbilityHiddenUnavailable { get; set; } = "Hidden Ability not available.";
public string AbilityMismatch { get; set; } = "Ability mismatch for encounter.";
public string AbilityMismatch3 { get; set; } = "Ability does not match Generation 3 species ability.";
public string AbilityMismatchFlag { get; set; } = "Ability does not match ability number.";
public string AbilityMismatchGift { get; set; } = "Ability does not match Mystery Gift.";
public string AbilityMismatchPID { get; set; } = "Ability does not match PID.";
public string AbilityUnexpected { get; set; } = "Ability is not valid for species/form.";
public string AwakenedCap { get; init; } = "Individual AV cannot be greater than {0}.";
public string AwakenedShouldBeValue { get; init; } = "{1} AV should be greater than {0}.";
public string AwakenedCap { get; set; } = "Individual AV cannot be greater than {0}.";
public string AwakenedShouldBeValue { get; set; } = "{1} AV should be greater than {0}.";
public string BallAbility { get; init; } = "Can't obtain Hidden Ability with Ball.";
public string BallEggCherish { get; init; } = "Can't have Cherish Ball for regular Egg.";
public string BallEggMaster { get; init; } = "Can't have Master Ball for regular Egg.";
public string BallEnc { get; init; } = "Correct ball for encounter type.";
public string BallEncMismatch { get; init; } = "Can't have ball for encounter type.";
public string BallHeavy { get; init; } = "Can't have Heavy Ball for light, low-catch rate species (Gen VII).";
public string BallSpecies { get; init; } = "Can't obtain species in Ball.";
public string BallSpeciesPass { get; init; } = "Ball possible for species.";
public string BallUnavailable { get; init; } = "Ball unobtainable in origin Generation.";
public string BallG4Sinnoh { get; init; } = "Ball value for D/P/Pt (0x83) is not within range.";
public string BallG4Johto { get; init; } = "Extended Ball value for HG/SS (0x86) is not within range.";
public string BallAbility { get; set; } = "Can't obtain Hidden Ability with Ball.";
public string BallEggCherish { get; set; } = "Can't have Cherish Ball for regular Egg.";
public string BallEggMaster { get; set; } = "Can't have Master Ball for regular Egg.";
public string BallEnc { get; set; } = "Correct ball for encounter type.";
public string BallEncMismatch { get; set; } = "Can't have ball for encounter type.";
public string BallHeavy { get; set; } = "Can't have Heavy Ball for light, low-catch rate species (Gen VII).";
public string BallSpecies { get; set; } = "Can't obtain species in Ball.";
public string BallSpeciesPass { get; set; } = "Ball possible for species.";
public string BallUnavailable { get; set; } = "Ball unobtainable in origin Generation.";
public string BallG4Sinnoh { get; set; } = "Ball value for D/P/Pt (0x83) is not within range.";
public string BallG4Johto { get; set; } = "Extended Ball value for HG/SS (0x86) is not within range.";
public string ContestZero { get; init; } = "Contest Stats should be 0.";
public string ContestZeroSheen { get; init; } = "Contest Stat Sheen should be 0.";
public string ContestSheenGEQ_0 { get; init; } = "Contest Stat Sheen should be >= {0}.";
public string ContestSheenLEQ_0 { get; init; } = "Contest Stat Sheen should be <= {0}.";
public string ContestZero { get; set; } = "Contest Stats should be 0.";
public string ContestZeroSheen { get; set; } = "Contest Stat Sheen should be 0.";
public string ContestSheenGEQ_0 { get; set; } = "Contest Stat Sheen should be >= {0}.";
public string ContestSheenLEQ_0 { get; set; } = "Contest Stat Sheen should be <= {0}.";
public string DateCalendarInvalidMet { get; init; } = "Met Date is not a valid calendar date.";
public string DateCalendarInvalidEgg { get; init; } = "Egg Met Date is not a valid calendar date.";
public string DateLocalInvalidDate { get; init; } = "Local Date is outside of console's local time window.";
public string DateLocalInvalidTime { get; init; } = "Local Time is not a valid timestamp.";
public string DateOutsideDistributionWindow { get; init; } = "Met Date is outside of distribution window.";
public string DateCalendarInvalidMet { get; set; } = "Met Date is not a valid calendar date.";
public string DateCalendarInvalidEgg { get; set; } = "Egg Met Date is not a valid calendar date.";
public string DateLocalInvalidDate { get; set; } = "Local Date is outside of console's local time window.";
public string DateLocalInvalidTime { get; set; } = "Local Time is not a valid timestamp.";
public string DateOutsideDistributionWindow { get; set; } = "Met Date is outside of distribution window.";
public string EggContest { get; init; } = "Cannot increase Contest Stats of an Egg.";
public string EggEXP { get; init; } = "Eggs cannot receive experience.";
public string EggFMetLevel_0 { get; init; } = "Invalid Met Level, expected {0}.";
public string EggHatchCycles { get; init; } = "Invalid Egg hatch cycles.";
public string EggLocation { get; init; } = "Able to hatch an Egg at Met Location.";
public string EggLocationInvalid { get; init; } = "Can't hatch an Egg at Met Location.";
public string EggLocationNone { get; init; } = "Invalid Egg Location, expected none.";
public string EggLocationPalPark { get; init; } = "Invalid Met Location, expected Pal Park.";
public string EggLocationTrade { get; init; } = "Able to hatch a traded Egg at Met Location.";
public string EggLocationTradeFail { get; init; } = "Invalid Egg Location, shouldn't be 'traded' while an Egg.";
public string EggMetLocationFail { get; init; } = "Can't obtain Egg from Egg Location.";
public string EggNature { get; init; } = "Eggs cannot have their Stat Nature changed.";
public string EggPP { get; init; } = "Eggs cannot have modified move PP counts.";
public string EggPPUp { get; init; } = "Cannot apply PP Ups to an Egg.";
public string EggRelearnFlags { get; init; } = "Expected no Relearn Move Flags.";
public string EggShinyPokeStar { get; init; } = "Eggs cannot be a Pokéstar Studios star.";
public string EggSpecies { get; init; } = "Can't obtain Egg for this species.";
public string EggUnhatched { get; init; } = "Valid un-hatched Egg.";
public string EggContest { get; set; } = "Cannot increase Contest Stats of an Egg.";
public string EggEXP { get; set; } = "Eggs cannot receive experience.";
public string EggFMetLevel_0 { get; set; } = "Invalid Met Level, expected {0}.";
public string EggHatchCycles { get; set; } = "Invalid Egg hatch cycles.";
public string EggLocation { get; set; } = "Able to hatch an Egg at Met Location.";
public string EggLocationInvalid { get; set; } = "Can't hatch an Egg at Met Location.";
public string EggLocationNone { get; set; } = "Invalid Egg Location, expected none.";
public string EggLocationPalPark { get; set; } = "Invalid Met Location, expected Pal Park.";
public string EggLocationTrade { get; set; } = "Able to hatch a traded Egg at Met Location.";
public string EggLocationTradeFail { get; set; } = "Invalid Egg Location, shouldn't be 'traded' while an Egg.";
public string EggMetLocationFail { get; set; } = "Can't obtain Egg from Egg Location.";
public string EggNature { get; set; } = "Eggs cannot have their Stat Nature changed.";
public string EggPP { get; set; } = "Eggs cannot have modified move PP counts.";
public string EggPPUp { get; set; } = "Cannot apply PP Ups to an Egg.";
public string EggRelearnFlags { get; set; } = "Expected no Relearn Move Flags.";
public string EggShinyPokeStar { get; set; } = "Eggs cannot be a Pokéstar Studios star.";
public string EggSpecies { get; set; } = "Can't obtain Egg for this species.";
public string EggUnhatched { get; set; } = "Valid un-hatched Egg.";
public string EncCondition { get; init; } = "Valid Wild Encounter at location.";
public string EncConditionBadRNGFrame { get; init; } = "Unable to match encounter conditions to a possible RNG frame.";
public string EncConditionBadSpecies { get; init; } = "Species does not exist in origin game.";
public string EncCondition { get; set; } = "Valid Wild Encounter at location.";
public string EncConditionBadRNGFrame { get; set; } = "Unable to match encounter conditions to a possible RNG frame.";
public string EncConditionBadSpecies { get; set; } = "Species does not exist in origin game.";
public string EncGift { get; init; } = "Unable to match a gift Egg encounter from origin game.";
public string EncGiftEggEvent { get; init; } = "Unable to match an event Egg encounter from origin game.";
public string EncGiftIVMismatch { get; init; } = "IVs do not match Mystery Gift Data.";
public string EncGiftNicknamed { get; init; } = "Event gift has been nicknamed.";
public string EncGiftNotFound { get; init; } = "Unable to match to a Mystery Gift in the database.";
public string EncGiftPIDMismatch { get; init; } = "Mystery Gift fixed PID mismatch.";
public string EncGiftShinyMismatch { get; init; } = "Mystery Gift shiny mismatch.";
public string EncGiftVersionNotDistributed { get; init; } = "Mystery Gift cannot be received by this version.";
public string EncGift { get; set; } = "Unable to match a gift Egg encounter from origin game.";
public string EncGiftEggEvent { get; set; } = "Unable to match an event Egg encounter from origin game.";
public string EncGiftIVMismatch { get; set; } = "IVs do not match Mystery Gift Data.";
public string EncGiftNicknamed { get; set; } = "Event gift has been nicknamed.";
public string EncGiftNotFound { get; set; } = "Unable to match to a Mystery Gift in the database.";
public string EncGiftPIDMismatch { get; set; } = "Mystery Gift fixed PID mismatch.";
public string EncGiftShinyMismatch { get; set; } = "Mystery Gift shiny mismatch.";
public string EncGiftVersionNotDistributed { get; set; } = "Mystery Gift cannot be received by this version.";
public string EncInvalid { get; init; } = "Unable to match an encounter from origin game.";
public string EncMasteryInitial { get; init; } = "Initial move mastery flags do not match the encounter's expected state.";
public string EncInvalid { get; set; } = "Unable to match an encounter from origin game.";
public string EncMasteryInitial { get; set; } = "Initial move mastery flags do not match the encounter's expected state.";
public string EncTradeChangedNickname { get; init; } = "In-game Trade Nickname has been altered.";
public string EncTradeChangedOT { get; init; } = "In-game Trade OT has been altered.";
public string EncTradeIndexBad { get; init; } = "In-game Trade invalid index?";
public string EncTradeMatch { get; init; } = "Valid In-game trade.";
public string EncTradeUnchanged { get; init; } = "In-game Trade OT and Nickname have not been altered.";
public string EncTradeChangedNickname { get; set; } = "In-game Trade Nickname has been altered.";
public string EncTradeChangedOT { get; set; } = "In-game Trade OT has been altered.";
public string EncTradeIndexBad { get; set; } = "In-game Trade invalid index?";
public string EncTradeMatch { get; set; } = "Valid In-game trade.";
public string EncTradeUnchanged { get; set; } = "In-game Trade OT and Nickname have not been altered.";
public string EncStaticPIDShiny { get; init; } = "Encounter shiny mismatch.";
public string EncTypeMatch { get; init; } = "Encounter Type matches encounter.";
public string EncTypeMismatch { get; init; } = "Encounter Type does not match encounter.";
public string EncUnreleased { get; init; } = "Unreleased event.";
public string EncUnreleasedEMewJP { get; init; } = "Non japanese Mew from Faraway Island. Unreleased event.";
public string EncStaticPIDShiny { get; set; } = "Encounter shiny mismatch.";
public string EncTypeMatch { get; set; } = "Encounter Type matches encounter.";
public string EncTypeMismatch { get; set; } = "Encounter Type does not match encounter.";
public string EncUnreleased { get; set; } = "Unreleased event.";
public string EncUnreleasedEMewJP { get; set; } = "Non japanese Mew from Faraway Island. Unreleased event.";
public string EReaderAmerica { get; init; } = "American E-Reader Berry in Japanese save file.";
public string EReaderInvalid { get; init; } = "Invalid E-Reader Berry.";
public string EReaderJapan { get; init; } = "Japanese E-Reader Berry in international save file.";
public string EReaderAmerica { get; set; } = "American E-Reader Berry in Japanese save file.";
public string EReaderInvalid { get; set; } = "Invalid E-Reader Berry.";
public string EReaderJapan { get; set; } = "Japanese E-Reader Berry in international save file.";
public string Effort2Remaining { get; init; } = "2 EVs remaining.";
public string EffortAbove252 { get; init; } = "EVs cannot go above 252.";
public string EffortAbove510 { get; init; } = "EV total cannot be above 510.";
public string EffortAllEqual { get; init; } = "EVs are all equal.";
public string EffortCap100 { get; init; } = "Individual EV for a level 100 encounter in Generation 4 cannot be greater than 100.";
public string EffortEgg { get; init; } = "Eggs cannot receive EVs.";
public string EffortShouldBeZero { get; init; } = "Cannot receive EVs.";
public string EffortEXPIncreased { get; init; } = "All EVs are zero, but leveled above Met Level.";
public string EffortUntrainedCap { get; init; } = "Individual EV without changing EXP cannot be greater than {0}.";
public string Effort2Remaining { get; set; } = "2 EVs remaining.";
public string EffortAbove252 { get; set; } = "EVs cannot go above 252.";
public string EffortAbove510 { get; set; } = "EV total cannot be above 510.";
public string EffortAllEqual { get; set; } = "EVs are all equal.";
public string EffortCap100 { get; set; } = "Individual EV for a level 100 encounter in Generation 4 cannot be greater than 100.";
public string EffortEgg { get; set; } = "Eggs cannot receive EVs.";
public string EffortShouldBeZero { get; set; } = "Cannot receive EVs.";
public string EffortEXPIncreased { get; set; } = "All EVs are zero, but leveled above Met Level.";
public string EffortUntrainedCap { get; set; } = "Individual EV without changing EXP cannot be greater than {0}.";
public string EvoInvalid { get; init; } = "Evolution not valid (or level/trade evolution unsatisfied).";
public string EvoTradeReqOutsider { get; init; } = "Outsider {0} should have evolved into {1}.";
public string EvoTradeRequired { get; init; } = "Version Specific evolution requires a trade to opposite version. A Handling Trainer is required.";
public string EvoInvalid { get; set; } = "Evolution not valid (or level/trade evolution unsatisfied).";
public string EvoTradeReqOutsider { get; set; } = "Outsider {0} should have evolved into {1}.";
public string EvoTradeRequired { get; set; } = "Version Specific evolution requires a trade to opposite version. A Handling Trainer is required.";
public string FatefulGiftMissing { get; init; } = "Fateful Encounter with no matching Encounter. Has the Mystery Gift data been contributed?";
public string FatefulInvalid { get; init; } = "Fateful Encounter should not be checked.";
public string FatefulMissing { get; init; } = "Special In-game Fateful Encounter flag missing.";
public string FatefulMystery { get; init; } = "Mystery Gift Fateful Encounter.";
public string FatefulMysteryMissing { get; init; } = "Mystery Gift Fateful Encounter flag missing.";
public string FatefulGiftMissing { get; set; } = "Fateful Encounter with no matching Encounter. Has the Mystery Gift data been contributed?";
public string FatefulInvalid { get; set; } = "Fateful Encounter should not be checked.";
public string FatefulMissing { get; set; } = "Special In-game Fateful Encounter flag missing.";
public string FatefulMystery { get; set; } = "Mystery Gift Fateful Encounter.";
public string FatefulMysteryMissing { get; set; } = "Mystery Gift Fateful Encounter flag missing.";
public string FavoriteMarkingUnavailable { get; init; } = "Favorite Marking is not available.";
public string FavoriteMarkingUnavailable { get; set; } = "Favorite Marking is not available.";
public string FormArgumentLEQ_0 { get; init; } = "Form argument is too high for current form.";
public string FormArgumentGEQ_0 { get; init; } = "Form argument is too low for current form.";
public string FormArgumentNotAllowed { get; init; } = "Form argument is not allowed for this encounter.";
public string FormArgumentValid { get; init; } = "Form argument is valid.";
public string FormArgumentInvalid { get; init; } = "Form argument is not valid.";
public string FormBattle { get; init; } = "Form cannot exist outside of a battle.";
public string FormInvalidGame { get; init; } = "Form cannot be obtained in origin game.";
public string FormInvalidNature { get; init; } = "Form cannot have this nature.";
public string FormItem { get; init; } = "Held item matches Form.";
public string FormItemInvalid { get; init; } = "Held item does not match Form.";
public string FormParty { get; init; } = "Form cannot exist outside of Party.";
public string FormInvalidExpect_0 { get; init; } = "Form is invalid, expected form index {0}.";
public string FormValid { get; init; } = "Form is Valid.";
public string FormVivillon { get; init; } = "Valid Vivillon pattern.";
public string FormVivillonEventPre { get; init; } = "Event Vivillon pattern on pre-evolution.";
public string FormVivillonInvalid { get; init; } = "Invalid Vivillon pattern.";
public string FormVivillonNonNative { get; init; } = "Non-native Vivillon pattern.";
public string FormArgumentLEQ_0 { get; set; } = "Form argument is too high for current form.";
public string FormArgumentGEQ_0 { get; set; } = "Form argument is too low for current form.";
public string FormArgumentNotAllowed { get; set; } = "Form argument is not allowed for this encounter.";
public string FormArgumentValid { get; set; } = "Form argument is valid.";
public string FormArgumentInvalid { get; set; } = "Form argument is not valid.";
public string FormBattle { get; set; } = "Form cannot exist outside of a battle.";
public string FormEternal { get; set; } = "Valid Eternal Flower encounter.";
public string FormEternalInvalid { get; set; } = "Invalid Eternal Flower encounter.";
public string FormInvalidGame { get; set; } = "Form cannot be obtained in origin game.";
public string FormInvalidNature { get; set; } = "Form cannot have this nature.";
public string FormItem { get; set; } = "Held item matches Form.";
public string FormItemInvalid { get; set; } = "Held item does not match Form.";
public string FormParty { get; set; } = "Form cannot exist outside of Party.";
public string FormPikachuCosplay { get; set; } = "Only Cosplay Pikachu can have this form.";
public string FormPikachuCosplayInvalid { get; set; } = "Cosplay Pikachu cannot have the default form.";
public string FormPikachuEventInvalid { get; set; } = "Event Pikachu cannot have the default form.";
public string FormInvalidExpect_0 { get; set; } = "Form is invalid, expected form index {0}.";
public string FormValid { get; set; } = "Form is Valid.";
public string FormVivillon { get; set; } = "Valid Vivillon pattern.";
public string FormVivillonEventPre { get; set; } = "Event Vivillon pattern on pre-evolution.";
public string FormVivillonInvalid { get; set; } = "Invalid Vivillon pattern.";
public string FormVivillonNonNative { get; set; } = "Non-native Vivillon pattern.";
public string G1CatchRateChain { get; init; } = "Catch rate does not match any species from Pokémon evolution chain.";
public string G1CatchRateEvo { get; init; } = "Catch rate match species without encounters. Expected a pre-evolution catch rate.";
public string G1CatchRateItem { get; init; } = "Catch rate does not match a valid held item from Generation 2.";
public string G1CatchRateMatchPrevious { get; init; } = "Catch Rate matches a species from Pokémon evolution chain.";
public string G1CatchRateMatchTradeback { get; init; } = "Catch rate matches a valid held item from Generation 2.";
public string G1CatchRateNone { get; init; } = "Catch rate does not match any species from Pokémon evolution chain or any Generation 2 held items.";
public string G1CharNick { get; init; } = "Nickname from Generation 1/2 uses unavailable characters.";
public string G1CharOT { get; init; } = "OT from Generation 1/2 uses unavailable characters.";
public string G1OTGender { get; init; } = "Female OT from Generation 1/2 is invalid.";
public string G1Stadium { get; init; } = "Incorrect Stadium OT.";
public string G1Type1Fail { get; init; } = "Invalid Type A, does not match species type.";
public string G1Type2Fail { get; init; } = "Invalid Type B, does not match species type.";
public string G1TypeMatch1 { get; init; } = "Valid Type A, matches species type.";
public string G1TypeMatch2 { get; init; } = "Valid Type B, matches species type.";
public string G1TypeMatchPorygon { get; init; } = "Porygon with valid Type A and B values.";
public string G1TypePorygonFail { get; init; } = "Porygon with invalid Type A and B values. Does not a match a valid type combination.";
public string G1TypePorygonFail1 { get; init; } = "Porygon with invalid Type A value.";
public string G1TypePorygonFail2 { get; init; } = "Porygon with invalid Type B value.";
public string G2InvalidTileTreeNotFound { get; init; } = "Could not find a tree for Crystal headbutt encounter that matches OTID.";
public string G2TreeID { get; init; } = "Found a tree for Crystal headbutt encounter that matches OTID.";
public string G2OTGender { get; init; } = "OT from Virtual Console games other than Crystal cannot be female.";
public string G1CatchRateChain { get; set; } = "Catch rate does not match any species from Pokémon evolution chain.";
public string G1CatchRateEvo { get; set; } = "Catch rate match species without encounters. Expected a preevolution catch rate.";
public string G1CatchRateItem { get; set; } = "Catch rate does not match a valid held item from Generation 2.";
public string G1CatchRateMatchPrevious { get; set; } = "Catch Rate matches a species from Pokémon evolution chain.";
public string G1CatchRateMatchTradeback { get; set; } = "Catch rate matches a valid held item from Generation 2.";
public string G1CatchRateNone { get; set; } = "Catch rate does not match any species from Pokémon evolution chain or any Generation 2 held items.";
public string G1CharNick { get; set; } = "Nickname from Generation 1/2 uses unavailable characters.";
public string G1CharOT { get; set; } = "OT from Generation 1/2 uses unavailable characters.";
public string G1OTGender { get; set; } = "Female OT from Generation 1/2 is invalid.";
public string G1Stadium { get; set; } = "Incorrect Stadium OT.";
public string G1Type1Fail { get; set; } = "Invalid Type A, does not match species type.";
public string G1Type2Fail { get; set; } = "Invalid Type B, does not match species type.";
public string G1TypeMatch1 { get; set; } = "Valid Type A, matches species type.";
public string G1TypeMatch2 { get; set; } = "Valid Type B, matches species type.";
public string G1TypeMatchPorygon { get; set; } = "Porygon with valid Type A and B values.";
public string G1TypePorygonFail { get; set; } = "Porygon with invalid Type A and B values. Does not a match a valid type combination.";
public string G1TypePorygonFail1 { get; set; } = "Porygon with invalid Type A value.";
public string G1TypePorygonFail2 { get; set; } = "Porygon with invalid Type B value.";
public string G2InvalidTileTreeNotFound { get; set; } = "Could not find a tree for Crystal headbutt encounter that matches OTID.";
public string G2TreeID { get; set; } = "Found a tree for Crystal headbutt encounter that matches OTID.";
public string G2OTGender { get; set; } = "OT from Virtual Console games other than Crystal cannot be female.";
public string G3EReader { get; init; } = "Non Japanese Shadow E-reader Pokémon. Unreleased encounter.";
public string G3OTGender { get; init; } = "OT from Colosseum/XD cannot be female.";
public string G4InvalidTileR45Surf { get; init; } = "Johto Route 45 surfing encounter. Unreachable Water tiles.";
public string G4PartnerMoodEgg { get; init; } = "Eggs cannot have an Mood stat value.";
public string G4PartnerMoodZero { get; init; } = "Mood stat value should be zero when not in the player's party.";
public string G4ShinyLeafBitsInvalid { get; init; } = "Shiny Leaf/Crown bits are not valid.";
public string G4ShinyLeafBitsEgg { get; init; } = "Eggs cannot have Shiny Leaf/Crown.";
public string G5IVAll30 { get; init; } = "All IVs of N's Pokémon should be 30.";
public string G5PIDShinyGrotto { get; init; } = "Hidden Grotto captures cannot be shiny.";
public string G5SparkleInvalid { get; init; } = "Special In-game N's Sparkle flag should not be checked.";
public string G5SparkleRequired { get; init; } = "Special In-game N's Sparkle flag missing.";
public string G5PokeStarMustBeZero { get; init; } = "Pokéstar Studios fame must be zero, cannot participate.";
public string G5PokeStarImpossibleValue { get; init; } = "Pokéstar Studios fame value is unreachable.";
public string G7BSocialShouldBe100Spirit { get; init; } = "Spirit should be 100 for Pokémon not in the player's party.";
public string G7BSocialShouldBe100Mood { get; init; } = "Mood should be 100 for Pokémon not in the player's party.";
public string G3EReader { get; set; } = "Non Japanese Shadow E-reader Pokémon. Unreleased encounter.";
public string G3OTGender { get; set; } = "OT from Colosseum/XD cannot be female.";
public string G4InvalidTileR45Surf { get; set; } = "Johto Route 45 surfing encounter. Unreachable Water tiles.";
public string G4PartnerMoodEgg { get; set; } = "Eggs cannot have an Mood stat value.";
public string G4PartnerMoodZero { get; set; } = "Mood stat value should be zero when not in the player's party.";
public string G4ShinyLeafBitsInvalid { get; set; } = "Shiny Leaf/Crown bits are not valid.";
public string G4ShinyLeafBitsEgg { get; set; } = "Eggs cannot have Shiny Leaf/Crown.";
public string G5IVAll30 { get; set; } = "All IVs of N's Pokémon should be 30.";
public string G5PIDShinyGrotto { get; set; } = "Hidden Grotto captures cannot be shiny.";
public string G5SparkleInvalid { get; set; } = "Special In-game N's Sparkle flag should not be checked.";
public string G5SparkleRequired { get; set; } = "Special In-game N's Sparkle flag missing.";
public string G5PokeStarMustBeZero { get; set; } = "Pokéstar Studios fame must be zero, cannot participate.";
public string G5PokeStarImpossibleValue { get; set; } = "Pokéstar Studios fame value is unreachable.";
public string G7BSocialShouldBe100Spirit { get; set; } = "Spirit should be 100 for Pokémon not in the player's party.";
public string G7BSocialShouldBe100Mood { get; set; } = "Mood should be 100 for Pokémon not in the player's party.";
public string GanbaruStatTooHigh { get; init; } = "One or more Ganbaru Value is above the natural limit of (10 - IV bonus).";
public string GanbaruStatTooHigh { get; set; } = "One or more Ganbaru Value is above the natural limit of (10 - IV bonus).";
public string GenderInvalidNone { get; init; } = "Genderless Pokémon should not have a gender.";
public string GeoBadOrder { get; init; } = "GeoLocation Memory: Gap/Blank present.";
public string GeoHardwareInvalid { get; init; } = "Geolocation: Country is not in 3DS region.";
public string GeoHardwareRange { get; init; } = "Invalid Console Region.";
public string GeoHardwareValid { get; init; } = "Geolocation: Country is in 3DS region.";
public string GeoMemoryMissing { get; init; } = "GeoLocation Memory: Memories should be present.";
public string GeoNoCountryHT { get; init; } = "GeoLocation Memory: HT Name present but has no previous Country.";
public string GeoNoRegion { get; init; } = "GeoLocation Memory: Region without Country.";
public string GenderInvalidNone { get; set; } = "Genderless Pokémon should not have a gender.";
public string GeoBadOrder { get; set; } = "GeoLocation Memory: Gap/Blank present.";
public string GeoHardwareInvalid { get; set; } = "Geolocation: Country is not in 3DS region.";
public string GeoHardwareRange { get; set; } = "Invalid Console Region.";
public string GeoHardwareValid { get; set; } = "Geolocation: Country is in 3DS region.";
public string GeoMemoryMissing { get; set; } = "GeoLocation Memory: Memories should be present.";
public string GeoNoCountryHT { get; set; } = "GeoLocation Memory: HT Name present but has no previous Country.";
public string GeoNoRegion { get; set; } = "GeoLocation Memory: Region without Country.";
public string HyperTrainLevelGEQ_0 { get; init; } = "Can't Hyper Train a Pokémon that isn't level {0}.";
public string HyperPerfectAll { get; init; } = "Can't Hyper Train a Pokémon with perfect IVs.";
public string HyperPerfectOne { get; init; } = "Can't Hyper Train a perfect IV.";
public string HyperPerfectUnavailable { get; init; } = "Can't Hyper Train any IV(s).";
public string HyperTrainLevelGEQ_0 { get; set; } = "Can't Hyper Train a Pokémon that isn't level {0}.";
public string HyperPerfectAll { get; set; } = "Can't Hyper Train a Pokémon with perfect IVs.";
public string HyperPerfectOne { get; set; } = "Can't Hyper Train a perfect IV.";
public string HyperPerfectUnavailable { get; set; } = "Can't Hyper Train any IV(s).";
public string ItemEgg { get; init; } = "Eggs cannot hold items.";
public string ItemUnreleased { get; init; } = "Held item is unreleased.";
public string ItemEgg { get; set; } = "Eggs cannot hold items.";
public string ItemUnreleased { get; set; } = "Held item is unreleased.";
public string IVAllEqual_0 { get; init; } = "All IVs are {0}.";
public string IVNotCorrect { get; init; } = "IVs do not match encounter requirements.";
public string IVFlawlessCountGEQ_0 { get; init; } = "Should have at least {0} IVs = 31.";
public string IVAllEqual_0 { get; set; } = "All IVs are {0}.";
public string IVNotCorrect { get; set; } = "IVs do not match encounter requirements.";
public string IVFlawlessCountGEQ_0 { get; set; } = "Should have at least {0} IVs = 31.";
public string LevelBoostNotZero { get; init; } = "Level Boost should be zero.";
public string LevelEXPThreshold { get; init; } = "Current experience matches level threshold.";
public string LevelEXPTooHigh { get; init; } = "Current experience exceeds maximum amount for level 100.";
public string LevelMetBelow { get; init; } = "Current level is below met level.";
public string LevelMetGift { get; init; } = "Met Level does not match Mystery Gift level.";
public string LevelMetGiftFail { get; init; } = "Current Level below Mystery Gift level.";
public string LevelMetSane { get; init; } = "Current level is not below met level.";
public string LevelBoostNotZero { get; set; } = "Level Boost should be zero.";
public string LevelEXPThreshold { get; set; } = "Current experience matches level threshold.";
public string LevelEXPTooHigh { get; set; } = "Current experience exceeds maximum amount for level 100.";
public string LevelMetBelow { get; set; } = "Current level is below met level.";
public string LevelMetGift { get; set; } = "Met Level does not match Mystery Gift level.";
public string LevelMetGiftFail { get; set; } = "Current Level below Mystery Gift level.";
public string LevelMetSane { get; set; } = "Current level is not below met level.";
public string MarkValueOutOfRange_0 { get; init; } = "Individual marking at index {0} is not within the allowed value range.";
public string MarkValueShouldBeZero { get; init; } = "Marking flags cannot be set.";
public string MarkValueUnusedBitsPresent { get; init; } = "Marking flags uses bits beyond the accessible range.";
public string MarkValueOutOfRange_0 { get; set; } = "Individual marking at index {0} is not within the allowed value range.";
public string MarkValueShouldBeZero { get; set; } = "Marking flags cannot be set.";
public string MarkValueUnusedBitsPresent { get; set; } = "Marking flags uses bits beyond the accessible range.";
public string MemoryArgBadCatch_H { get; init; } = "{0} Memory: {0} did not catch this.";
public string MemoryArgBadHatch_H { get; init; } = "{0} Memory: {0} did not hatch this.";
public string MemoryArgBadHT { get; init; } = "Memory: Can't have Handling Trainer Memory as Egg.";
public string MemoryArgBadID_H { get; init; } = "{0} Memory: Can't obtain Memory on {0} Version.";
public string MemoryArgBadItem_H1 { get; init; } = "{0} Memory: Species can't hold this item.";
public string MemoryArgBadLocation_H { get; init; } = "{0} Memory: Can't obtain Location on {0} Version.";
public string MemoryArgBadMove_H1 { get; init; } = "{0} Memory: Species can't learn {1}.";
public string MemoryArgBadOTEgg_H { get; init; } = "{0} Memory: Link Trade is not a valid first memory.";
public string MemoryArgBadSpecies_H1 { get; init; } = "{0} Memory: Can't capture species in game.";
public string MemoryArgSpecies_H { get; init; } = "{0} Memory: Species can be captured in game.";
public string MemoryCleared_H { get; init; } = "Memory: Not cleared properly.";
public string MemoryValid_H { get; init; } = "{0} Memory is valid.";
public string MemoryFeelInvalid_H { get; init; } = "{0} Memory: Invalid Feeling.";
public string MemoryHTFlagInvalid { get; init; } = "Untraded: Current handler should not be the Handling Trainer.";
public string MemoryHTGender_0 { get; init; } = "HT Gender invalid: {0}";
public string MemoryHTLanguage { get; init; } = "HT Language is missing.";
public string MemoryArgBadCatch_H { get; set; } = "{0} Memory: {0} did not catch this.";
public string MemoryArgBadHatch_H { get; set; } = "{0} Memory: {0} did not hatch this.";
public string MemoryArgBadHT { get; set; } = "Memory: Can't have Handling Trainer Memory as Egg.";
public string MemoryArgBadID_H { get; set; } = "{0} Memory: Can't obtain Memory on {0} Version.";
public string MemoryArgBadItem_H1 { get; set; } = "{0} Memory: Species can't hold this item.";
public string MemoryArgBadLocation_H { get; set; } = "{0} Memory: Can't obtain Location on {0} Version.";
public string MemoryArgBadMove_H1 { get; set; } = "{0} Memory: Species can't learn {1}.";
public string MemoryArgBadOTEgg_H { get; set; } = "{0} Memory: Link Trade is not a valid first memory.";
public string MemoryArgBadSpecies_H1 { get; set; } = "{0} Memory: Can't capture species in game.";
public string MemoryArgSpecies_H { get; set; } = "{0} Memory: Species can be captured in game.";
public string MemoryCleared_H { get; set; } = "Memory: Not cleared properly.";
public string MemoryValid_H { get; set; } = "{0} Memory is valid.";
public string MemoryFeelInvalid_H { get; set; } = "{0} Memory: Invalid Feeling.";
public string MemoryHTFlagInvalid { get; set; } = "Untraded: Current handler should not be the Handling Trainer.";
public string MemoryHTGender_0 { get; set; } = "HT Gender invalid: {0}";
public string MemoryHTLanguage { get; set; } = "HT Language is missing.";
public string MemoryIndexArgHT { get; init; } = "Should have a HT Memory TextVar value (somewhere).";
public string MemoryIndexFeel_H1 { get; init; } = "{0} Memory: Feeling should be index {1}.";
public string MemoryIndexFeelHTLEQ9 { get; init; } = "Should have a HT Memory Feeling value 0-9.";
public string MemoryIndexID_H1 { get; init; } = "{0} Memory: Should be index {1}.";
public string MemoryIndexIntensity_H1 { get; init; } = "{0} Memory: Intensity should be index {1}.";
public string MemoryIndexIntensityHT1 { get; init; } = "Should have a HT Memory Intensity value (1st).";
public string MemoryIndexIntensityMin_H1 { get; init; } = "{0} Memory: Intensity should be at least {1}.";
public string MemoryIndexLinkHT { get; init; } = "Should have a Link Trade HT Memory.";
public string MemoryIndexVar { get; init; } = "{0} Memory: TextVar should be index {1}.";
public string MemoryMissingHT { get; init; } = "Memory: Handling Trainer Memory missing.";
public string MemoryMissingOT { get; init; } = "Memory: Original Trainer Memory missing.";
public string MemoryIndexArgHT { get; set; } = "Should have a HT Memory TextVar value (somewhere).";
public string MemoryIndexFeel_H1 { get; set; } = "{0} Memory: Feeling should be index {1}.";
public string MemoryIndexFeelHTLEQ9 { get; set; } = "Should have a HT Memory Feeling value 0-9.";
public string MemoryIndexID_H1 { get; set; } = "{0} Memory: Should be index {1}.";
public string MemoryIndexIntensity_H1 { get; set; } = "{0} Memory: Intensity should be index {1}.";
public string MemoryIndexIntensityHT1 { get; set; } = "Should have a HT Memory Intensity value (1st).";
public string MemoryIndexIntensityMin_H1 { get; set; } = "{0} Memory: Intensity should be at least {1}.";
public string MemoryIndexLinkHT { get; set; } = "Should have a Link Trade HT Memory.";
public string MemoryIndexVar { get; set; } = "{0} Memory: TextVar should be index {1}.";
public string MemoryMissingHT { get; set; } = "Memory: Handling Trainer Memory missing.";
public string MemoryMissingOT { get; set; } = "Memory: Original Trainer Memory missing.";
public string MemorySocialZero { get; init; } = "Social Stat should be zero.";
public string MemoryStatSocialLEQ_0 { get; init; } = "Social Stat should be <= {0}";
public string MemorySocialZero { get; set; } = "Social Stat should be zero.";
public string MemoryStatSocialLEQ_0 { get; set; } = "Social Stat should be <= {0}";
public string MemoryStatAffectionHT0 { get; init; } = "Untraded: Handling Trainer Affection should be 0.";
public string MemoryStatAffectionOT0 { get; init; } = "OT Affection should be 0.";
public string MemoryStatFriendshipHT0 { get; init; } = "Untraded: Handling Trainer Friendship should be 0.";
public string MemoryStatFriendshipOTBaseEvent_0 { get; init; } = "Event OT Friendship does not match base friendship ({0}).";
public string MemoryStatAffectionHT0 { get; set; } = "Untraded: Handling Trainer Affection should be 0.";
public string MemoryStatAffectionOT0 { get; set; } = "OT Affection should be 0.";
public string MemoryStatFriendshipHT0 { get; set; } = "Untraded: Handling Trainer Friendship should be 0.";
public string MemoryStatFriendshipOTBaseEvent_0 { get; set; } = "Event OT Friendship does not match base friendship ({0}).";
public string MetDetailTimeOfDay { get; init; } = "Met Time of Day value is not within the expected range.";
public string MoveEvoFCombination_0 { get; init; } = "Moves combinations is not compatible with {0} evolution.";
public string MoveFExpectSingle_0 { get; init; } = "Expected: {0}";
public string MoveKeldeoMismatch { get; init; } = "Keldeo Move/Form mismatch.";
public string MovePPExpectHealed_01 { get; init; } = "Move {0} PP is below the amount expected ({1}).";
public string MovePPTooHigh_01 { get; init; } = "Move {0} PP is above the amount allowed ({1}).";
public string MovePPUpsTooHigh_01 { get; init; } = "Move {0} PP Ups is above the amount allowed ({1}).";
public string MetDetailTimeOfDay { get; set; } = "Met Time of Day value is not within the expected range.";
public string MoveEvoFCombination_0 { get; set; } = "Moves combinations is not compatible with {0} evolution.";
public string MoveFExpectSingle_0 { get; set; } = "Expected: {0}";
public string MoveKeldeoMismatch { get; set; } = "Keldeo Move/Form mismatch.";
public string MovePPExpectHealed_01 { get; set; } = "Move {0} PP is below the amount expected ({1}).";
public string MovePPTooHigh_01 { get; set; } = "Move {0} PP is above the amount allowed ({1}).";
public string MovePPUpsTooHigh_01 { get; set; } = "Move {0} PP Ups is above the amount allowed ({1}).";
public string MoveShopAlphaMoveShouldBeMastered_0 { get; init; } = "Alpha Move should be marked as mastered.";
public string MoveShopAlphaMoveShouldBeOther { get; init; } = "Alpha encounter cannot be found with this Alpha Move.";
public string MoveShopAlphaMoveShouldBeZero { get; init; } = "Only Alphas may have an Alpha Move set.";
public string MoveShopMasterInvalid_0 { get; init; } = "Cannot manually master {0}: not permitted to master.";
public string MoveShopMasterNotLearned_0 { get; init; } = "Cannot manually master {0}: not in possible learned level up moves.";
public string MoveShopPurchaseInvalid_0 { get; init; } = "Cannot purchase {0} from the move shop.";
public string MoveShopAlphaMoveShouldBeMastered_0 { get; set; } = "Alpha Move should be marked as mastered.";
public string MoveShopAlphaMoveShouldBeOther { get; set; } = "Alpha encounter cannot be found with this Alpha Move.";
public string MoveShopAlphaMoveShouldBeZero { get; set; } = "Only Alphas may have an Alpha Move set.";
public string MoveShopMasterInvalid_0 { get; set; } = "Cannot manually master {0}: not permitted to master.";
public string MoveShopMasterNotLearned_0 { get; set; } = "Cannot manually master {0}: not in possible learned level up moves.";
public string MoveShopPurchaseInvalid_0 { get; set; } = "Cannot purchase {0} from the move shop.";
public string MoveTechRecordFlagMissing_0 { get; init; } = "Unexpected Technical Record Learned flag: {0}";
public string MoveTechRecordFlagMissing_0 { get; set; } = "Unexpected Technical Record Learned flag: {0}";
public string NickFlagEggNo { get; init; } = "Egg must be not nicknamed.";
public string NickFlagEggYes { get; init; } = "Egg must be nicknamed.";
public string NickInvalidChar { get; init; } = "Cannot be given this Nickname.";
public string NickLengthLong { get; init; } = "Nickname too long.";
public string NickLengthShort { get; init; } = "Nickname is empty.";
public string NickMatchLanguage { get; init; } = "Nickname matches species name.";
public string NickMatchLanguageEgg { get; init; } = "Egg matches language Egg name.";
public string NickMatchLanguageEggFail { get; init; } = "Egg name does not match language Egg name.";
public string NickMatchLanguageFail { get; init; } = "Nickname does not match species name.";
public string NickMatchLanguageFlag { get; init; } = "Nickname flagged, matches species name.";
public string NickMatchNoOthers { get; init; } = "Nickname does not match another species name.";
public string NickMatchNoOthersFail { get; init; } = "Nickname matches another species name (+language).";
public string NickFlagEggNo { get; set; } = "Egg must be not nicknamed.";
public string NickFlagEggYes { get; set; } = "Egg must be nicknamed.";
public string NickInvalidChar { get; set; } = "Cannot be given this Nickname.";
public string NickLengthLong { get; set; } = "Nickname too long.";
public string NickLengthShort { get; set; } = "Nickname is empty.";
public string NickMatchLanguage { get; set; } = "Nickname matches species name.";
public string NickMatchLanguageEgg { get; set; } = "Egg matches language Egg name.";
public string NickMatchLanguageEggFail { get; set; } = "Egg name does not match language Egg name.";
public string NickMatchLanguageFail { get; set; } = "Nickname does not match species name.";
public string NickMatchLanguageFlag { get; set; } = "Nickname flagged, matches species name.";
public string NickMatchNoOthers { get; set; } = "Nickname does not match another species name.";
public string NickMatchNoOthersFail { get; set; } = "Nickname matches another species name (+language).";
public string OTLanguage { get; init; } = "Language ID should be {0}, not {1}.";
public string OTLong { get; init; } = "OT Name too long.";
public string OTShort { get; init; } = "OT Name too short.";
public string OTSuspicious { get; init; } = "Suspicious Original Trainer details.";
public string OTLanguage { get; set; } = "Language ID should be {0}, not {1}.";
public string OTLong { get; set; } = "OT Name too long.";
public string OTShort { get; set; } = "OT Name too short.";
public string OTSuspicious { get; set; } = "Suspicious Original Trainer details.";
public string OT_IDEqual { get; init; } = "TID16 and SID16 are equal.";
public string OT_IDs0 { get; init; } = "TID16 and SID16 are 0.";
public string OT_SID0 { get; init; } = "SID16 is zero.";
public string OT_SID0Invalid { get; init; } = "SID16 should be 0.";
public string OT_TID0 { get; init; } = "TID16 is zero.";
public string OT_IDInvalid { get; init; } = "TID16 and SID16 combination is not possible.";
public string OT_IDEqual { get; set; } = "TID16 and SID16 are equal.";
public string OT_IDs0 { get; set; } = "TID16 and SID16 are 0.";
public string OT_SID0 { get; set; } = "SID16 is zero.";
public string OT_SID0Invalid { get; set; } = "SID16 should be 0.";
public string OT_TID0 { get; set; } = "TID16 is zero.";
public string OT_IDInvalid { get; set; } = "TID16 and SID16 combination is not possible.";
public string PIDEncryptWurmple { get; init; } = "Wurmple evolution Encryption Constant mismatch.";
public string PIDEncryptZero { get; init; } = "Encryption Constant is not set.";
public string PIDEqualsEC { get; init; } = "Encryption Constant matches PID.";
public string PIDGenderMatch { get; init; } = "Gender matches PID.";
public string PIDGenderMismatch { get; init; } = "PID-Gender mismatch.";
public string PIDNatureMatch { get; init; } = "Nature matches PID.";
public string PIDNatureMismatch { get; init; } = "PID-Nature mismatch.";
public string PIDTypeMismatch { get; init; } = "PID+ correlation does not match what was expected for the Encounter's type.";
public string PIDZero { get; init; } = "PID is not set.";
public string PIDEncryptWurmple { get; set; } = "Wurmple evolution Encryption Constant mismatch.";
public string PIDEncryptZero { get; set; } = "Encryption Constant is not set.";
public string PIDEqualsEC { get; set; } = "Encryption Constant matches PID.";
public string PIDGenderMatch { get; set; } = "Gender matches PID.";
public string PIDGenderMismatch { get; set; } = "PID-Gender mismatch.";
public string PIDNatureMatch { get; set; } = "Nature matches PID.";
public string PIDNatureMismatch { get; set; } = "PID-Nature mismatch.";
public string PIDTypeMismatch { get; set; } = "PID+ correlation does not match what was expected for the Encounter's type.";
public string PIDZero { get; set; } = "PID is not set.";
public string PlusMoveAlphaMissing_0 { get; init; } = "Expected to have mastered the move {0} when encountered as an alpha.";
public string PlusMoveMultipleInvalid { get; init; } = "Multiple Plus Move flags are invalid.";
public string PlusMoveInvalid_0 { get; init; } = "{0} cannot be learned and set as a Plus Move.";
public string PlusMoveSufficientLevelMissing_0 { get; init; } = "Plus Move flag for {0} must be set."; // as the Pokémon's current level is above the level it becomes available for use as a Plus Move.
public string PlusMoveCountInvalid { get; init; } = "Out of range Plus Move flag index is set.";
public string PlusMoveAlphaMissing_0 { get; set; } = "Expected to have mastered the move {0} when encountered as an alpha.";
public string PlusMoveMultipleInvalid { get; set; } = "Multiple Plus Move flags are invalid.";
public string PlusMoveInvalid_0 { get; set; } = "{0} cannot be learned and set as a Plus Move.";
public string PlusMoveSufficientLevelMissing_0 { get; set; } = "Plus Move flag for {0} must be set."; // as the Pokémon's current level is above the level it becomes available for use as a Plus Move.
public string PlusMoveCountInvalid { get; set; } = "Out of range Plus Move flag index is set.";
public string PokerusDaysTooHigh_0 { get; init; } = "Pokérus Days Remaining value is too high; expected <= {0}.";
public string PokerusStrainUnobtainable_0 { get; init; } = "Pokérus Strain {0} cannot be obtained.";
public string PokerusDaysTooHigh_0 { get; set; } = "Pokérus Days Remaining value is too high; expected <= {0}.";
public string PokerusStrainUnobtainable_0 { get; set; } = "Pokérus Strain {0} cannot be obtained.";
public string RibbonAllValid { get; init; } = "All ribbons accounted for.";
public string RibbonEgg { get; init; } = "Can't receive Ribbon(s) as an Egg.";
public string RibbonsInvalid_0 { get; init; } = "Invalid Ribbons: {0}";
public string RibbonsMissing_0 { get; init; } = "Missing Ribbons: {0}";
public string RibbonMarkingInvalid_0 { get; init; } = "Invalid Marking: {0}";
public string RibbonMarkingMissing_0 { get; init; } = "Missing Marking: {0}";
public string RibbonMarkingAffixed_0 { get; init; } = "Invalid Affixed Ribbon/Marking: {0}";
public string RibbonAllValid { get; set; } = "All ribbons accounted for.";
public string RibbonEgg { get; set; } = "Can't receive Ribbon(s) as an Egg.";
public string RibbonsInvalid_0 { get; set; } = "Invalid Ribbons: {0}";
public string RibbonsMissing_0 { get; set; } = "Missing Ribbons: {0}";
public string RibbonMarkingInvalid_0 { get; set; } = "Invalid Marking: {0}";
public string RibbonMarkingMissing_0 { get; set; } = "Missing Marking: {0}";
public string RibbonMarkingAffixed_0 { get; set; } = "Invalid Affixed Ribbon/Marking: {0}";
public string StatDynamaxInvalid { get; init; } = "Dynamax Level is not within the expected range.";
public string StatIncorrectHeight { get; init; } = "Calculated Height does not match stored value.";
public string StatIncorrectWeight { get; init; } = "Calculated Weight does not match stored value.";
public string StatIncorrectHeightValue_0 { get; init; } = "Height should be {0}.";
public string StatIncorrectWeightValue_0 { get; init; } = "Weight should be {0}.";
public string StatIncorrectScaleValue_0 { get; init; } = "Scale should be {0}.";
public string StatInvalidHeightWeight { get; init; } = "Height / Weight values are statistically improbable.";
public string StatIncorrectCP { get; init; } = "Calculated CP does not match stored value.";
public string StatGigantamaxInvalid { get; init; } = "Gigantamax Flag mismatch.";
public string StatGigantamaxValid { get; init; } = "Gigantamax Flag was changed via Max Soup.";
public string StatNatureInvalid { get; init; } = "Stat Nature is not within the expected range.";
public string StatBattleVersionInvalid { get; init; } = "Battle Version is not within the expected range.";
public string StatNobleInvalid { get; init; } = "Noble Flag mismatch.";
public string StatAlphaInvalid { get; init; } = "Alpha Flag mismatch.";
public string StatDynamaxInvalid { get; set; } = "Dynamax Level is not within the expected range.";
public string StatIncorrectHeight { get; set; } = "Calculated Height does not match stored value.";
public string StatIncorrectWeight { get; set; } = "Calculated Weight does not match stored value.";
public string StatIncorrectHeightValue_0 { get; set; } = "Height should be {0}.";
public string StatIncorrectWeightValue_0 { get; set; } = "Weight should be {0}.";
public string StatIncorrectScaleValue_0 { get; set; } = "Scale should be {0}.";
public string StatInvalidHeightWeight { get; set; } = "Height / Weight values are statistically improbable.";
public string StatIncorrectCP { get; set; } = "Calculated CP does not match stored value.";
public string StatGigantamaxInvalid { get; set; } = "Gigantamax Flag mismatch.";
public string StatGigantamaxValid { get; set; } = "Gigantamax Flag was changed via Max Soup.";
public string StatNatureInvalid { get; set; } = "Stat Nature is not within the expected range.";
public string StatBattleVersionInvalid { get; set; } = "Battle Version is not within the expected range.";
public string StatNobleInvalid { get; set; } = "Noble Flag mismatch.";
public string StatAlphaInvalid { get; set; } = "Alpha Flag mismatch.";
public string StoredSourceEgg { get; init; } = "Egg must be in Box or Party.";
public string StoredSlotSourceInvalid_0 { get; init; } = "Invalid Stored Source: {0}";
public string StoredSourceEgg { get; set; } = "Egg must be in Box or Party.";
public string StoredSlotSourceInvalid_0 { get; set; } = "Invalid Stored Source: {0}";
public string SuperComplete { get; init; } = "Super Training complete flag mismatch.";
public string SuperDistro { get; init; } = "Distribution Super Training missions are not released.";
public string SuperEgg { get; init; } = "Can't Super Train an Egg.";
public string SuperNoComplete { get; init; } = "Can't have active Super Training complete flag for origins.";
public string SuperNoUnlocked { get; init; } = "Can't have active Super Training unlocked flag for origins.";
public string SuperUnavailable { get; init; } = "Super Training missions are not available in games visited.";
public string SuperUnused { get; init; } = "Unused Super Training Flag is flagged.";
public string G6SuperTrainEggBag { get; init; } = "Egg cannot use a Training Bag.";
public string G6SuperTrainEggHits { get; init; } = "Eggs cannot hit Training Bags.";
public string G6SuperTrainBagInvalid_0 { get; init; } = "Unrecognized Training Bag ID: {0}";
public string G6SuperTrainBagHitsInvalid_012 { get; init; } = "Training bag cannot have {0} hits; expected value within [{1},{2}].";
public string SuperComplete { get; set; } = "Super Training complete flag mismatch.";
public string SuperDistro { get; set; } = "Distribution Super Training missions are not released.";
public string SuperEgg { get; set; } = "Can't Super Train an Egg.";
public string SuperNoComplete { get; set; } = "Can't have active Super Training complete flag for origins.";
public string SuperNoUnlocked { get; set; } = "Can't have active Super Training unlocked flag for origins.";
public string SuperUnavailable { get; set; } = "Super Training missions are not available in games visited.";
public string SuperUnused { get; set; } = "Unused Super Training Flag is flagged.";
public string G6SuperTrainEggBag { get; set; } = "Egg cannot use a Training Bag.";
public string G6SuperTrainEggHits { get; set; } = "Eggs cannot hit Training Bags.";
public string G6SuperTrainBagInvalid_0 { get; set; } = "Unrecognized Training Bag ID: {0}";
public string G6SuperTrainBagHitsInvalid_012 { get; set; } = "Training bag cannot have {0} hits; expected value within [{1},{2}].";
public string TeraTypeIncorrect { get; init; } = "Tera Type does not match the expected value.";
public string TeraTypeMismatch { get; init; } = "Tera Type does not match either of the default types.";
public string TeraTypeIncorrect { get; set; } = "Tera Type does not match the expected value.";
public string TeraTypeMismatch { get; set; } = "Tera Type does not match either of the default types.";
public string TradeNotAvailable { get; init; } = "Encounter cannot be traded to the active trainer.";
public string TradeNotAvailable { get; set; } = "Encounter cannot be traded to the active trainer.";
public string TrainerIDNoSeed { get; init; } = "Trainer ID is not obtainable from any RNG seed.";
public string TrainerIDNoSeed { get; set; } = "Trainer ID is not obtainable from any RNG seed.";
public string TransferBad { get; init; } = "Incorrectly transferred from previous generation.";
public string TransferBad { get; set; } = "Incorrectly transferred from previous generation.";
public string TransferCurrentHandlerInvalid { get; init; } = "Invalid Current handler value, trainer details for save file expected another value.";
public string TransferEgg { get; init; } = "Can't transfer Eggs between Generations.";
public string TransferEggLocationTransporter { get; init; } = "Invalid Met Location, expected Poké Transfer.";
public string TransferEggMetLevel { get; init; } = "Invalid Met Level for transfer.";
public string TransferEggVersion { get; init; } = "Can't transfer Eggs to this game.";
public string TransferFlagIllegal { get; init; } = "Flagged as illegal by the game (glitch abuse).";
public string TransferHTFlagRequired { get; init; } = "Current handler cannot be the OT.";
public string TransferHTMismatchName { get; init; } = "Handling trainer does not match the expected trainer name.";
public string TransferHTMismatchGender { get; init; } = "Handling trainer does not match the expected trainer gender.";
public string TransferHTMismatchLanguage { get; init; } = "Handling trainer does not match the expected trainer language.";
public string TransferKoreanGen4 { get; init; } = "Korean Generation 4 games cannot interact with International Generation 4 games.";
public string TransferMet { get; init; } = "Invalid Met Location, expected Poké Transfer or Crown.";
public string TransferNotPossible { get; init; } = "Unable to transfer into current format from origin format.";
public string TransferMetLocation { get; init; } = "Invalid Transfer Met Location.";
public string TransferNature { get; init; } = "Invalid Nature for transfer Experience.";
public string TransferObedienceLevel { get; init; } = "Invalid Obedience Level.";
public string TransferPIDECBitFlip { get; init; } = "PID should be equal to EC [with top bit flipped]!";
public string TransferPIDECEquals { get; init; } = "PID should be equal to EC!";
public string TransferPIDECXor { get; init; } = "Encryption Constant matches shiny-xor'd PID.";
public string TransferTrackerMissing { get; init; } = "Pokémon HOME Transfer Tracker is missing.";
public string TransferTrackerShouldBeZero { get; init; } = "Pokémon HOME Transfer Tracker should be 0.";
public string TransferCurrentHandlerInvalid { get; set; } = "Invalid Current handler value, trainer details for save file expected another value.";
public string TransferEgg { get; set; } = "Can't transfer Eggs between Generations.";
public string TransferEggLocationTransporter { get; set; } = "Invalid Met Location, expected Poké Transfer.";
public string TransferEggMetLevel { get; set; } = "Invalid Met Level for transfer.";
public string TransferEggVersion { get; set; } = "Can't transfer Eggs to this game.";
public string TransferFlagIllegal { get; set; } = "Flagged as illegal by the game (glitch abuse).";
public string TransferHTFlagRequired { get; set; } = "Current handler cannot be the OT.";
public string TransferHTMismatchName { get; set; } = "Handling trainer does not match the expected trainer name.";
public string TransferHTMismatchGender { get; set; } = "Handling trainer does not match the expected trainer gender.";
public string TransferHTMismatchLanguage { get; set; } = "Handling trainer does not match the expected trainer language.";
public string TransferKoreanGen4 { get; set; } = "Korean Generation 4 games cannot interact with International Generation 4 games.";
public string TransferMet { get; set; } = "Invalid Met Location, expected Poké Transfer or Crown.";
public string TransferNotPossible { get; set; } = "Unable to transfer into current format from origin format.";
public string TransferMetLocation { get; set; } = "Invalid Transfer Met Location.";
public string TransferNature { get; set; } = "Invalid Nature for transfer Experience.";
public string TransferObedienceLevel { get; set; } = "Invalid Obedience Level.";
public string TransferPIDECBitFlip { get; set; } = "PID should be equal to EC [with top bit flipped]!";
public string TransferPIDECEquals { get; set; } = "PID should be equal to EC!";
public string TransferPIDECXor { get; set; } = "Encryption Constant matches shinyxored PID.";
public string TransferTrackerMissing { get; set; } = "Pokémon HOME Transfer Tracker is missing.";
public string TransferTrackerShouldBeZero { get; set; } = "Pokémon HOME Transfer Tracker should be 0.";
public string TrashBytesExpected { get; init; } = "Expected Trash Bytes.";
public string TrashBytesMismatchInitial { get; init; } = "Expected initial trash bytes to match the encounter.";
public string TrashBytesMissingTerminatorFinal { get; init; } = "Final terminator missing.";
public string TrashBytesShouldBeEmpty { get; init; } = "Trash Bytes should be cleared.";
public string TrashBytesResetViaTransfer { get; init; } = "Trash Bytes were reset via transfer.";
public string TrashBytesExpected { get; set; } = "Expected Trash Bytes.";
public string TrashBytesMismatchInitial { get; set; } = "Expected initial trash bytes to match the encounter.";
public string TrashBytesMissingTerminatorFinal { get; set; } = "Final terminator missing.";
public string TrashBytesShouldBeEmpty { get; set; } = "Trash Bytes should be cleared.";
public string TrashBytesResetViaTransfer { get; set; } = "Trash Bytes were reset via transfer.";
#endregion
public string EncTradeShouldHaveEvolvedToSpecies_0 { get; init; } = "Trade Encounter should have evolved to species: {0}.";
public string EncGiftLanguageNotDistributed { get; init; } = "Gift Encounter was never distributed with this language.";
public string EncGiftRegionNotDistributed { get; init; } = "Gift Encounter was never distributed to this Console Region.";
public string FormInvalidRangeLEQ_0F { get; init; } = "Form Count is out of range. Expected <= {0}, got {1}.";
public string MovesShouldMatchRelearnMoves { get; init; } = "Moves should exactly match Relearn Moves.";
public string MemoryStatEnjoyment_0 { get; init; } = "Enjoyment should be {0}.";
public string MemoryStatFullness_0 { get; init; } = "Fullness should be {0}.";
public string MemoryStatFullnessLEQ_0 { get; init; } = "Fullness should be <= {0}.";
public string OTLanguageShouldBe_0 { get; init; } = "Language ID should be {0}, not {1}.";
public string OTLanguageShouldBe_0or1 { get; init; } = "Language ID should be {0} or {1}, not {2}.";
public string OTLanguageShouldBeLeq_0 { get; init; } = "Language ID should be <= {0}, not {1}.";
public string OTLanguageCannotPlayOnVersion_0 { get; init; } = "Language ID {0} cannot be played on this version.";
public string OTLanguageCannotTransferToConsoleRegion_0 { get; init; } = "Language ID {0} cannot be transferred to this Console Region.";
public string EncTradeShouldHaveEvolvedToSpecies_0 { get; set; } = "Trade Encounter should have evolved to species: {0}.";
public string EncGiftLanguageNotDistributed { get; set; } = "Gift Encounter was never distributed with this language.";
public string EncGiftRegionNotDistributed { get; set; } = "Gift Encounter was never distributed to this Console Region.";
public string FormInvalidRangeLEQ_0F { get; set; } = "Form Count is out of range. Expected <= {0}, got {1}.";
public string MovesShouldMatchRelearnMoves { get; set; } = "Moves should exactly match Relearn Moves.";
public string MemoryStatEnjoyment_0 { get; set; } = "Enjoyment should be {0}.";
public string MemoryStatFullness_0 { get; set; } = "Fullness should be {0}.";
public string MemoryStatFullnessLEQ_0 { get; set; } = "Fullness should be <= {0}.";
public string OTLanguageShouldBe_0 { get; set; } = "Language ID should be {0}, not {1}.";
public string OTLanguageShouldBe_0or1 { get; set; } = "Language ID should be {0} or {1}, not {2}.";
public string OTLanguageShouldBeLeq_0 { get; set; } = "Language ID should be <= {0}, not {1}.";
public string OTLanguageCannotPlayOnVersion_0 { get; set; } = "Language ID {0} cannot be played on this version.";
public string OTLanguageCannotTransferToConsoleRegion_0 { get; set; } = "Language ID {0} cannot be transferred to this Console Region.";
public string WordFilterInvalidCharacter_0 { get; init; } = "Word Filter: Invalid character '{0}' (0x{1}).";
public string WordFilterFlaggedPattern_01 { get; init; } = "Word Filter ({1}): Flagged pattern '{0}'.";
public string WordFilterTooManyNumbers_0 { get; init; } = "Word Filter: Too many numbers (>{0}).";
public string BulkCloneDetectedDetails { get; init; } = "Clone detected (Details).";
public string BulkCloneDetectedTracker { get; init; } = "Clone detected (Duplicate Tracker).";
public string HintEvolvesToSpecies_0 { get; init; } = "Evolves to species: {0}.";
public string HintEvolvesToRareForm_0 { get; init; } = "Evolves to rare form: {0}.";
public string BulkSharingEncryptionConstantGenerationDifferent { get; init; } = "Detected sharing of Encryption Constant across generations.";
public string BulkSharingEncryptionConstantGenerationSame { get; init; } = "Detected sharing of Encryption Constant.";
public string BulkSharingEncryptionConstantRNGType { get; init; } = "Detected sharing of Encryption Constant sharing for different RNG encounters.";
public string BulkSharingPIDGenerationDifferent { get; init; } = "Detected sharing of PID across generations.";
public string BulkSharingPIDGenerationSame { get; init; } = "Detected sharing of PID.";
public string BulkSharingPIDRNGType { get; init; } = "Detected sharing of PID for different RNG encounters.";
public string BulkDuplicateMysteryGiftEggReceived { get; init; } = "Detected multiple redemptions of the same non-repeatable Mystery Gift Egg.";
public string BulkSharingTrainerID { get; init; } = "Detected sharing of Trainer ID across multiple trainer names.";
public string BulkSharingTrainerVersion { get; init; } = "Detected sharing of Trainer ID across multiple versions.";
public string BulkDuplicateFusionSlot { get; init; } = "Detected multiple fusions of the same fusion stored slot species.";
public string BulkHeldItemInventoryAssignedNoneHeld_0 { get; init; } = "{0} is marked as held player inventory, but no Pokémon found in slots checked.";
public string BulkHeldItemInventoryMultipleSlots_0 { get; init; } = "{0} is a unique item and cannot be held by multiple Pokémon.";
public string BulkHeldItemInventoryNotAcquired_0 { get; init; } = "{0} has not been acquired in player inventory.";
public string BulkHeldItemInventoryUnassigned_0 { get; init; } = "{0} is not marked as assigned in player inventory.";
public string BulkFusionSourceInvalid { get; init; } = "The subsumed Species-Form stored in the save file does not match the expected Species-Form of the fused slot.";
public string WordFilterInvalidCharacter_0 { get; set; } = "Word Filter: Invalid character '{0}' (0x{1}).";
public string WordFilterFlaggedPattern_01 { get; set; } = "Word Filter ({1}): Flagged pattern '{0}'.";
public string WordFilterTooManyNumbers_0 { get; set; } = "Word Filter: Too many numbers (>{0}).";
public string BulkCloneDetectedDetails { get; set; } = "Clone detected (Details).";
public string BulkCloneDetectedTracker { get; set; } = "Clone detected (Duplicate Tracker).";
public string HintEvolvesToSpecies_0 { get; set; } = "Evolves to species: {0}.";
public string HintEvolvesToRareForm_0 { get; set; } = "Evolves to rare form: {0}.";
public string BulkSharingEncryptionConstantGenerationDifferent { get; set; } = "Detected sharing of Encryption Constant across generations.";
public string BulkSharingEncryptionConstantGenerationSame { get; set; } = "Detected sharing of Encryption Constant.";
public string BulkSharingEncryptionConstantRNGType { get; set; } = "Detected sharing of Encryption Constant sharing for different RNG encounters.";
public string BulkSharingPIDGenerationDifferent { get; set; } = "Detected sharing of PID across generations.";
public string BulkSharingPIDGenerationSame { get; set; } = "Detected sharing of PID.";
public string BulkSharingPIDRNGType { get; set; } = "Detected sharing of PID for different RNG encounters.";
public string BulkDuplicateMysteryGiftEggReceived { get; set; } = "Detected multiple redemptions of the same non-repeatable Mystery Gift Egg.";
public string BulkSharingTrainerID { get; set; } = "Detected sharing of Trainer ID across multiple trainer names.";
public string BulkSharingTrainerVersion { get; set; } = "Detected sharing of Trainer ID across multiple versions.";
public string BulkDuplicateFusionSlot { get; set; } = "Detected multiple fusions of the same fusion stored slot species.";
public string BulkHeldItemInventoryAssignedNoneHeld_0 { get; set; } = "{0} is marked as held player inventory, but no Pokémon found in slots checked.";
public string BulkHeldItemInventoryMultipleSlots_0 { get; set; } = "{0} is a unique item and cannot be held by multiple Pokémon.";
public string BulkHeldItemInventoryNotAcquired_0 { get; set; } = "{0} has not been acquired in player inventory.";
public string BulkHeldItemInventoryUnassigned_0 { get; set; } = "{0} is not marked as assigned in player inventory.";
public string BulkFusionSourceInvalid { get; set; } = "The subsumed Species-Form stored in the save file does not match the expected Species-Form of the fused slot.";
}
[JsonSerializable(typeof(LegalityCheckLocalization))]

View File

@ -134,7 +134,7 @@ public static class LegalityCheckResultCodeExtensions
// Evolution
EvoInvalid => localization.EvoInvalid,
EvoTradeReqOutsider_01 => localization.EvoTradeReqOutsider,
EvoTradeReqOutsider_0 => localization.EvoTradeReqOutsider,
EvoTradeRequired => localization.EvoTradeRequired,
// Form
@ -144,11 +144,16 @@ public static class LegalityCheckResultCodeExtensions
FormArgumentValid => localization.FormArgumentValid,
FormArgumentInvalid => localization.FormArgumentInvalid,
FormBattle => localization.FormBattle,
FormEternal => localization.FormEternal,
FormEternalInvalid => localization.FormEternalInvalid,
FormInvalidGame => localization.FormInvalidGame,
FormInvalidNature => localization.FormInvalidNature,
FormItemMatches => localization.FormItem,
FormItemInvalid => localization.FormItemInvalid,
FormParty => localization.FormParty,
FormPikachuCosplay => localization.FormPikachuCosplay,
FormPikachuCosplayInvalid => localization.FormPikachuCosplayInvalid,
FormPikachuEventInvalid => localization.FormPikachuEventInvalid,
FormInvalidExpect_0 => localization.FormInvalidExpect_0,
FormValid => localization.FormValid,
FormVivillon => localization.FormVivillon,

View File

@ -128,7 +128,6 @@ private string GetMemory(CheckResult chk, string template, LegalityCheckResultCo
EncTradeShouldHaveEvolvedToSpecies_0 => string.Format(format, GetSpeciesName(chk.Argument)),
MoveEvoFCombination_0 => string.Format(format, GetSpeciesName(chk.Argument)),
HintEvolvesToSpecies_0 => string.Format(format, GetSpeciesName(chk.Argument)),
EvoTradeReqOutsider_01 => string.Format(format, GetSpeciesName(chk.Argument), GetSpeciesName(chk.Argument2)),
RibbonMarkingInvalid_0 => string.Format(format, GetRibbonName((RibbonIndex)chk.Argument)),
RibbonMarkingMissing_0 => string.Format(format, GetRibbonName((RibbonIndex)chk.Argument)),

View File

@ -68,15 +68,14 @@ public static void Generate(PK5 pk, in EncounterCriteria criteria, byte gr, ulon
if (type is Shiny.Never)
{
if (ShinyUtil.GetIsShiny3(id32, pid))
pid ^= 0x1000_0000; // force not shiny. the wild xor never undoes this fixing.
pid ^= 0x1000_0000; // force not shiny. the wild xor is never true.
}
else if (type is Shiny.Always)
{
// inlined GetShinyPID without ability force (done later)
// retain the random gender value, then ensure top16 bits yield a shiny.
// this results in xor = 0.
pid &= 0xFF;
pid |= ShinyUtil.GetShinyXor(pid, id32) << 16;
var gval = (byte)pid;
var trainerXor = (id32 >> 16) ^ (ushort)id32;
pid = ((gval ^ trainerXor) << 16) | gval;
}
if (isSingleAbility)
@ -90,11 +89,8 @@ public static void Generate(PK5 pk, in EncounterCriteria criteria, byte gr, ulon
if (wildXor)
{
// Attempt to nullify the top bit of the shiny xor?
// It seems this was an attempt by the game developer to give 2x shiny chance for wild encounters...
// Checks and flips bits inconsistently?
// Possibly due to ability being moved to bit16 from bit0, thus shifting the anti-shiny to msb?
var corr = (pid >> 31) ^ (pid & 1) ^ bitXor; // msb, lsb, lsb of tid^sid
// great job checking the wrong bits to give 2x shiny chance for wild stuff.
var corr = (pid >> 31) ^ (pid & 1) ^ bitXor;
if (corr != 0)
pid ^= 0x8000_0000;
}

View File

@ -55,8 +55,7 @@ private static bool TryApplyFromSeed(PK8 pk, in EncounterCriteria criteria, Shin
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false;
}
if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid;
// IVs

View File

@ -114,7 +114,7 @@ public static bool Verify(PKM pk, ulong seed, Span<int> ivs, in GenerateParam8 p
break;
}
var nature = param.Nature.IsFixed ? param.Nature
var nature = param.Nature != Nature.Random ? param.Nature
: param.Species == (int)Species.Toxtricity
? ToxtricityUtil.GetRandomNature(ref rng, pk.Form)
: (Nature)rng.NextInt(25);
@ -160,8 +160,6 @@ public static bool TryApply(PK8 pk, ulong seed, Span<int> ivs, in GenerateParam8
var trID = (uint)rng.NextInt();
var pid = (uint)rng.NextInt();
// Battle
var xor = GetShinyXor(pid, trID);
bool isShiny = xor < 16;
if (isShiny && param.Shiny == Shiny.Never)
@ -169,8 +167,9 @@ public static bool TryApply(PK8 pk, ulong seed, Span<int> ivs, in GenerateParam8
ForceShinyState(false, ref pid, trID, 0);
isShiny = false;
}
if (param.Shiny is Shiny.Random && isShiny != criteria.Shiny.IsShiny())
return false;
// Captured
if (isShiny)
{
if (!GetIsShiny6(pk.ID32, pid))
@ -182,8 +181,6 @@ public static bool TryApply(PK8 pk, ulong seed, Span<int> ivs, in GenerateParam8
pid ^= 0x1000_0000;
}
if (param.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid;
const int UNSET = -1;
@ -239,7 +236,7 @@ public static bool TryApply(PK8 pk, ulong seed, Span<int> ivs, in GenerateParam8
return false;
pk.Gender = gender;
var nature = param.Nature.IsFixed ? param.Nature
var nature = param.Nature != Nature.Random ? param.Nature
: param.Species == (int)Species.Toxtricity
? ToxtricityUtil.GetRandomNature(ref rng, pk.Form)
: (Nature)rng.NextInt(25);

View File

@ -129,8 +129,6 @@ public static bool TryApplyFromSeed(PA8 pk, in EncounterCriteria criteria, in Ov
if (para.Shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false;
}
if (para.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid;
Span<int> ivs = [UNSET, UNSET, UNSET, UNSET, UNSET, UNSET];
@ -174,8 +172,6 @@ public static bool TryApplyFromSeed(PA8 pk, in EncounterCriteria criteria, in Ov
pk.Gender = gender;
var nature = (Nature)rand.NextInt(25);
if (criteria.IsSpecifiedNature() && !criteria.IsSatisfiedNature(nature))
return false;
pk.Nature = pk.StatNature = nature;
var (height, weight) = para.IsAlpha
@ -183,10 +179,16 @@ public static bool TryApplyFromSeed(PA8 pk, in EncounterCriteria criteria, in Ov
: ((byte)(rand.NextInt(0x81) + rand.NextInt(0x80)),
(byte)(rand.NextInt(0x81) + rand.NextInt(0x80)));
pk.HeightScalar = height;
pk.WeightScalar = weight;
pk.ResetHeight();
pk.ResetWeight();
if (pk is IScaledSize s)
{
s.HeightScalar = height;
s.WeightScalar = weight;
if (pk is IScaledSizeValue a)
{
a.ResetHeight();
a.ResetWeight();
}
}
return true;
}

View File

@ -65,8 +65,6 @@ public static bool TryApplyFromSeed(PB8 pk, in EncounterCriteria criteria, Shiny
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false;
}
if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid;
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.

View File

@ -65,8 +65,6 @@ public static bool TryApplyFromSeed(PB8 pk, in EncounterCriteria criteria, Shiny
if (shiny == Shiny.AlwaysStar && type != Shiny.AlwaysStar)
return false;
}
if (shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
return false;
pk.PID = pid;
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.

View File

@ -62,10 +62,10 @@ public static bool GenerateData(PK9 pk, in GenerateParam9 enc, in EncounterCrite
{
var rand = new Xoroshiro128Plus(seed);
pk.EncryptionConstant = (uint)rand.NextInt(uint.MaxValue);
var pid = GetAdaptedPID(ref rand, pk, enc);
if (enc.Shiny is Shiny.Random && criteria.IsSpecifiedShiny() && !criteria.IsSatisfiedShiny(GetShinyXor(pid, pk.ID32), 16))
pk.PID = GetAdaptedPID(ref rand, pk, enc);
if (enc.Shiny is Shiny.Random && criteria.Shiny.IsShiny() != pk.IsShiny)
return false;
pk.PID = pid;
const int UNSET = -1;
const int MAX = 31;
@ -121,7 +121,7 @@ public static bool GenerateData(PK9 pk, in GenerateParam9 enc, in EncounterCrite
return false;
pk.Gender = gender;
var nature = enc.Nature.IsFixed ? enc.Nature : enc.Species == (int)Species.Toxtricity
var nature = enc.Nature != Nature.Random ? enc.Nature : enc.Species == (int)Species.Toxtricity
? ToxtricityUtil.GetRandomNature(ref rand, pk.Form)
: (Nature)rand.NextInt(25);
@ -205,7 +205,7 @@ public static bool IsMatch(PKM pk, in GenerateParam9 enc, in ulong seed)
if (pk.Gender != gender)
return false;
var nature = enc.Nature.IsFixed ? enc.Nature : enc.Species == (int)Species.Toxtricity
var nature = enc.Nature != Nature.Random ? enc.Nature : enc.Species == (int)Species.Toxtricity
? ToxtricityUtil.GetRandomNature(ref rand, pk.Form)
: (Nature)rand.NextInt(25);
if (pk.Nature != nature)

View File

@ -4,7 +4,7 @@ namespace PKHeX.Core;
/// Parameters used to generate data for an encounter.
/// </summary>
/// <param name="Species">Species to generate.</param>
/// <param name="GenderRatio">Gender ratio byte from Personal Info.</param>
/// <param name="GenderRatio">Gender ratio byte.</param>
/// <param name="FlawlessIVs">Count of IVs that are perfect.</param>
/// <param name="RollCount">Count of shiny rolls allowed for the PID calculation.</param>
/// <param name="Height">Height value to generate. If zero, full random.</param>

View File

@ -38,7 +38,6 @@ public static bool IsRequired(IEncounterTemplate enc, EntityContext current)
WB8 { IsHOMEGift: true } => true,
WA8 { IsHOMEGift: true } => true,
WC9 { IsHOMEGift: true } => true,
WA9 { IsHOMEGift: true } => true,
_ => enc.Generation < 8,
};
}

View File

@ -51,7 +51,7 @@ public static bool IsHeldItemAllowed(int item, EntityContext context)
// Combined bitflags for released held items across generations.
private static readonly bool[] ReleasedHeldItems_2 = GetPermitList(MaxItemID_2, HeldItems_GSC);
private static readonly bool[] ReleasedHeldItems_3 = GetPermitList(MaxItemID_3_RS, HeldItems_RS, ItemStorage3RS.Unreleased); // Safari Ball
private static readonly bool[] ReleasedHeldItems_3 = GetPermitList(MaxItemID_3, HeldItems_RS, ItemStorage3RS.Unreleased); // Safari Ball
private static readonly bool[] ReleasedHeldItems_4 = GetPermitList(MaxItemID_4_HGSS, HeldItems_HGSS, ItemStorage4.Unreleased);
private static readonly bool[] ReleasedHeldItems_5 = GetPermitList(MaxItemID_5_B2W2, HeldItems_BW, ItemStorage5.Unreleased);
private static readonly bool[] ReleasedHeldItems_6 = GetPermitList(MaxItemID_6_AO, HeldItems_AO, ItemStorage6XY.Unreleased);

View File

@ -20,8 +20,9 @@ public static MemorySource GetPossibleSources(EvolutionHistory history)
sources |= MemorySource.Bank; // Trade encounters from Gen7 also come with hardcoded memories.
if (history.HasVisitedSWSH)
sources |= MemorySource.Gen8;
if (history.HasVisitedGen9 || history.HasVisitedZA)
if (history.HasVisitedGen9)
sources |= MemorySource.Deleted;
// TODO HOME ZA
return sources;
}

View File

@ -138,11 +138,16 @@ public enum LegalityCheckResultCode : ushort
FormArgumentValid,
FormArgumentInvalid,
FormBattle,
FormEternal,
FormEternalInvalid,
FormInvalidGame,
FormInvalidNature,
FormItemMatches,
FormItemInvalid,
FormParty,
FormPikachuCosplay,
FormPikachuCosplayInvalid,
FormPikachuEventInvalid,
FormValid,
FormVivillon,
FormVivillonEventPre,
@ -388,6 +393,7 @@ public enum LegalityCheckResultCode : ushort
ContestSheenLEQ_0,
EggFMetLevel_0,
EffortUntrainedCap_0,
EvoTradeReqOutsider_0,
FormArgumentLEQ_0,
FormArgumentGEQ_0,
FormInvalidExpect_0,
@ -473,7 +479,6 @@ public enum LegalityCheckResultCode : ushort
EncTradeShouldHaveEvolvedToSpecies_0, // species
MoveEvoFCombination_0, // species
HintEvolvesToSpecies_0, // species
EvoTradeReqOutsider_01, // species, species
RibbonMarkingInvalid_0, // ribbon
RibbonMarkingMissing_0, // ribbon

View File

@ -137,7 +137,7 @@ public static bool IsFormChangeable(ushort species, byte oldForm, byte newForm,
{
EntityContext.Gen6 => false,
EntityContext.Gen7 => newForm >= 2 || (oldForm == 1 && newForm == 0),
EntityContext.Gen9a when origin is EntityContext.Gen9a => newForm >= 2,
EntityContext.Gen9a => newForm >= 2,
_ => true,
};
}
@ -420,7 +420,7 @@ public static bool IsLordForm(ushort species, byte form, EntityContext context)
/// <param name="pi">Game specific personal info</param>
/// <param name="species"><see cref="Species"/> ID</param>
/// <param name="format"><see cref="PKM.Form"/> ID</param>
/// <returns>True if it has forms that can be provided by <see cref="FormConverter"/>, otherwise false for none.</returns>
/// <returns>True if it has forms that can be provided by <see cref="FormConverter.GetFormList"/>, otherwise false for none.</returns>
public static bool HasFormSelection(IPersonalFormInfo pi, ushort species, byte format)
{
if (format <= 3 && species != (int)Unown)

View File

@ -1,4 +1,3 @@
using System;
using static PKHeX.Core.LegalityCheckResultCode;
namespace PKHeX.Core;
@ -473,8 +472,6 @@ private CheckResult VerifyBirthAbility(LegalityAnalysis data, PA9 pa9)
var index = bitNum >> 1;
var species = pa9.Species;
var ability = pa9.Ability;
// In-battle forms allow mismatching for the current form.
if (FormInfo.HasMegaForm(species) || species is (int)Species.Aegislash)
{
var form = pa9.Form;
@ -495,62 +492,10 @@ private CheckResult VerifyBirthAbility(LegalityAnalysis data, PA9 pa9)
}
}
// If deposited in HOME, it will realign to the deposited species-form ability.
// If you transfer back into ZA then evolve it, you can disjoint it again.
// If it hasn't evolved, then it must have been realigned by HOME.
if (pa9 is IHomeTrack { HasTracker: true })
{
var form = pa9.Form;
var actual = PersonalTable.ZA[species, form];
var require = actual.GetAbilityAtIndex(index);
if (ability == require)
return VALID;
if (enc.Species == species || IsAnyMoveSourceNotZA_Head(data.Info.Moves)) // Not evolved after; must match.
return GetInvalid(AbilityMismatch);
}
// Otherwise, we expect the form's personal info to match the original encounter's ability.
var expect = pi.GetAbilityAtIndex(index);
if (ability != expect)
return GetInvalid(AbilityMismatch);
return VerifyFinalState(data, enc, index);
}
private CheckResult VerifyFinalState(LegalityAnalysis data, IEncounterTemplate enc, int currentIndex)
{
if (currentIndex == 2) // Not normally obtainable. Has to visit another game where it can be switched.
{
if (AbilityChangeRules.IsAbilityPatchPossible(data.Info.EvoChainsAllGens))
return GetValid(AbilityPatchUsed);
return GetInvalid(AbilityHiddenUnavailable);
}
if (!enc.Ability.IsSingleValue(out var bit) || bit == currentIndex) // Any/Unchanged
return VALID;
var history = data.Info.EvoChainsAllGens;
if (bit == 2 && AbilityChangeRules.IsAbilityPatchRevertPossible(history, currentIndex))
return GetValid(AbilityPatchRevertUsed);
if (bit != 2 && AbilityChangeRules.IsAbilityCapsuleAvailable(history))
return GetValid(AbilityCapsuleUsed);
// Can't change to current state.
return GetInvalid(AbilityMismatch);
}
private static bool IsAnyMoveSourceNotZA_Head(ReadOnlySpan<MoveResult> infoMoves)
{
foreach (var info in infoMoves)
{
if (info.EvoStage != 0) // pre-evo
continue;
if (info.Context == EntityContext.Gen9a)
continue;
// move verifier doesn't check for realignment lockout scenario; disable check.
// return true;
}
return false;
}
}

View File

@ -31,8 +31,20 @@ private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
return (Species)pk.Species switch
{
Furfrou => CheckFurfrou(pk, enc, f),
Hoopa when pk.Form == 1 => CheckHoopa(data, f, arg),
// Transfer Edge Cases -- Bank wipes the form but keeps old FormArgument value.
Furfrou when pk is { Context: EntityContext.Gen7, Form: 0 } &&
((enc.Generation == 6 && f.FormArgument <= byte.MaxValue) || IsFormArgumentDayCounterValid(f, 5, true))
=> GetValid(FormArgumentValid),
Furfrou when pk.Form != 0 => !IsFormArgumentDayCounterValid(f, 5, true) ? GetInvalid(FormArgumentInvalid) : GetValid(FormArgumentValid),
Hoopa when pk.Form == 1 => data.Info.EvoChainsAllGens switch
{
{ HasVisitedZA: true } when arg == 0 => GetValid(FormArgumentValid), // Value not applied on form change, and reset when reverted.
{ HasVisitedGen9: true } when arg == 0 => GetValid(FormArgumentValid), // Value not applied on form change, and reset when reverted.
{ HasVisitedGen6: true } when IsFormArgumentDayCounterValid(f, 3) => GetValid(FormArgumentValid), // 0-3 via OR/AS
{ HasVisitedGen7: true } when IsFormArgumentDayCounterValid(f, 3) && f.FormArgumentRemain != 0 => GetValid(FormArgumentValid), // 1-3 via Gen7
_ => GetInvalid(FormArgumentInvalid),
},
Yamask when pk.Form == 1 => arg switch
{
not 0 when pk.IsEgg => GetInvalid(FormArgumentNotAllowed),
@ -62,8 +74,22 @@ private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
> 9_999 => GetInvalid(FormArgumentLEQ_0, 9999),
_ => arg == 0 || HasVisitedSV(data, Bisharp) ? GetValid(FormArgumentValid) : GetInvalid(FormArgumentNotAllowed),
},
Gimmighoul => CheckGimmighoul(data.Info.EvoChainsAllGens, arg, pk),
Gholdengo => CheckGholdengo(data.Info.EvoChainsAllGens, arg, enc, pk),
Gimmighoul => arg switch
{
// Z-A evolutions do not set form argument to Gimmighoul.
0 when data.Info.EvoChainsAllGens.HasVisitedZA => GetValid(FormArgumentValid),
// When leveled up, the game copies the save file's current coin count to the arg (clamped to <=999). If >=999, evolution is triggered.
// Without being leveled up at least once, it cannot have a form arg value.
>= 999 => GetInvalid(FormArgumentLEQ_0, 999),
0 => GetValid(FormArgumentValid),
// S/V sets form argument to match coin count.
_ when !data.Info.EvoChainsAllGens.HasVisitedGen9 => GetInvalid(FormArgumentInvalid),
_ => pk.CurrentLevel != pk.MetLevel ? GetValid(FormArgumentValid) : GetInvalid(FormArgumentNotAllowed),
},
Gholdengo when !data.Info.EvoChainsAllGens.HasVisitedGen9 => arg == 0 ? GetValid(FormArgumentValid) : GetInvalid(FormArgumentInvalid),
Gholdengo => VerifyFormArgumentRange(enc.Species, Gholdengo, arg, 999, 999),
Runerigus => VerifyFormArgumentRange(enc.Species, Runerigus, arg, 49, 9999),
Alcremie => VerifyFormArgumentRange(enc.Species, Alcremie, arg, 0, (ushort)AlcremieDecoration.Ribbon),
@ -93,186 +119,6 @@ private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
};
}
private CheckResult CheckHoopa(LegalityAnalysis data, IFormArgument f, uint arg)
{
var history = data.Info.EvoChainsAllGens;
if (arg == 0)
{
if (history.HasVisitedZA) // Value not applied on form change, and reset when reverted.
return GetValid(FormArgumentValid);
if (history.HasVisitedGen9) // Value not applied on form change, and reset when reverted.
return GetValid(FormArgumentValid);
}
else
{
if (history.HasVisitedGen7 && IsFormArgumentDayCounterValid(f, 3)) // 1-3 via Gen7
return GetValid(FormArgumentValid);
}
var pk = data.Entity;
if (pk is PK6 pk6)
{
// 0-3 via OR/AS
if (pk6.FormArgument != 0) // 0x3C not used (elapsed streak)
return GetInvalid(FormArgumentNotAllowed);
var elapsed = pk6.FormArgumentElapsed;
var remain = pk6.FormArgumentRemain;
var sum = elapsed + remain;
if (sum != 3)
return GetInvalid(FormArgumentInvalid);
return GetValid(FormArgumentValid);
}
return GetInvalid(FormArgumentInvalid);
}
private CheckResult CheckFurfrou(PKM pk, IEncounterTemplate enc, IFormArgument f)
{
// Transfer Edge Cases -- Bank wipes the form but keeps old FormArgument value.
// Gen6: Reverts when deposited.
// Gen7: Reverts form & arg when withdrawn, reverts form (NOT arg) when deposited in Bank.
// Gen9a: Doesn't decrease, always 5.
if (pk is PK6 pk6)
return CheckFurfrou6(pk6);
if (pk is PK7 pk7)
return CheckFurfrou7(pk7, enc);
if (pk is { GO_HOME: true } && f.FormArgument == 0)
return GetValid(FormArgumentValid); // GO transfers forget to set Form Argument.
if (pk is PA9 pa9)
return CheckFurfrou9a(pa9, enc);
// Only legal pathways are via methods above.
return GetInvalid(FormArgumentInvalid);
}
private CheckResult CheckFurfrou9a(PA9 pk, IEncounterTemplate enc)
{
// Gen6=>Gen7 transfer edge case: Form argument is not cleared when depositing in Bank, but form is.
if (enc.Generation == 6 && pk is { Form: 0, FormArgument: <= byte.MaxValue })
return GetValid(FormArgumentValid);
// Z-A trims set to 5.
if ((pk.FormArgument == 5) == (pk.Form != 0))
return GetValid(FormArgumentValid);
// Bank=>HOME with form 0 forgets to wipe, but Form is reverted.
if (pk.Form == 0 && enc.Generation <= 7 && IsFormArgumentDayCounterValid(pk, 5, true))
return GetValid(FormArgumentValid);
return GetInvalid(FormArgumentInvalid);
}
private CheckResult CheckFurfrou7(PK7 pk, IEncounterTemplate enc)
{
// Gen6=>Gen7 transfer edge case: Form argument is not cleared when depositing in Bank, but form is.
if (enc.Generation == 6 && pk is { Form: 0, FormArgument: <= byte.MaxValue })
return GetValid(FormArgumentValid);
if (pk is { Form: 0, FormArgument: 0 })
return GetValid(FormArgumentValid);
// Depositing into box no longer clears form; they only wipe it when you withdraw.
// Storing in Bank will revert the form, so any form is valid as long as the day counter values are valid for any trim.
if (!IsFormArgumentDayCounterValid(pk, 5, true))
return GetInvalid(FormArgumentInvalid);
return GetValid(FormArgumentValid);
}
private CheckResult CheckFurfrou6(PK6 pk)
{
// Can only exist in party.
// 0x3C: Current streak
// 0xED: Remaining days
// 0xEE: Elapsed days (same as current streak)
var arg = pk.FormArgument;
// Argument can be anything; depositing drops the form and party stats and forgets to clear the arg.
if (arg > byte.MaxValue)
return GetInvalid(FormArgumentLEQ_0, byte.MaxValue);
// Form can only exist inside party. Checked elsewhere.
var remain = pk.FormArgumentRemain;
var elapsed = pk.FormArgumentElapsed;
if (pk.Form != 0)
{
var sum = remain + elapsed;
if (sum < 5)
return GetInvalid(FormArgumentInvalid);
if (elapsed != arg)
return GetInvalid(FormArgumentInvalid);
}
else
{
// Party stat values must be zero.
if (remain != 0 || elapsed != 0)
return GetInvalid(FormArgumentNotAllowed);
}
return GetValid(FormArgumentValid);
}
private CheckResult CheckGimmighoul(EvolutionHistory history, uint arg, PKM pk)
{
if (arg == 0)
return GetValid(FormArgumentValid);
// The only game we can assign a form argument value is in S/V.
// Z-A evolutions do not set form argument to Gimmighoul.
if (history.HasVisitedGen9)
{
// When leveled up, the game copies the save file's current coin count to the arg (clamped to <=999). If >=999, evolution is triggered (can cancel).
// Without being leveled up at least once, it cannot have a form arg value.
if (arg > 999)
return GetInvalid(FormArgumentLEQ_0, 999);
if (pk.CurrentLevel == pk.MetLevel)
return GetInvalid(FormArgumentNotAllowed);
return GetValid(FormArgumentValid);
}
return GetInvalid(FormArgumentNotAllowed);
}
private CheckResult CheckGholdengo(EvolutionHistory history, uint arg, IEncounterable enc, PKM pk)
{
if (enc.Species == (ushort)Gholdengo)
{
if (arg == 0)
return GetValid(FormArgumentValid);
return GetInvalid(FormArgumentNotAllowed);
}
// Gimmighoul evolved.
// The only game we can assign a form argument value is in S/V.
// Z-A evolutions do not set form argument to Gimmighoul.
var hasVisitedNoArgGame = history.HasVisitedZA;
if (hasVisitedNoArgGame && arg == 0)
return GetValid(FormArgumentValid);
if (history.HasVisitedGen9)
{
// When leveled up, the game copies the save file's current coin count to the arg (clamped to <=999). If >=999, evolution is triggered (can cancel).
// Without being leveled up at least once, it cannot have a form arg value.
if (arg > 999)
return GetInvalid(FormArgumentLEQ_0, 999);
if (pk.CurrentLevel == pk.MetLevel)
return GetInvalid(FormArgumentNotAllowed);
if (!hasVisitedNoArgGame && arg != 999) // Evolving without visiting a less-restricted game requires 999.
return GetInvalid(FormArgumentGEQ_0, 999);
return GetValid(FormArgumentValid);
}
return GetInvalid(FormArgumentNotAllowed);
}
private static bool IsFormArgumentValidFurfrou8HOME(IFormArgument f, IEncounterTemplate enc)
{
if (f.FormArgument == 0 && enc is { Version: GameVersion.GO })
return true; // Does not come with a Form Argument.
return IsFormArgumentDayCounterValid(f, 5, enc.Generation < 8);
}
private CheckResult CheckPrimeape(LegalityAnalysis data, PKM pk, uint arg, IEncounterTemplate enc)
{
if (arg == 0)

View File

@ -43,7 +43,15 @@ private CheckResult VerifyForm(LegalityAnalysis data)
switch ((Species)species)
{
case Pikachu when enc.Generation == 6: // Cosplay
if (form != 0 && pk.Format != 6) // Regular Pikachu, OK.
if (enc is not EncounterStatic6 s6)
{
if (form == 0)
break; // Regular Pikachu, OK.
return GetInvalid(FormPikachuCosplay);
}
if (form != s6.Form)
return GetInvalid(FormPikachuCosplayInvalid);
if (pk.Format != 6)
return GetInvalid(TransferBad); // Can't transfer.
break;
@ -52,6 +60,16 @@ private CheckResult VerifyForm(LegalityAnalysis data)
case Eevee when form is not 0 && ParseSettings.ActiveTrainer is SAV7b {Version:GameVersion.GP}:
return GetInvalid(FormBattle);
case Pikachu when enc.Generation >= 7: // Cap
var expectForm = enc is EncounterInvalid or IEncounterEgg ? 0 : enc.Form;
if (form != expectForm)
{
bool gift = enc is MysteryGift g && g.Form != form;
var msg = gift ? FormPikachuEventInvalid : FormInvalidGame;
return GetInvalid(msg);
}
break;
case Unown when enc.Generation == 2 && form >= 26:
return GetInvalid(FormInvalidRangeLEQ_0F, 25);
case Unown when enc.Generation == 3:
@ -83,9 +101,12 @@ private CheckResult VerifyForm(LegalityAnalysis data)
case Genesect:
var genesect = FormItem.GetFormGenesect(pk.HeldItem);
return genesect != form ? GetInvalid(FormItemInvalid) : GetValid(FormItemMatches);
case Furfrou when pk.Context == EntityContext.Gen6 && form != 0 && !data.IsStoredSlot(StorageSlotType.Party):
return GetInvalid(FormParty);
case Greninja:
if (form > 1) // Ash Battle Bond active
return GetInvalid(FormBattle);
if (form != 0 && enc is not MysteryGift) // Form can not be bred for, MysteryGift already checked
return GetInvalid(FormInvalidRangeLEQ_0F, 0);
break;
case Scatterbug or Spewpa or Vivillon when enc.Context is EntityContext.Gen9:
if (form > 18 && enc.Form != form) // Pokéball
@ -118,6 +139,10 @@ private CheckResult VerifyForm(LegalityAnalysis data)
data.AddLine(Get(Severity.Fishy, FormVivillonNonNative));
break;
case Floette when form == 5: // Eternal Flower Floette - not released until Pokémon Legends: Z-A
if (enc is not EncounterGift9a)
return GetInvalid(FormEternalInvalid);
return GetValid(FormEternal);
case Meowstic when (form & 1) != pk.Gender:
return GetInvalid(GenderInvalidNone);

View File

@ -299,10 +299,8 @@ public static bool GetCanOTHandle(IEncounterTemplate enc, PKM pk, byte generatio
WB8 wb8 when wb8.GetHasOT(pk.Language) => false,
WA8 wa8 when wa8.GetHasOT(pk.Language) => false,
WC9 wc9 when wc9.GetHasOT(pk.Language) => false,
WA9 wa9 when wa9.GetHasOT(pk.Language) => false,
WC8 {IsHOMEGift: true} => false,
WC9 {IsHOMEGift: true} => false,
WA9 {IsHOMEGift: true} => false,
_ => true,
};

View File

@ -31,9 +31,10 @@ private void CheckLearnset(LegalityAnalysis data, PA9 pa)
if (moveCount == 4)
return;
// Flag move slots that are empty.
if (pa is IHomeTrack { HasTracker: true } || !ParseSettings.IgnoreTransferIfNoTracker)
return; // Can delete moves in PA9 moveset via HOME.
// TODO ZA HOME
// // Flag move slots that are empty.
// if (pa.Tracker != 0 || !ParseSettings.IgnoreTransferIfNoTracker)
// return; // Can delete moves in PA9 moveset via HOME.
if (e9a.Species is (int)Rotom && moveCount == 3 && pa.Form == 0)
{
@ -215,8 +216,7 @@ private void CheckFlagsPlus(LegalityAnalysis la, PA9 pk)
// Trade evolutions forget to set the Plus flags, unlike triggered evolutions.
// If the move is not present as a previous-evolution learnset move, and the head species is a Trade evo, skip the error.
// Assume the best case -- evolved at current level, so none would get set.
var evos = la.Info.EvoChainsAllGens;
if (IsTradeEvoSkip(evos.Gen9a, move))
if (IsTradeEvoSkip(la.Info.EvoChainsAllGens.Gen9a, move))
continue;
if (WasPossiblyObtainedBeforeDLC(pk, la.EncounterMatch) && IsPermittedUnsetPlusMove((Species)pk.Species, (Move)move))

View File

@ -95,7 +95,7 @@ public void VerifyG1(LegalityAnalysis data)
{
// Pokémon has been traded illegally between games without evolving.
// Trade evolution species IDs for Gen1 are sequential dex numbers.
data.AddLine(GetInvalid(EvoTradeReqOutsider_01, enc.Species, (ushort)(enc.Species + 1u)));
data.AddLine(GetInvalid(EvoTradeReqOutsider_0, enc.Species + 1u));
}
}

View File

@ -78,8 +78,7 @@ private void VerifyAffixedRibbonMark(LegalityAnalysis data, IRibbonIndex m)
return;
var affix = (RibbonIndex)affixValue;
var evos = data.Info.EvoChainsAllGens;
var max = MarkRules.GetMaxAffixValue(evos);
var max = MarkRules.GetMaxAffixValue(data.Info.EvoChainsAllGens);
if ((sbyte)max == -1 || affix > max)
{
data.AddLine(GetInvalid(RibbonMarkingAffixed_0, (ushort)affix));
@ -89,19 +88,11 @@ private void VerifyAffixedRibbonMark(LegalityAnalysis data, IRibbonIndex m)
if (m is not PKM pk)
return;
var enc = data.EncounterOriginal;
if (MarkRules.IsEncounterMarkLost(enc, data.Entity))
if (MarkRules.IsEncounterMarkLost(data.EncounterOriginal, data.Entity))
{
VerifyShedinjaAffixed(data, affix, pk, m);
return;
}
// Some games cannot affix ribbon unless it transfers to a game that can affix it.
if (enc.Context is EntityContext.Gen8a or EntityContext.Gen9a && pk.Context == enc.Context)
{
if (!evos.HasVisitedExcept(enc.Context))
data.AddLine(GetInvalid(RibbonMarkingAffixed_0, (ushort)affix));
}
EnsureHasRibbon(data, m, affix);
}

View File

@ -90,8 +90,7 @@ private void VerifyHTLanguage(LegalityAnalysis data, MemorySource source)
private static bool GetIsHTLanguageValid(IEncounterTemplate enc, PKM pk, byte language, MemorySource source)
{
// Bounds check.
var max = Legal.GetMaxLanguageID(pk.Format, pk.Context);
if (language > max)
if (language > (int)LanguageID.ChineseT)
return false;
// Gen6 and Bank don't have the HT language flag.

View File

@ -33,7 +33,7 @@ private void VerifyRandomCorrelationSwitch(LegalityAnalysis data, PKM pk, IEncou
{
VerifyCorrelation8b(data, s8b, pk);
}
else if (enc is ISeedCorrelation64<PKM> s64 && !(enc is WA9 { IsHOMEGift: true }))
else if (enc is ISeedCorrelation64<PKM> s64)
{
var pidiv = s64.TryGetSeed(pk, out var seed);
if (pidiv == SeedCorrelationResult.Success)

View File

@ -29,14 +29,11 @@ private static void VerifyHeightWeight(LegalityAnalysis data, PKM pk, IEncounter
}
else if (enc.Context is EntityContext.Gen9a)
{
// By default, Gen9a does not apply height/weight properties, so 0-0 is expected.
// If touched by HOME, Scale is copied to both Height and Weight properties.
// Thus, only n-n is valid. Scale we'll check separately if the current object's format supports it.
var height = s2.HeightScalar;
var weight = s2.WeightScalar;
if (height != weight)
data.AddLine(GetInvalid(Encounter, StatIncorrectWeightValue_0, height));
// TODO HOME ZA
if (s2.HeightScalar != 0)
data.AddLine(GetInvalid(Encounter, StatIncorrectHeightValue_0, 0));
if (s2.WeightScalar != 0)
data.AddLine(GetInvalid(Encounter, StatIncorrectWeightValue_0, 0));
}
else if (CheckHeightWeightOdds(enc))
{
@ -53,7 +50,7 @@ private void VerifyScale(LegalityAnalysis data, PKM pk, IEncounterTemplate enc,
// PLA static Alphas have potential for 127 scale; this is already checked explicitly in the matching check.
// Ensure all Alphas have 255 scale.
// Otherwise, ensure scale matches height scalar if required.
if (enc is { Context: EntityContext.Gen8a } and IAlphaReadOnly { IsAlpha: true })
if (enc is IAlphaReadOnly { IsAlpha: true })
{
byte expect = enc switch
{
@ -62,27 +59,6 @@ private void VerifyScale(LegalityAnalysis data, PKM pk, IEncounterTemplate enc,
};
if (s3.Scale != expect)
data.AddLine(GetInvalid(StatIncorrectScaleValue_0, expect));
return;
}
if (enc is { Context: EntityContext.Gen9a })
{
// Height != Weight already checked.
var scale = s3.Scale;
var height = s2.HeightScalar;
if (scale == 0)
{
// If scale is 0, then the only legal value for height/weight is also 0.
if (height != 0)
data.AddLine(GetInvalid(StatIncorrectHeightValue_0, 0));
}
else
{
// If scale is nonzero, then height/weight must be 0 or equal to scale.
// If it was definitely touched by HOME, then they can only be equal to scale, since HOME copies scale to height/weight.
if ((height != 0 || IsHeightScaleMatchRequired(pk)) && height != scale)
data.AddLine(GetInvalid(StatIncorrectHeightValue_0, height));
}
}
else if (IsHeightScaleMatchRequired(pk) && s2.HeightScalar != s3.Scale)
{
@ -96,6 +72,8 @@ private static bool CheckHeightWeightOdds(IEncounterTemplate enc)
{
if (enc.Generation < 8)
return false;
if (enc.Context is EntityContext.Gen9a) // TODO HOME ZA
return true;
if (enc is WC8 { IsHOMEGift: true })
return false;
if (enc is WC9) // fixed values (usually 0 or 128)

View File

@ -46,7 +46,7 @@ private void VerifyTrash(LegalityAnalysis data, G3PKM pk)
VerifyTrashCXD(data, pk);
}
private static void VerifyTrashCXD(LegalityAnalysis data, G3PKM pk)
private void VerifyTrashCXD(LegalityAnalysis data, G3PKM pk)
{
// Buffers should be entirely clean.
var ot = pk.OriginalTrainerTrash;
@ -122,10 +122,7 @@ private static void VerifyTrashINT(LegalityAnalysis data, PK3 pk)
var trash = pk.OriginalTrainerTrash;
// OT name from save file is copied byte-for-byte. All 8 bytes are initialized to FF on new game.
if (!TrashByteRules3.IsTerminatedFFZero(trash, 7))
{
if (!TrashByteRules3.IsTrashPatternDefaultTrainer(trash, pk.Version, (LanguageID)pk.Language))
data.AddLine(GetInvalid(Trainer, TrashBytesMissingTerminatorFinal));
}
// Nickname can be all FF's (nicknamed) or whatever random garbage is in the buffer before filling. Unsure if we can reliably check this, but it should be "dirty" usually.
// If it is clean, flag as fishy.
FlagIsNicknameClean(data, pk);
@ -135,16 +132,10 @@ private static void FlagIsNicknameClean(LegalityAnalysis data, PK3 pk)
{
if (!pk.IsNicknamed || pk.IsEgg)
return;
// Japanese only fills the first 5+1 bytes; everything else is trash.
// International games are 10 chars (full buffer) max; implicit terminator if full.
var nick = pk.GetNicknamePrefillRegion();
var nick = pk.NicknameTrash;
if (!TrashByteRules3.IsTerminatedFF(nick))
{
// Trade to another language and evolve will treat it like a nickname, without actually filling with FF.
if (!TrashByteRules3.IsTerminatedFFZero(nick) || pk.Species == data.EncounterOriginal.Species) // not evolved
data.AddLine(GetInvalid(Trainer, TrashBytesMismatchInitial));
}
}
}
public static class TrashByteRules3
@ -154,10 +145,6 @@ public static class TrashByteRules3
// When transferred to Colosseum/XD, the encoding method switches to u16[length], thus discarding the original buffer along with its "trash".
// For original encounters from a mainline save file,
// - OT Name: the game copies the entire buffer from the save file OT as the PK3's OT. Thus, that must match exactly.
// - - Japanese OT names are 5 chars, international is 7 chars. Manually entered strings are FF terminated to max length + 1.
// - - Default OT (Japanese) names were padded with FF to len=6, so they always match manually entered names (no trash).
// - - Default OT (International) names from the character select screen can have trash bytes due to being un-padded (single FF end of string, saves ROM space).
// - - verification of Default OTs todo (if OT dirty, check if is default with expected trash pattern)
// - Nickname: the buffer has garbage RAM data leftover in the nickname field, thus it should be "dirty" usually.
// - Nicknamed: when nicknamed, the game fills the buffer with FFs then applies the nickname.
// For event encounters from GameCube:
@ -186,7 +173,7 @@ public static bool IsResetTrash(PK3 pk3)
public static bool IsTerminatedZero(ReadOnlySpan<byte> data)
{
var first = TrashBytes8.GetTerminatorIndex(data);
if (first == -1 || first >= data.Length - 1)
if (first == -1 || first >= data.Length - 2)
return true;
return !data[(first+1)..].ContainsAnyExcept<byte>(0);
}
@ -194,93 +181,28 @@ public static bool IsTerminatedZero(ReadOnlySpan<byte> data)
public static bool IsTerminatedFF(ReadOnlySpan<byte> data)
{
var first = TrashBytes8.GetTerminatorIndex(data);
if (first == -1 || first >= data.Length - 1)
if (first == -1 || first >= data.Length - 2)
return true;
return !data[(first + 1)..].ContainsAnyExcept(Terminator);
return !data[(first + 1)..].ContainsAnyExcept<byte>(0xFF);
}
/// <summary>
/// Checks if the <see cref="data"/> matches the pattern of a pre-filled array with terminators of count <see cref="preFill"/>.
/// </summary>
/// <param name="data">Raw text string to check</param>
/// <param name="preFill">Count of chars filled with terminator.</param>
/// <returns><see langword="true"/> if the text matches the pre-fill pattern.</returns>
public static bool IsTerminatedFFZero(ReadOnlySpan<byte> data, int preFill = 0)
{
if (preFill == 0)
return IsTerminatedZero(data);
var first = TrashBytes8.GetTerminatorIndex(data);
if (first == -1 || first >= data.Length - 1)
if (first == -1 || first == data.Length - 2)
return true;
first++;
if (first < preFill)
{
var inner = data[first..preFill];
if (inner.ContainsAnyExcept(Terminator))
return false;
first = preFill;
if (first >= data.Length)
if (first >= data.Length - 2)
return true;
}
return !data[first..].ContainsAnyExcept<byte>(0);
return !data[(first + 1)..].ContainsAnyExcept<byte>(0);
}
// TRASH BYTES: New Game Default OTs
// Default OT names in International (not JPN) Gen3 mainline games memcpy 7 chars and FF from the "default OT name" table, regardless of strlen.
// Copied strings therefore contain trash from the next string entry that is encoded into the rom's string table.
// Below is a list of possible (version, language, trash) default OTs, as initialized by the game. An `*` is used to denote the terminator.
/// <summary>
/// Checks if the specified trash byte pattern matches a default trainer name pattern for the given game <see cref="version"/> and <see cref="language"/>.
/// </summary>
/// <remarks>Default trainer names in certain Generation 3 Pokémon games may include trailing bytes ("trash") due to how names are stored in the game's ROM.
/// This method checks if the provided pattern matches any of these known default patterns for the specified version and language.
/// </remarks>
public static bool IsTrashPatternDefaultTrainer(ReadOnlySpan<byte> trash, GameVersion version, LanguageID language) => version switch
{
GameVersion.R or GameVersion.S => IsTrashPatternDefaultTrainerRS(trash, language),
GameVersion.E => IsTrashPatternDefaultTrainerE(trash, language),
GameVersion.FR => IsTrashPatternDefaultTrainerFR(trash, language),
GameVersion.LG => IsTrashPatternDefaultTrainerLG(trash, language),
_ => false,
};
/// <inheritdoc cref="IsTrashPatternDefaultTrainer"/>
/// <remarks>Default OT names present in <see cref="GameVersion.R"/> and <see cref="GameVersion.S"/> based on the language of the game.</remarks>
public static bool IsTrashPatternDefaultTrainerRS(ReadOnlySpan<byte> trash, LanguageID language) => language switch
{
// TODO
LanguageID.English => trash switch
{
[0xCD, 0xBB, 0xCC, 0xBB, 0xFF, 0xCE, 0xDC] => true, // SARA*Th
_ => false,
},
_ => false,
};
/// <inheritdoc cref="IsTrashPatternDefaultTrainer"/>
/// <remarks>Default OT names present in <see cref="GameVersion.E"/> based on the language of the game.</remarks>
public static bool IsTrashPatternDefaultTrainerE(ReadOnlySpan<byte> trash, LanguageID language) => language switch
{
// TODO
_ => false,
};
/// <inheritdoc cref="IsTrashPatternDefaultTrainer"/>
/// <remarks>Default OT names present in <see cref="GameVersion.FR"/> based on the language of the game.</remarks>
public static bool IsTrashPatternDefaultTrainerFR(ReadOnlySpan<byte> trash, LanguageID language) => language switch
{
// TODO
_ => false,
};
/// <inheritdoc cref="IsTrashPatternDefaultTrainer"/>
/// <remarks>Default OT names present in <see cref="GameVersion.LG"/> based on the language of the game.</remarks>
public static bool IsTrashPatternDefaultTrainerLG(ReadOnlySpan<byte> trash, LanguageID language) => language switch
{
// TODO
_ => false,
};
}

View File

@ -17,7 +17,7 @@ internal static void VerifyStatNature(LegalityAnalysis data, PKM pk)
return;
// Must be a valid mint nature.
if (!statNature.IsMint)
if (!statNature.IsMint())
data.AddLine(Get(Invalid, Misc, StatNatureInvalid));
}

View File

@ -1,6 +1,5 @@
using System;
using static PKHeX.Core.LegalityCheckResultCode;
using static PKHeX.Core.MoveHealState;
namespace PKHeX.Core;
@ -67,124 +66,68 @@ private void VerifyEntity(LegalityAnalysis data)
}
}
var allowedStates = GetPermittedStatePP(data, pk);
var expectHeal = Legal.IsPPUnused(pk) || IsPPHealed(data, pk);
for (int i = 0; i < pp.Length; i++)
{
// Sometimes the PP count will exceed (such as VC=>Bank); just flag it as invalid so the user knows they need to heal them.
// Technically that case is legal (game bug) only if they never move it from the box, but we want to inform the user.
var healed = pk.GetMovePP(moves[i], ups[i]);
var expect = pk.GetMovePP(moves[i], ups[i]);
var value = pp[i];
if (value > healed)
{
if (value > expect)
data.AddLine(GetInvalid(MovePPTooHigh_01, (ushort)(i + 1), (ushort)value));
continue;
else if (expectHeal && value != expect)
data.AddLine(GetInvalid(MovePPExpectHealed_01, (ushort)(i + 1), (ushort)value));
}
}
if (allowedStates == Any)
continue;
if (value == healed && (allowedStates == OnlyHealed || allowedStates.HasFlag(AllowHealed)))
continue;
if (value == 0 && (allowedStates == Only0 || allowedStates.HasFlag(Allow0)))
continue;
// Not Valid. Add a flag.
var (message, expect) = allowedStates switch
private static bool IsPPHealed(LegalityAnalysis data, PKM pk)
{
OnlyHealed => (MovePPExpectHealed_01, healed),
Only0 => (MovePPTooHigh_01, 0),
_ => (MovePPExpectHealed_01, healed), // just pick one of the expected states; heal is safe default.
};
data.AddLine(GetInvalid(message, (ushort)(i + 1), (ushort)expect));
}
}
private static bool IsFreshZATransferFromHOME_400(PA9 pk, EntityContext context)
{
if (context != EntityContext.Gen9a)
return true;
var scale = pk.Scale;
if (pk.HeightScalar == scale && pk.WeightScalar == scale)
return true;
return false;
}
private static MoveHealState GetPermittedStatePP(LegalityAnalysis data, PKM pk)
{
if (Legal.IsPPUnused(pk))
{
if (pk is PA9 pa9 && IsFreshZATransferFromHOME_400(pa9, data.EncounterOriginal.Context))
return AllowHealedOr0; // HOME sets 0 PP for all moves. Healing / reassigning moves in ZA will heal individual indexes.
return OnlyHealed;
}
if (data.IsStoredSlot(StorageSlotType.Party))
return Any;
return false;
return data.SlotOrigin switch
{
StorageSlotType.Box or StorageSlotType.GTS or StorageSlotType.BattleBox => GetIsStoredHealed(pk, data.EncounterOriginal),
_ => Any, // Deposited slots pass through party.
_ => false, // Deposited slots pass through party.
};
}
/// <summary>
/// Checks if the format is expected to have the Pokémon healed to full PP.
/// </summary>
private static MoveHealState GetIsStoredHealed(PKM pk, IEncounterTemplate enc) => pk switch
private static bool GetIsStoredHealed(PKM pk, IEncounterTemplate enc) => pk switch
{
// Boxes accessible from anywhere; retain HP and PP
PK9 => Any,
PK8 or PA8 or PB8 => Any,
PB7 => Any,
PK9 => false,
PK8 or PA8 or PB8 => false,
PB7 => false,
// Don't heal PP when deposited
PK1 or PK2 => Any,
PK6 or PK7 => Any,
PK1 or PK2 => false,
PK6 or PK7 => false,
// Do heal after capture/deposit
SK2 => OnlyHealed,
CK3 or XK3 => OnlyHealed,
PK4 or RK4 or BK4 or PK5 => OnlyHealed,
SK2 => true,
CK3 or XK3 => true,
PK4 or RK4 or BK4 or PK5 => true,
// Check if the encounter has left the boxes after being acquired by the player
// only reachable by PK3?
_ => HasLeftBoxAfterAcquisition(pk, enc),
};
private static MoveHealState HasLeftBoxAfterAcquisition(PKM pk, IEncounterTemplate enc)
private static bool HasLeftBoxAfterAcquisition(PKM pk, IEncounterTemplate enc)
{
if (enc.Context != pk.Context)
return OnlyHealed; // Different context, assume it was traded and thus is not a wild->box
return true; // Different context, assume it was traded and thus is not a wild->box
if (pk.EVTotal != 0)
return OnlyHealed; // EVs are not possible direct from wild encounters
return true; // EVs are not possible direct from wild encounters
if (!Experience.IsAtLevelThreshold(pk.EXP, pk.PersonalInfo.EXPGrowth, out var current))
return OnlyHealed; // gained experience
return true; // gained experience
// Only scenario is if it was leveled up AND matches that exp threshold
if (pk.Format >= 3) // has met level
return pk.MetLevel != current ? OnlyHealed : Any;
return !enc.IsLevelWithinRange(current) ? OnlyHealed : Any;
return pk.MetLevel != current;
return !enc.IsLevelWithinRange(current);
}
}
[Flags]
public enum MoveHealState
{
None, // Invalid result.
// Intermixed states for individual move indexes:
Allow0 = 1 << 0, // A zero PP value is allowed.
AllowHealed = 1 << 1, // Expect PP to be fully healed.
AllowUsed = 1 << 2, // Any value in-between is allowed.
AllowHealedOr0 = Allow0 | AllowHealed, // Expect PP to be either fully healed or 0.
// Overall states for all move indexes:
OnlyHealed = 1 << 3, // Expect PP of all indexes to be fully healed.
Only0 = 1 << 4, // Expect PP of all indexes to be 0.
Any = Allow0 | AllowHealed | AllowUsed, // No expectation on PP values.
}

View File

@ -24,7 +24,7 @@ public override void Verify(LegalityAnalysis data)
if (pk.PID == 0)
data.AddLine(Get(Severity.Fishy, PIDZero));
if (!pk.Nature.IsFixed) // out of range
if (!pk.Nature.IsFixed()) // out of range
data.AddLine(GetInvalid(PIDNatureMismatch));
if (data.Info.EncounterMatch is IEncounterEgg egg)
VerifyEggPID(data, pk, egg);
@ -207,9 +207,9 @@ public static bool GetTransferEC(PKM pk, out uint ec)
return false;
}
// get the current shiny xor value, check against the previous 1:8192 scheme
var pid = pk.PID;
var XOR = ShinyUtil.GetShinyXor(pid, pk.ID32);
var tmp = pid ^ pk.ID32;
var XOR = (ushort)(tmp ^ (tmp >> 16));
// Ensure we don't have a shiny.
if (XOR >> 3 == 1) // Illegal, fix. (not 16<XOR>=8)

View File

@ -147,23 +147,20 @@ public static bool IsMarkValidAlpha(PKM pk, bool wasAlpha)
return true;
if (!wasAlpha)
return !m.RibbonMarkAlpha; // Shouldn't have the flag.
if (!HasEnteredHOME_Alpha(pk))
if (!HasEnteredHOME300(pk))
return true; // Can be either state -- only HOME sets the flag.
return m.RibbonMarkAlpha; // Should have the flag.
}
private static bool HasEnteredHOME_Alpha(PKM pk)
private static bool HasEnteredHOME300(PKM pk)
{
// Mark is only set by HOME ingesting the data for the first time.
// Before HOME 3.0.0, this mark was never set.
// Could be okay as a Gen8* format -- don't bother checking for "must have visited HOME 3.0.0+".
// Could be okay as Gen8 format -- don't bother checking for "must have visited HOME 3.0.0+".
if (pk is IHomeTrack { HasTracker: false })
return false; // Hasn't been transferred to HOME yet.
if (pk.LA && pk is PK8 or PB8 or PA8)
return false; // Could have been moved prior to the HOME 3.0.0 update.
// Before HOME 4.0.0, this mark was only set when you moved it in for the first time.
// In HOME 4.0.0, the mark is set by HOME opening your save data and saving, modifying properties without you touching them.
if (pk.ZA && pk is IScaledSize { HeightScalar: 0 }) // Alphas would update to 255-255-255 scale.
return false; // Might not have touched HOME yet.
return true;
}
@ -172,7 +169,7 @@ private static bool HasEnteredHOME_Alpha(PKM pk)
/// </summary>
public static bool IsMarkValidAlpha(IEncounterTemplate enc, PKM pk)
{
var expect = enc is IAlphaReadOnly { IsAlpha: true };
var expect = enc is IAlphaReadOnly { IsAlpha: true } && enc.Context != EntityContext.Gen9a; // TODO ZA HOME
return IsMarkValidAlpha(pk, expect);
}

View File

@ -184,6 +184,9 @@ private void VerifyHOMETracker(LegalityAnalysis data, PKM pk)
// - Transfer a 0-Tracker pk to HOME to get assigned a valid Tracker via the game it originated from.
// - Don't make one up.
}
if (pk.ZA != pk is PA9) // TODO: ZA HOME Compatibility - flag in/out transfers for now.
data.AddLine(GetInvalid(TransferBad));
}
public void VerifyVCEncounter(PKM pk, IEncounterTemplate original, EncounterTransfer7 transfer, LegalityAnalysis data)

View File

@ -41,7 +41,7 @@ public override ReadOnlySpan<byte> Write()
{
// Ensure PGT content is encrypted
var clone = new PCD(Data.ToArray());
clone.Gift.VerifyGiftEncryption();
clone.Gift.VerifyPKEncryption();
return clone.Data;
}

View File

@ -45,16 +45,17 @@ public override byte Ball
public int ItemSubID { get => ReadInt32LittleEndian(Data[0x8..]); set => WriteInt32LittleEndian(Data[0x8..], value); }
public int PokewalkerCourseID { get => Data[0x4]; set => Data[0x4] = (byte)value; }
private Span<byte> DataGift => Data.Slice(8, PokeCrypto.SIZE_4PARTY);
public PK4 PK
{
get => field ??= new PK4(DataGift.ToArray());
get => field ??= new PK4(Data.Slice(8, PokeCrypto.SIZE_4PARTY).ToArray());
set
{
field = value.Clone(); // cache the PK4 for future use
value.Data.CopyTo(DataGift);
VerifyGiftEncryption();
field = value;
var data = value.Data;
bool zero = !data.ContainsAnyExcept<byte>(0); // all zero
if (!zero)
data = PokeCrypto.EncryptArray45(data);
data.CopyTo(Data[8..]);
}
}
@ -62,29 +63,31 @@ public override ReadOnlySpan<byte> Write()
{
// Ensure PGT content is encrypted
var clone = new PGT(Data.ToArray());
clone.VerifyGiftEncryption();
clone.VerifyPKEncryption();
return clone.Data;
}
/// <summary>
/// Double-checks the encryption of the gift data.
/// Double-checks the encryption of the gift data for Pokémon data.
/// </summary>
/// <returns>True if data was encrypted, false if the data was not modified.</returns>
public bool VerifyGiftEncryption()
public bool VerifyPKEncryption()
{
if (GiftType is not (Pokémon or PokémonEgg))
return false; // not encrypted
var gift = DataGift;
var isEmpty = !gift.ContainsAnyExcept<byte>(0); // all zero
if (isEmpty) // shouldn't ever be empty, just return if so.
return false;
if (PokeCrypto.IsEncrypted45(gift)) // unused PK4 ribbon bits
return false;
PokeCrypto.Encrypt45(gift);
if (ReadUInt32LittleEndian(Data[(0x64 + 8)..]) != 0)
return false; // already encrypted (unused PK4 field, zero)
EncryptPK();
return true;
}
private void EncryptPK()
{
var span = Data.Slice(8, PokeCrypto.SIZE_4PARTY);
var ekdata = PokeCrypto.EncryptArray45(span);
ekdata.CopyTo(span);
}
public GiftType4 GiftType { get => (GiftType4)CardType; set => CardType = (byte)value; }
public GiftSubType4 GiftSubType { get => (GiftSubType4)ItemID; set => ItemID = (int)value; }
public PoketchApp PoketchApp { get => (PoketchApp)ItemID; set => ItemID = (int)value; }

View File

@ -39,13 +39,6 @@ public override int CardID
set => WriteUInt16LittleEndian(Data[0x8..], (ushort)value);
}
public bool IsHOMEGift => CardID >= 9000;
public int HomeBaseIV => CardID switch
{
>= 9031 and <= 9033 => 20,
_ => 0,
};
public byte RestrictVersion { get => Data[0xE]; set => Data[0xE] = value; } // 0x01 = ZA only (only one game in this Context, so always ZA).
public byte CardFlags { get => Data[0x10]; set => Data[0x10] = value; }
public GiftType CardType { get => (GiftType)Data[0x11]; set => Data[0x11] = (byte)value; }
@ -309,7 +302,7 @@ public bool CanBeAnyLanguage()
public bool CanHaveLanguage(int language)
{
if (language is < (int)LanguageID.Japanese or > (int)LanguageID.SpanishL)
if (language is < (int)LanguageID.Japanese or > (int)LanguageID.ChineseT)
return false;
if (CanBeAnyLanguage())
@ -333,7 +326,7 @@ public bool CanHaveLanguage(int language)
private static int GetLanguageIndex(int language)
{
var lang = (LanguageID) language;
if (lang is < LanguageID.Japanese or LanguageID.UNUSED_6 or > LanguageID.SpanishL)
if (lang is < LanguageID.Japanese or LanguageID.UNUSED_6 or > LanguageID.ChineseT)
return (int) LanguageID.English; // fallback
return lang < LanguageID.UNUSED_6 ? language - 1 : language - 2;
}
@ -367,7 +360,7 @@ public override string OriginalTrainerName
get => GetOT(Language);
set
{
for (int i = 1; i <= (int)LanguageID.SpanishL; i++)
for (int i = 1; i <= (int)LanguageID.ChineseT; i++)
SetOT(i, value);
}
}
@ -470,6 +463,16 @@ public override PA9 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.IsNicknamed = GetIsNicknamed(language);
pk.Nickname = pk.IsNicknamed ? GetNickname(language) : SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation);
// No ribbons set.
// for (var i = 0; i < RibbonBytesCount; i++)
// {
// var ribbon = GetRibbonAtIndex(i);
// if (ribbon == RibbonByteNone)
// continue;
// pk.SetRibbon(ribbon);
// pk.AffixedRibbon = (sbyte)ribbon;
// }
SetPINGA(pk, criteria, pi);
SetMoves(currentLevel, pk, pi);
pk.HealPP();
@ -478,18 +481,6 @@ public override PA9 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
SetEggMetData(pk);
pk.CurrentFriendship = pk.IsEgg ? pi.HatchCycles : pi.BaseFriendship;
if (IsHOMEGift)
{
for (var i = 0; i < RibbonBytesCount; i++)
{
var ribbon = GetRibbonAtIndex(i);
if (ribbon == RibbonByteNone)
continue;
pk.SetRibbon(ribbon);
}
pk.Scale = pk.HeightScalar = pk.WeightScalar = Scale == 256 ? (byte)rnd.Next(256) : (byte)Scale;
}
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
@ -524,17 +515,6 @@ private void SetEggMetData(PA9 pk)
}
private void SetPINGA(PA9 pk, EncounterCriteria criteria, PersonalInfo9ZA pi)
{
if (IsHOMEGift) // Do not use LumioseRNG for HOME gifts
{
pk.Nature = pk.StatNature = criteria.GetNature((sbyte)Nature == -1 ? Nature.Random : Nature);
pk.Gender = criteria.GetGender(Gender, pi);
var av = GetAbilityIndex(criteria, AbilityType);
pk.RefreshAbility(av);
SetPID(pk);
SetIVs(pk);
}
else
{
var param = GetParams(pi);
ulong init = Util.Rand.Rand64();
@ -545,14 +525,6 @@ private void SetPINGA(PA9 pk, EncounterCriteria criteria, PersonalInfo9ZA pi)
if (PIDType is not (ShinyType8.Never or ShinyType8.Random))
pk.PID = GetPID(pk, PIDType);
}
}
private int GetAbilityIndex(in EncounterCriteria criteria, int type) => type switch
{
00 or 01 or 02 => type, // Fixed 0/1/2
03 or 04 => criteria.GetAbilityFromNumber(Ability), // 0/1 or 0/1/H
_ => throw new ArgumentOutOfRangeException(nameof(type)),
};
public override AbilityPermission Ability => AbilityType switch
{
@ -592,39 +564,6 @@ private static uint GetAntishiny(ITrainerID32 tr)
return pid;
}
private void SetPID(PA9 pk)
{
pk.PID = GetPID(pk, PIDType);
}
private void SetIVs(PA9 pk)
{
Span<int> finalIVs = stackalloc int[6];
GetIVs(finalIVs);
var ivflag = finalIVs.IndexOfAny(0xFC, 0xFD, 0xFE);
var rng = Util.Rand;
if (ivflag == -1) // Random IVs
{
for (int i = 0; i < finalIVs.Length; i++)
{
if (finalIVs[i] > 31)
finalIVs[i] = rng.Next(32);
}
}
else // 1/2/3 perfect IVs
{
int IVCount = finalIVs[ivflag] - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < finalIVs.Length; i++)
{
if (finalIVs[i] != 31)
finalIVs[i] = IsHOMEGift ? HomeBaseIV : rng.Next(32); // HOME ZA-starters gifts have 20 in non-perfect IVs
}
}
pk.SetIVs(finalIVs);
}
public override bool IsMatchExact(PKM pk, EvoCriteria evo)
{
if (!IsEgg)
@ -687,7 +626,7 @@ public override bool IsMatchExact(PKM pk, EvoCriteria evo)
if (MetLevel != 0 && MetLevel != pk.MetLevel) return false;
if ((Ball == 0 ? 4 : Ball) != pk.Ball) return false;
if (OTGender < 2 && OTGender != pk.OriginalTrainerGender) return false;
if (Nature.IsFixed && pk.Nature != Nature) return false;
if (Nature != Nature.Random && pk.Nature != Nature) return false;
if (Gender != 3 && Gender != pk.Gender) return false;
if (pk is IScaledSize s)
@ -702,20 +641,6 @@ public override bool IsMatchExact(PKM pk, EvoCriteria evo)
if (pk is IAlphaReadOnly a && a.IsAlpha != IsAlpha)
return true;
if (IsHOMEGift)
{
if (pk.FlawlessIVCount != FlawlessIVCount)
return false; // HOME ZA-starters have non-perfect IVs to 20, so IVs at 31 can't exceed the flawless count.
Span<int> IVs = stackalloc int[6];
pk.GetIVs(IVs);
foreach (var iv in IVs)
{
if (iv != 31 && iv != HomeBaseIV)
return false;
}
}
// PID Types 0 and 1 do not use the fixed PID value.
// Values 2,3 are specific shiny states, and 4 is fixed value.
// 2,3,4 can change if it is a traded egg to ensure the same shiny state.
@ -751,11 +676,11 @@ private bool IsMatchTrainerName(ReadOnlySpan<byte> trainerTrash, PKM pk)
protected override bool IsMatchDeferred(PKM pk) => false;
protected override bool IsMatchPartial(PKM pk) => !IsHOMEGift && TryGetSeed(pk, out _) != SeedCorrelationResult.Success;
protected override bool IsMatchPartial(PKM pk) => TryGetSeed(pk, out _) != SeedCorrelationResult.Success;
#region Lazy Ribbon Implementation
private bool HasRibbon(RibbonIndex index) => IsHOMEGift && this.GetRibbonIndex(index);
private static bool HasRibbon(RibbonIndex _) => false; // HasRibbon(index); // ZA is hard-coded to never set ribbons, so we need to return false for validation/setting.
public bool RibbonEarth { get => HasRibbon(Earth); set => this.SetRibbonIndex(Earth, value); }
public bool RibbonNational { get => HasRibbon(National); set => this.SetRibbonIndex(National, value); }
public bool RibbonCountry { get => HasRibbon(Country); set => this.SetRibbonIndex(Country, value); }

View File

@ -581,17 +581,7 @@ public override bool IsMatchExact(PKM pk, EvoCriteria evo)
}
if (Form != evo.Form && !FormInfo.IsFormChangeable(Species, Form, pk.Form, Context, pk.Context))
{
// Small bypass for Greninja-Ash when mega evolved (normal and Ash map to the same Mega-3, managed by game)
if (this is { Species: (ushort)Core.Species.Greninja, Form: 1 } && pk is { Context: EntityContext.Gen9a, Form: 3 })
{
// Allow
}
else
{
return false;
}
}
if (IsEgg)
{

Some files were not shown because too many files have changed in this diff Show More