mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-03-22 01:55:10 -05:00
Compare commits
No commits in common. "master" and "26.02.27" have entirely different histories.
2
.github/README-it.md
vendored
2
.github/README-it.md
vendored
|
|
@ -9,7 +9,7 @@ Supporta i seguenti tipi di file:
|
|||
* File di Memory Card GameCube (\*.raw, \*.bin) contenenti File di Salvataggio Pokémon.
|
||||
* File di Entità Pokémon individuali (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
|
||||
* File di Dono Segreto (\*.pgt, \*.pcd, \*.pgf, .wc\*) inclusa conversione in .pk\*
|
||||
* Importazione di Entità del GO Park (\*.gp1) inclusa conversione in .pb7
|
||||
* Importazione di Entità del Go Park (\*.gp1) inclusa conversione in .pb7
|
||||
* Importazione di squadre da Video Lotta del 3DS decriptati
|
||||
* Trasferimento da una generazione all'altra, convertendo i formati propriamente.
|
||||
|
||||
|
|
|
|||
2
.github/README-zh-Hans.md
vendored
2
.github/README-zh-Hans.md
vendored
|
|
@ -9,7 +9,7 @@ PKHeX
|
|||
* GameCube 宝可梦游戏存档包含 GameCube 记忆存档 (\*.raw, \*.bin)
|
||||
* 单个宝可梦实体文件 (.pk\*, \*.ck3, \*.xk3, \*.pb7, \*.sk2, \*.bk4, \*.rk4)
|
||||
* 神秘礼物文件 (\*.pgt, \*.pcd, \*.pgf, .wc\*) 并转换为 .pk\*
|
||||
* 导入 GO Park存档 (\*.gp1) 并转换为 .pb7
|
||||
* 导入 Go Park存档 (\*.gp1) 并转换为 .pb7
|
||||
* 从已破解的 3DS 对战视频中导入队伍
|
||||
* 支持宝可梦在不同世代的间转移,并转换文件格式
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>26.03.20</Version>
|
||||
<Version>26.02.27</Version>
|
||||
<LangVersion>14</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<NeutralLanguage>en</NeutralLanguage>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ public sealed class AdvancedSettings
|
|||
[LocalizedDescription("Folder path that contains dump(s) of block hash-names. If a specific dump file does not exist, only names defined within the program's code will be loaded.")]
|
||||
public string PathBlockKeyList { get; set; } = string.Empty;
|
||||
|
||||
[LocalizedDescription("Hide event variables below this event type value. Removes event values from the GUI that the user doesn't care to view.")]
|
||||
public NamedEventType HideEventTypeBelow { get; set; }
|
||||
|
||||
[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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
||||
|
|
|
|||
|
|
@ -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) { }
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ private static List<SlotInfoMisc> GetExtraSlots4(SAV4 sav)
|
|||
if (sav.GTS > 0)
|
||||
list.Add(new SlotInfoMisc(sav.GeneralBuffer[sav.GTS..], 0) { Type = StorageSlotType.GTS });
|
||||
if (sav is SAV4HGSS hgss)
|
||||
list.Add(new SlotInfoMisc(hgss.GeneralBuffer[SAV4HGSS.WalkerPair..], 1) {Type = StorageSlotType.Pokéwalker});
|
||||
list.Add(new SlotInfoMisc(hgss.GeneralBuffer[SAV4HGSS.WalkerPair..], 1) {Type = StorageSlotType.Misc});
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ private static List<SlotInfoMisc> GetExtraSlots5(SAV5 sav)
|
|||
var list = new List<SlotInfoMisc>
|
||||
{
|
||||
new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS},
|
||||
new(sav.GlobalLink.Upload, 0) { Type = StorageSlotType.PGL },
|
||||
new(sav.GlobalLink.Upload, 0) { Type = StorageSlotType.Misc },
|
||||
|
||||
new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.BattleBox[1], 1) {Type = StorageSlotType.BattleBox},
|
||||
|
|
@ -106,7 +106,7 @@ private static List<SlotInfoMisc> GetExtraSlots6XY(SAV6XY sav)
|
|||
[
|
||||
new(sav.GTS.Upload, 0) {Type = StorageSlotType.GTS},
|
||||
new(sav.Fused[0], 0) {Type = StorageSlotType.FusedKyurem},
|
||||
new(sav.SUBE.GiveSlot, 0, Mutable: true) {Type = StorageSlotType.Scripted}, // Old Man
|
||||
new(sav.SUBE.GiveSlot, 0, Mutable: true) {Type = StorageSlotType.Misc}, // Old Man
|
||||
|
||||
new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.BattleBox[1], 1) {Type = StorageSlotType.BattleBox},
|
||||
|
|
@ -123,7 +123,7 @@ private static List<SlotInfoMisc> GetExtraSlots6AO(SAV6AO sav)
|
|||
[
|
||||
new(sav.GTS.Upload, 0) { Type = StorageSlotType.GTS },
|
||||
new(sav.Fused[0], 0) { Type = StorageSlotType.FusedKyurem },
|
||||
new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Scripted},
|
||||
new(sav.SUBE.GiveSlot, 0) {Type = StorageSlotType.Misc},
|
||||
|
||||
new(sav.BattleBox[0], 0) {Type = StorageSlotType.BattleBox},
|
||||
new(sav.BattleBox[1], 1) {Type = StorageSlotType.BattleBox},
|
||||
|
|
@ -150,9 +150,9 @@ private static List<SlotInfoMisc> GetExtraSlots7(SAV7 sav, bool all)
|
|||
]);
|
||||
list.AddRange(
|
||||
[
|
||||
new SlotInfoMisc(uu.BattleAgency[0], 0) {Type = StorageSlotType.BattleAgency},
|
||||
new SlotInfoMisc(uu.BattleAgency[1], 1) {Type = StorageSlotType.BattleAgency},
|
||||
new SlotInfoMisc(uu.BattleAgency[2], 2) {Type = StorageSlotType.BattleAgency},
|
||||
new SlotInfoMisc(uu.BattleAgency[0], 0) {Type = StorageSlotType.Misc},
|
||||
new SlotInfoMisc(uu.BattleAgency[1], 1) {Type = StorageSlotType.Misc},
|
||||
new SlotInfoMisc(uu.BattleAgency[2], 2) {Type = StorageSlotType.Misc},
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -202,15 +202,15 @@ private static List<SlotInfoMisc> GetExtraSlots8b(SAV8BS sav)
|
|||
{
|
||||
return
|
||||
[
|
||||
new(sav.UgSaveData[0], 0, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[1], 1, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[2], 2, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[3], 3, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[4], 4, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[5], 5, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[6], 6, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[7], 7, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[8], 8, true) { Type = StorageSlotType.Underground, HideLegality = true },
|
||||
new(sav.UgSaveData[0], 0, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
new(sav.UgSaveData[1], 1, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
new(sav.UgSaveData[2], 2, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
new(sav.UgSaveData[3], 3, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
new(sav.UgSaveData[4], 4, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
new(sav.UgSaveData[5], 5, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
new(sav.UgSaveData[6], 6, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
new(sav.UgSaveData[7], 7, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
new(sav.UgSaveData[8], 8, true) { Type = StorageSlotType.Misc, HideLegality = true },
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -239,8 +239,8 @@ private static List<SlotInfoMisc> GetExtraSlots9(SAV9SV sav)
|
|||
|
||||
if (sav.Blocks.TryGetBlock(SaveBlockAccessor9SV.KSurpriseTrade, out var surprise))
|
||||
{
|
||||
list.Add(new(surprise.Raw[0x198..], 0) { Type = StorageSlotType.SurpriseTrade }); // my upload
|
||||
list.Add(new(surprise.Raw[0x02C..], 1) { Type = StorageSlotType.SurpriseTrade }); // received from others
|
||||
list.Add(new(surprise.Raw[0x198..], 0) { Type = StorageSlotType.Misc }); // my upload
|
||||
list.Add(new(surprise.Raw[0x02C..], 1) { Type = StorageSlotType.Misc }); // received from others
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
|
@ -268,7 +268,7 @@ private static List<SlotInfoMisc> GetExtraSlots9a(SAV9ZA sav)
|
|||
var ofs = (i * size) + 8;
|
||||
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 });
|
||||
list.Add(new(entry, i, true, Mutable: true) { Type = StorageSlotType.Misc });
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,106 +7,28 @@ public enum StorageSlotType : byte
|
|||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Originated from Box
|
||||
/// </summary>
|
||||
Box,
|
||||
|
||||
/// <summary>
|
||||
/// Originated from Party
|
||||
/// </summary>
|
||||
Party,
|
||||
|
||||
/// <summary>
|
||||
/// Battle Box
|
||||
/// </summary>
|
||||
/// <summary> Battle Box </summary>
|
||||
BattleBox,
|
||||
|
||||
/// <summary>
|
||||
/// Daycare
|
||||
/// </summary>
|
||||
/// <summary> Daycare </summary>
|
||||
Daycare,
|
||||
|
||||
/// <summary>
|
||||
/// Miscellaneous Origin (usually in-game scripted event recollection)
|
||||
/// </summary>
|
||||
Scripted,
|
||||
|
||||
/// <summary>
|
||||
/// Global Trade Station (GTS)
|
||||
/// </summary>
|
||||
/// <summary> Global Trade Station (GTS) </summary>
|
||||
GTS,
|
||||
|
||||
/// <summary>
|
||||
/// Pokémon Global Link (PGL)
|
||||
/// </summary>
|
||||
PGL,
|
||||
|
||||
/// <summary>
|
||||
/// Surprise Trade Upload/Download
|
||||
/// </summary>
|
||||
SurpriseTrade,
|
||||
|
||||
/// <summary>
|
||||
/// Shiny Overworld Cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="GameVersion.ZA"/>
|
||||
/// </remarks>
|
||||
/// <summary> Shiny Overworld Cache </summary>
|
||||
Shiny,
|
||||
|
||||
/// <summary>
|
||||
/// Underground area wild Pokémon cache
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="GameVersion.BD"/>
|
||||
/// <see cref="GameVersion.SP"/>
|
||||
/// </remarks>
|
||||
Underground,
|
||||
|
||||
/// <summary>
|
||||
/// Fused Legendary Storage
|
||||
/// </summary>
|
||||
Fused,
|
||||
|
||||
/// <summary>
|
||||
/// Sub-tag for <see cref="Species.Kyurem"/> differentiation.
|
||||
/// </summary>
|
||||
/// <summary> Fused Legendary Storage </summary>
|
||||
FusedKyurem,
|
||||
/// <summary>
|
||||
/// Sub-tag for <see cref="Species.Solgaleo"/> differentiation.
|
||||
/// </summary>
|
||||
FusedNecrozmaS,
|
||||
/// <summary>
|
||||
/// Sub-tag for <see cref="Species.Lunala"/> differentiation.
|
||||
/// </summary>
|
||||
FusedNecrozmaM,
|
||||
/// <summary>
|
||||
/// Sub-tag for <see cref="Species.Calyrex"/> differentiation.
|
||||
/// </summary>
|
||||
FusedCalyrex,
|
||||
|
||||
/// <summary>
|
||||
/// Poké Pelago (Gen7)
|
||||
/// </summary>
|
||||
/// <summary> Miscellaneous </summary>
|
||||
Misc,
|
||||
/// <summary> Poké Pelago (Gen7) </summary>
|
||||
Resort,
|
||||
|
||||
/// <summary>
|
||||
/// Ride Legendary Slot (S/V)
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="GameVersion.SL"/>
|
||||
/// <see cref="GameVersion.VL"/>
|
||||
/// </remarks>
|
||||
/// <summary> Ride Legendary Slot (S/V) </summary>
|
||||
Ride,
|
||||
|
||||
/// <summary>
|
||||
/// Battle Agency (Gen7)
|
||||
/// </summary>
|
||||
BattleAgency,
|
||||
|
||||
/// <summary>
|
||||
/// Gen4 HeartGold/SoulSilver pedometer accessory upload
|
||||
/// </summary>
|
||||
Pokéwalker,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@
|
|||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class PlayerBag3FRLG(bool VC) : PlayerBag, IPlayerBag3
|
||||
public sealed class PlayerBag3FRLG : PlayerBag, IPlayerBag3
|
||||
{
|
||||
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));
|
||||
private const int BaseOffset = 0x0298;
|
||||
|
||||
private static InventoryPouch3[] GetPouches(IItemStorage info) =>
|
||||
public override IReadOnlyList<InventoryPouch3> Pouches { get; } = GetPouches(ItemStorage3FRLG.Instance);
|
||||
public override ItemStorage3FRLG Info => ItemStorage3FRLG.Instance;
|
||||
|
||||
private static InventoryPouch3[] GetPouches(ItemStorage3FRLG info) =>
|
||||
[
|
||||
new(0x078, 42, 999, info, Items),
|
||||
new(0x120, 30, 001, info, KeyItems),
|
||||
|
|
@ -20,15 +21,15 @@ 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(ReadOnlySpan<byte> data, uint security, bool vc) : this(vc)
|
||||
public PlayerBag3FRLG(SAV3FRLG sav) : this(sav.Large[BaseOffset..], sav.SecurityKey) { }
|
||||
public PlayerBag3FRLG(ReadOnlySpan<byte> data, uint security)
|
||||
{
|
||||
UpdateSecurityKey(security);
|
||||
Pouches.LoadAll(data);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public sealed class ItemStorage3Colo : IItemStorage
|
|||
540, 541, 542, 546, 547,
|
||||
];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,16 +13,17 @@ 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,
|
||||
];
|
||||
|
||||
private static readonly ushort[] PCItems = [.. General, .. Berry, .. Balls, .. Machine];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ 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,
|
||||
];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
@ -31,58 +31,3 @@ public sealed class ItemStorage3FRLG : IItemStorage
|
|||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Item storage for <see cref="GameVersion.FR"/> and <see cref="GameVersion.LG"/> on <see cref="GameConsole.NX"/>.
|
||||
/// </summary>
|
||||
public sealed class ItemStorage3FRLG_VC : IItemStorage // TODO VC RSE: delete me and any usages as RSE gives the remainder of items.
|
||||
{
|
||||
public static readonly ItemStorage3FRLG_VC Instance = new();
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => ItemStorage3FRLG.Instance.GetItems(type);
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !IsUnreleasedHeld(itemIndex); // use VC unreleased list
|
||||
|
||||
public static bool IsUnreleasedHeld(int itemIndex) => Unreleased.Contains((ushort)itemIndex);
|
||||
|
||||
private static ReadOnlySpan<ushort> Unreleased =>
|
||||
[
|
||||
// Unobtainable
|
||||
005, // Safari
|
||||
|
||||
// TODO RSE VC: Remove these
|
||||
007, 012, // Dive Ball, Premier Ball (Unobtainable without trading from R/S/E)
|
||||
039, 041, 042, 043, // Flutes (Yellow is obtainable via Coins)
|
||||
|
||||
// Unobtainable
|
||||
044, // Berry Juice
|
||||
|
||||
// TODO RSE VC: Remove these
|
||||
046, 047, // Shoal Salt, Shoal Shell
|
||||
048, 049, 050, 051, // Shards
|
||||
081, // Fluffy Tail
|
||||
121, 122, 123, 124, 125, 126, 127, 128, 129, // Mail
|
||||
168, // Liechi Berry (Mirage Island)
|
||||
|
||||
// Event Berries (Unobtainable)
|
||||
169, // Ganlon Berry (Event)
|
||||
170, // Salac Berry (Event)
|
||||
171, // Petaya Berry (Event)
|
||||
172, // Apicot Berry (Event)
|
||||
173, // Lansat Berry (Event)
|
||||
174, // Starf Berry (Event)
|
||||
175, // Enigma Berry (Event)
|
||||
|
||||
// TODO RSE VC: Remove these
|
||||
179, // BrightPowder
|
||||
180, // White Herb
|
||||
185, // Mental Herb
|
||||
186, // Choice Band
|
||||
191, // Soul Dew
|
||||
192, // DeepSeaTooth
|
||||
193, // DeepSeaScale
|
||||
198, // Scope Lens
|
||||
202, // Light Ball
|
||||
219, // Shell Bell
|
||||
254, 255, 256, 257, 258, 259, // Scarves
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,13 +54,13 @@ public sealed class ItemStorage3RS : IItemStorage
|
|||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||||
];
|
||||
|
||||
internal static ReadOnlySpan<ushort> Unreleased => [005, 044]; // Safari Ball, Berry Juice
|
||||
internal static ReadOnlySpan<ushort> Unreleased => [005]; // Safari Ball
|
||||
|
||||
public static ushort[] GetAllHeld() => [..General, ..Balls, ..Berry, ..MachineOnlyTM];
|
||||
|
||||
private static readonly ushort[] PCItems = [..General, ..Key, .. Berry, ..Balls, ..Machine];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public sealed class ItemStorage3XD : IItemStorage
|
|||
590, 591, 592, 593,
|
||||
];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ public sealed class ItemStorage4DP : ItemStorage4, IItemStorage
|
|||
|
||||
public static ushort[] GetAllHeld() => [..GeneralDP, ..Mail, ..Medicine, ..Berry, ..BallsDPPt, ..Battle, ..Machine[..^8]];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public sealed class ItemStorage4HGSS : ItemStorage4, IItemStorage
|
|||
492, 493, 494, 495, 496, 497, 498, 499, 500, // Apricorn Balls
|
||||
];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public sealed class ItemStorage4Pt : ItemStorage4, IItemStorage
|
|||
|
||||
public static ushort[] GetAllHeld() => [..GeneralPt, ..Mail, ..Medicine, ..Berry, ..BallsDPPt, ..Battle, ..Machine[..^8]];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public sealed class ItemStorage5B2W2 : ItemStorage5, IItemStorage
|
|||
616, 617, 621, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638,
|
||||
];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ public sealed class ItemStorage5BW : ItemStorage5, IItemStorage
|
|||
616, 617, 621, 623, 624, 625, 626,
|
||||
];
|
||||
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => !Unreleased.Contains((ushort)itemIndex);
|
||||
public bool IsLegal(InventoryType type, int itemIndex, int itemCount) => true;
|
||||
|
||||
public ReadOnlySpan<ushort> GetItems(InventoryType type) => type switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -71,19 +71,24 @@ private static EncounterArea3[] GetRegular([ConstantExpected] string resource, [
|
|||
new(147, 18, FR) { FixedBall = Ball.Poke, Location = 94 }, // Dratini
|
||||
new(137, 26, FR) { FixedBall = Ball.Poke, Location = 94 }, // Porygon
|
||||
|
||||
new(386, 30, FR) { Location = 187, FatefulEncounter = true, Form = 1 }, // Deoxys @ Birth Island
|
||||
new(386, 30, FR ) { Location = 187, FatefulEncounter = true, Form = 1 }, // Deoxys @ Birth Island
|
||||
];
|
||||
|
||||
public static readonly EncounterStatic3[] StaticLG =
|
||||
[
|
||||
// Celadon City Game Corner
|
||||
new(063, 09, FR) { FixedBall = Ball.Poke, Location = 94 }, // Abra
|
||||
new(035, 08, FR) { FixedBall = Ball.Poke, Location = 94 }, // Clefairy
|
||||
new(123, 25, FR) { FixedBall = Ball.Poke, Location = 94 }, // Scyther
|
||||
new(147, 18, FR) { FixedBall = Ball.Poke, Location = 94 }, // Dratini
|
||||
new(137, 26, FR) { FixedBall = Ball.Poke, Location = 94 }, // Porygon
|
||||
|
||||
new(063, 07, LG) { FixedBall = Ball.Poke, Location = 94 }, // Abra
|
||||
new(035, 12, LG) { FixedBall = Ball.Poke, Location = 94 }, // Clefairy
|
||||
new(127, 18, LG) { FixedBall = Ball.Poke, Location = 94 }, // Pinsir
|
||||
new(147, 24, LG) { FixedBall = Ball.Poke, Location = 94 }, // Dratini
|
||||
new(137, 18, LG) { FixedBall = Ball.Poke, Location = 94 }, // Porygon
|
||||
|
||||
new(386, 30, LG) { Location = 187, FatefulEncounter = true, Form = 2 }, // Deoxys @ Birth Island
|
||||
new(386, 30, LG) { Location = 187, FatefulEncounter = true, Form = 2 }, // Deoxys @ Birth Island
|
||||
];
|
||||
|
||||
private static ReadOnlySpan<byte> TradeContest_Cool => [ 30, 05, 05, 05, 05, 10 ];
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,16 +58,12 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
|
|||
|
||||
// Force Hatch
|
||||
Language = language,
|
||||
OriginalTrainerName = tr.OT,
|
||||
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, language, Generation),
|
||||
OriginalTrainerFriendship = 120,
|
||||
MetLevel = 0,
|
||||
MetLocation = Location,
|
||||
};
|
||||
// Copy from SaveFile's OT name. Trash bytes here should be pure, but our OT name might not always source from a PK3/SAV3.
|
||||
// Condition the buffer as if it came from a correct SAV3 named after the OT.
|
||||
var ot = pk.OriginalTrainerTrash;
|
||||
ot[..(language == 1 ? 6 : 7)].Fill(0xFF);
|
||||
pk.OriginalTrainerName = EncounterUtil.GetTrainerName(tr, language);
|
||||
|
||||
SetEncounterMoves(pk);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,15 +64,11 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
|
|||
Ball = (byte)GetRequiredBall(Ball.Poke),
|
||||
|
||||
Language = language,
|
||||
OriginalTrainerName = tr.OT,
|
||||
OriginalTrainerGender = tr.Gender,
|
||||
ID32 = tr.ID32,
|
||||
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, language, Generation),
|
||||
};
|
||||
// Copy from SaveFile's OT name. Trash bytes here should be pure, but our OT name might not always source from a PK3/SAV3.
|
||||
// Condition the buffer as if it came from a correct SAV3 named after the OT.
|
||||
var ot = pk.OriginalTrainerTrash;
|
||||
ot[..(language == 1 ? 6 : 7)].Fill(0xFF);
|
||||
pk.OriginalTrainerName = EncounterUtil.GetTrainerName(tr, language);
|
||||
|
||||
SetPINGA(pk, criteria, pi);
|
||||
SetEncounterMoves(pk);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public sealed record EncounterStatic3(ushort Species, byte Level, GameVersion Ve
|
|||
|
||||
public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
|
||||
{
|
||||
int language = GetTemplateLanguage(tr);
|
||||
int lang = GetTemplateLanguage(tr);
|
||||
var version = this.GetCompatibleVersion(tr.Version);
|
||||
var pi = PersonalTable.E[Species];
|
||||
var pk = new PK3
|
||||
|
|
@ -56,16 +56,12 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
|
|||
Ball = (byte)(FixedBall != Ball.None ? FixedBall : Ball.Poke),
|
||||
FatefulEncounter = FatefulEncounter,
|
||||
|
||||
Language = language,
|
||||
Language = lang,
|
||||
OriginalTrainerName = EncounterUtil.GetTrainerName(tr, lang),
|
||||
OriginalTrainerGender = tr.Gender,
|
||||
ID32 = tr.ID32,
|
||||
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, language, Generation),
|
||||
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
|
||||
};
|
||||
// Copy from SaveFile's OT name. Trash bytes here should be pure, but our OT name might not always source from a PK3/SAV3.
|
||||
// Condition the buffer as if it came from a correct SAV3 named after the OT.
|
||||
var ot = pk.OriginalTrainerTrash;
|
||||
ot[..(language == 1 ? 6 : 7)].Fill(0xFF);
|
||||
pk.OriginalTrainerName = EncounterUtil.GetTrainerName(tr, language);
|
||||
|
||||
if (IsEgg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,8 @@ public static class EncounterVerifier
|
|||
private static CheckResult VerifyEncounter(PKM pk, IEncounterTemplate enc) => enc switch
|
||||
{
|
||||
EncounterShadow3Colo { IsEReader: true } when pk.Language != (int)LanguageID.Japanese => GetInvalid(G3EReader),
|
||||
|
||||
// Mew @ Faraway Island (Emerald)
|
||||
EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese
|
||||
=> GetInvalid(EncUnreleasedEMewJP),
|
||||
// Deoxys @ Birth Island (FireRed/LeafGreen) - Never distributed in Japan during GBA Cart era. NX virtual console added for all.
|
||||
EncounterStatic3 { Species: (int)Species.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese && !ParseSettings.AllowGen3EventTicketsAll(pk)
|
||||
=> GetInvalid(EncUnreleased),
|
||||
|
||||
EncounterStatic3 { Species: (int)Species.Mew } when pk.Language != (int)LanguageID.Japanese => GetInvalid(EncUnreleasedEMewJP),
|
||||
EncounterStatic3 { Species: (int)Species.Deoxys, Location: 200 } when pk.Language == (int)LanguageID.Japanese => GetInvalid(EncUnreleased),
|
||||
EncounterStatic4 { Species: (int)Species.Shaymin } when pk.Language == (int)LanguageID.Korean => GetInvalid(EncUnreleased),
|
||||
EncounterStatic4 { IsRoaming: true } when pk is G4PKM { MetLocation: 193, GroundTile: GroundTileType.Water } => GetInvalid(G4InvalidTileR45Surf),
|
||||
MysteryGift g => VerifyEncounterEvent(pk, g),
|
||||
|
|
@ -152,8 +146,6 @@ private static CheckResult VerifyEncounterEgg3(PKM pk)
|
|||
return GetValid(EggLocation);
|
||||
|
||||
// Version isn't updated when hatching on a different game. Check any game.
|
||||
if (!ParseSettings.AllowGBACrossTransferRSE(pk)) // Must match the origin game (Nintendo Switch VC)
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
if (EggHatchLocation3.IsValidMet3Any(met))
|
||||
return GetValid(EggLocationTrade);
|
||||
return GetInvalid(EggLocationInvalid);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ public sealed class EvolutionGroup3 : IEvolutionGroup
|
|||
private static PersonalTable3 Personal => PersonalTable.E;
|
||||
private static EvolutionRuleTweak Tweak => EvolutionRuleTweak.Default;
|
||||
|
||||
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup4.Instance : null; // TODO HOME FR/LG
|
||||
public IEvolutionGroup? GetNext(PKM pk, EvolutionOrigin enc) => pk.Format > Generation ? EvolutionGroup4.Instance : null;
|
||||
public IEvolutionGroup? GetPrevious(PKM pk, EvolutionOrigin enc) => null;
|
||||
public void DiscardForOrigin(Span<EvoCriteria> result, PKM pk, EvolutionOrigin enc) => EvolutionUtil.Discard(result, Personal);
|
||||
|
||||
|
|
|
|||
|
|
@ -94,12 +94,6 @@ private static void CheckEncounterMoves(Span<MoveResult> result, ReadOnlySpan<us
|
|||
|
||||
private static void Check(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, EvoCriteria evo, int stage, MoveSourceType types)
|
||||
{
|
||||
if (!ParseSettings.AllowGBACrossTransferRSE(pk))
|
||||
{
|
||||
CheckNX(result, current, pk, evo, stage, types);
|
||||
return;
|
||||
}
|
||||
|
||||
var rs = LearnSource3RS.Instance;
|
||||
var species = evo.Species;
|
||||
if (!rs.TryGetPersonal(species, evo.Form, out var rp))
|
||||
|
|
@ -143,48 +137,6 @@ private static void Check(Span<MoveResult> result, ReadOnlySpan<ushort> current,
|
|||
}
|
||||
}
|
||||
|
||||
private static void CheckNX(Span<MoveResult> result, ReadOnlySpan<ushort> current, PKM pk, EvoCriteria evo, int stage, MoveSourceType types)
|
||||
{
|
||||
var species = evo.Species;
|
||||
var fr = LearnSource3FR.Instance;
|
||||
if (!fr.TryGetPersonal(species, evo.Form, out var fp))
|
||||
return; // should never happen.
|
||||
var lg = LearnSource3LG.Instance;
|
||||
var lp = lg[species];
|
||||
|
||||
var isFireRed = pk.Version == GameVersion.FR;
|
||||
ILearnSource<PersonalInfo3> primaryLS = isFireRed ? fr : lg;
|
||||
ILearnSource<PersonalInfo3> secondaryLS = !isFireRed ? fr : lg;
|
||||
var primaryPI = isFireRed ? fp : lp;
|
||||
var secondaryPI = !isFireRed ? fp : lp;
|
||||
var primaryEnv = isFireRed ? LearnEnvironment.FR : LearnEnvironment.LG;
|
||||
|
||||
for (int i = result.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (result[i].Valid)
|
||||
continue;
|
||||
|
||||
// Level Up moves are different for each game, but TM/HM is shared.
|
||||
var move = current[i];
|
||||
var chk = primaryLS.GetCanLearn(pk, primaryPI, evo, move, types);
|
||||
if (chk != default)
|
||||
{
|
||||
result[i] = new(chk, (byte)stage, Context);
|
||||
continue;
|
||||
}
|
||||
chk = secondaryLS.GetCanLearn(pk, secondaryPI, evo, move, types & MoveSourceType.LevelUp); // Tutors same as FR
|
||||
if (chk != default)
|
||||
{
|
||||
result[i] = new(chk, (byte)stage, Context);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Tutors are indexes 0-14, same as Emerald.
|
||||
if (LearnSource3E.GetIsTutorFRLG(evo.Species, move))
|
||||
result[i] = new(new(LearnMethod.Tutor, primaryEnv));
|
||||
}
|
||||
}
|
||||
|
||||
public void GetAllMoves(Span<bool> result, PKM pk, EvolutionHistory history, IEncounterTemplate enc, MoveSourceType types = MoveSourceType.All, LearnOption option = LearnOption.Current)
|
||||
{
|
||||
if (types.HasFlag(MoveSourceType.Encounter) && enc.Context == Context)
|
||||
|
|
@ -206,16 +158,6 @@ public void GetAllMoves(Span<bool> result, PKM pk, EvolutionHistory history, IEn
|
|||
|
||||
private static void GetAllMoves(Span<bool> result, PKM pk, EvoCriteria evo, MoveSourceType types)
|
||||
{
|
||||
if (!ParseSettings.AllowGBACrossTransferRSE(pk)) // NX
|
||||
{
|
||||
LearnSource3FR.Instance.GetAllMoves(result, pk, evo, types);
|
||||
LearnSource3LG.Instance.GetAllMoves(result, pk, evo, types & (MoveSourceType.LevelUp));
|
||||
|
||||
// Tutors are indexes 0-14, same as Emerald.
|
||||
if (types.HasFlag(MoveSourceType.EnhancedTutor))
|
||||
LearnSource3E.GetAllTutorMovesFRLG(result, evo.Species);
|
||||
return;
|
||||
}
|
||||
LearnSource3E.Instance.GetAllMoves(result, pk, evo, types);
|
||||
LearnSource3RS.Instance.GetAllMoves(result, pk, evo, types & (MoveSourceType.LevelUp | MoveSourceType.AllTutors));
|
||||
LearnSource3FR.Instance.GetAllMoves(result, pk, evo, types & (MoveSourceType.LevelUp | MoveSourceType.AllTutors));
|
||||
|
|
|
|||
|
|
@ -144,33 +144,10 @@ public void GetAllMoves(Span<bool> result, PKM pk, EvoCriteria evo, MoveSourceTy
|
|||
}
|
||||
}
|
||||
|
||||
// TODO RSE VC: Remove these.
|
||||
internal static bool GetIsTutorFRLG(ushort species, ushort move)
|
||||
{
|
||||
var info = Personal[species];
|
||||
var index = Tutor_E.IndexOf(move);
|
||||
if ((uint)index >= 15)
|
||||
return false;
|
||||
return info.TypeTutors[index];
|
||||
}
|
||||
|
||||
internal static void GetAllTutorMovesFRLG(Span<bool> result, ushort species)
|
||||
{
|
||||
var pi = Personal[species];
|
||||
var flags = pi.TypeTutors;
|
||||
var moves = Tutor_E;
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
if (flags[i])
|
||||
result[moves[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<ushort> Tutor_E =>
|
||||
[
|
||||
// Introduced in FR/LG
|
||||
005, 014, 025, 034, 038, 068, 069, 102, 118, 135, 138, 086, 153, 157, 164,
|
||||
// Introduced in Emerald
|
||||
223, 205, 244, 173, 196, 203, 189, 008, 207, 214, 129, 111, 009, 007, 210,
|
||||
005, 014, 025, 034, 038, 068, 069, 102, 118, 135,
|
||||
138, 086, 153, 157, 164, 223, 205, 244, 173, 196,
|
||||
203, 189, 008, 207, 214, 129, 111, 009, 007, 210,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
|
|
@ -20,8 +19,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;
|
||||
|
|
@ -304,40 +302,4 @@ public static bool GetIsFixedIVSequenceValidNoRand(in IndividualValueSet IVs, PK
|
|||
if (IVs.SPD != pk.IV_SPD) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For a species with a potentially valid FR/LG origin encounter, flag if not permitted.
|
||||
/// </summary>
|
||||
public static bool IsForeignFRLG(ushort species) => IsForeign(ForeignFRLG, species, ShiftFRLG);
|
||||
|
||||
private static bool IsForeign(ReadOnlySpan<byte> bitSet, int species, [ConstantExpected] int shift)
|
||||
{
|
||||
species -= shift;
|
||||
var offset = species >> 3;
|
||||
if ((uint)offset >= bitSet.Length)
|
||||
return false;
|
||||
|
||||
var bit = species & 7;
|
||||
if ((bitSet[offset] & (1 << bit)) != 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private const ushort ShiftFRLG = 151; // First unavailable Species (Mew)
|
||||
|
||||
/// <summary>
|
||||
/// Bitset representing species that are considered unobtainable in FR/LG.
|
||||
/// Includes foreign transfers and time-of-day evolutions.
|
||||
/// First species is Mew (151), last is Deoxys (386).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Source: https://www.serebii.net/fireredleafgreen/unobtainable.shtml
|
||||
/// </remarks>
|
||||
private static ReadOnlySpan<byte> ForeignFRLG =>
|
||||
[
|
||||
0xFF, 0x33, 0x18, 0x60, 0x04, 0x63, 0x50, 0x0D,
|
||||
0x84, 0x40, 0x00, 0x04, 0xF0, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0x07,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -421,9 +421,8 @@ public sealed class LegalityCheckLocalization
|
|||
|
||||
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 TrashBytesMissingTerminator { 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
|
||||
|
||||
|
|
|
|||
|
|
@ -410,9 +410,8 @@ public static class LegalityCheckResultCodeExtensions
|
|||
TransferTrackerShouldBeZero => localization.TransferTrackerShouldBeZero,
|
||||
TrashBytesExpected => localization.TrashBytesExpected,
|
||||
TrashBytesMismatchInitial => localization.TrashBytesMismatchInitial,
|
||||
TrashBytesMissingTerminatorFinal => localization.TrashBytesMissingTerminatorFinal,
|
||||
TrashBytesMissingTerminator => localization.TrashBytesMissingTerminator,
|
||||
TrashBytesShouldBeEmpty => localization.TrashBytesShouldBeEmpty,
|
||||
TrashBytesResetViaTransfer => localization.TrashBytesResetViaTransfer,
|
||||
WordFilterInvalidCharacter_0 => localization.WordFilterInvalidCharacter_0,
|
||||
WordFilterFlaggedPattern_01 => localization.WordFilterFlaggedPattern_01,
|
||||
WordFilterTooManyNumbers_0 => localization.WordFilterTooManyNumbers_0,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -31,17 +31,7 @@ public static class ParseSettings
|
|||
/// Setting to specify if an analysis should permit data sourced from the physical cartridge era of Game Boy games.
|
||||
/// </summary>
|
||||
/// <remarks>If false, indicates to use Virtual Console rules (which are transferable to Gen7+)</remarks>
|
||||
public static bool AllowEraCartGB { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting to specify if an analysis should permit data sourced from the physical cartridge era of Game Boy Advance games.
|
||||
/// </summary>
|
||||
public static bool AllowEraCartGBA { private get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Setting to specify if an analysis should permit data sourced from the Nintendo Switch Virtual Console era of Game Boy Advance games.
|
||||
/// </summary>
|
||||
public static bool AllowEraSwitchGBA { private get; set; }
|
||||
public static bool AllowGBCartEra { private get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting to specify if an analysis should permit trading a Generation 1 origin file to Generation 2, then back. Useful for checking RBY Metagame rules.
|
||||
|
|
@ -65,35 +55,27 @@ public static class ParseSettings
|
|||
/// <returns>True if Crystal data is allowed</returns>
|
||||
public static bool AllowGen2MoveReminder(PKM pk) => !pk.Korean && AllowGBStadium2;
|
||||
|
||||
public static bool AllowGen2OddEgg(PKM pk) => !pk.Japanese || AllowEraCartGB;
|
||||
public static bool AllowGen2OddEgg(PKM pk) => !pk.Japanese || AllowGBCartEra;
|
||||
|
||||
public static bool AllowGBVirtualConsole3DS => !AllowEraCartGB;
|
||||
public static bool AllowGBEraEvents => AllowEraCartGB;
|
||||
public static bool AllowGBStadium2 => AllowEraCartGB;
|
||||
|
||||
// This logic will likely need to change (format check): TODO HOME FR/LG
|
||||
public static bool AllowGBACrossTransferXD(PKM pk) => AllowEraCartGBA;
|
||||
public static bool AllowGBACrossTransferRSE(PKM pk) => AllowEraCartGBA;
|
||||
public static bool AllowGen3EventTicketsAll(PKM pk) => AllowEraSwitchGBA;
|
||||
public static bool AllowGBVirtualConsole3DS => !AllowGBCartEra;
|
||||
public static bool AllowGBEraEvents => AllowGBCartEra;
|
||||
public static bool AllowGBStadium2 => AllowGBCartEra;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes certain settings
|
||||
/// </summary>
|
||||
/// <param name="sav">Newly loaded save file</param>
|
||||
/// <returns>Save file is Physical GB cartridge save file (not Virtual Console)</returns>
|
||||
public static void InitFromSaveFileData(SaveFile sav)
|
||||
public static bool InitFromSaveFileData(SaveFile sav)
|
||||
{
|
||||
ActiveTrainer = sav;
|
||||
AllowEraCartGB = sav switch
|
||||
return AllowGBCartEra = sav switch
|
||||
{
|
||||
SAV1 { IsVirtualConsole: true } => false,
|
||||
SAV2 { IsVirtualConsole: true } => false,
|
||||
{ Generation: 1 or 2 } => true,
|
||||
_ => false,
|
||||
};
|
||||
var isVirtual3 = sav is SAV3 { IsVirtualConsole: true };
|
||||
AllowEraSwitchGBA = isVirtual3;
|
||||
AllowEraCartGBA = !isVirtual3; // sav.Generation >= 8; TODO HOME FR/LG
|
||||
}
|
||||
|
||||
internal static bool IgnoreTransferIfNoTracker => Settings.HOMETransfer.HOMETransferTrackerNotPresent == Severity.Invalid;
|
||||
|
|
|
|||
|
|
@ -363,13 +363,10 @@ public enum LegalityCheckResultCode : ushort
|
|||
TransferEncryptGen6Xor,
|
||||
TransferTrackerMissing,
|
||||
TransferTrackerShouldBeZero,
|
||||
|
||||
// Trash Bytes
|
||||
TrashBytesExpected,
|
||||
TrashBytesMismatchInitial,
|
||||
TrashBytesMissingTerminatorFinal,
|
||||
TrashBytesMissingTerminator,
|
||||
TrashBytesShouldBeEmpty,
|
||||
TrashBytesResetViaTransfer,
|
||||
|
||||
// Bulk Cross-Comparison
|
||||
BulkCloneDetectedDetails,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public override void Verify(LegalityAnalysis data)
|
|||
|
||||
// If no stats have been increased from the initial amount, then we're done here.
|
||||
// some encounters have contest stats built in. they're already checked by the initial encounter match.
|
||||
if (!s.HasContestStats() || data.EncounterOriginal is IContestStatsReadOnly ro && ro.IsContestEqual(s))
|
||||
if (!s.HasContestStats())
|
||||
return;
|
||||
|
||||
// Check the correlation of Stats & Sheen!
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ public void SetMaxContestStats(IEncounterTemplate enc, EvolutionHistory h)
|
|||
|
||||
public static ContestStatGranting GetContestStatRestriction(PKM pk, byte origin, EvolutionHistory h) => origin switch
|
||||
{
|
||||
3 when pk.Format == 3 && !ParseSettings.AllowGBACrossTransferRSE(pk) => None,
|
||||
3 => pk.Format < 6 ? CorrelateSheen : Mixed,
|
||||
4 => pk.Format < 6 ? CorrelateSheen : Mixed,
|
||||
|
||||
|
|
|
|||
|
|
@ -14,12 +14,6 @@ internal void Verify(LegalityAnalysis data, PKM pk)
|
|||
if (pk.Format == 1) // not stored in Gen1 format
|
||||
return;
|
||||
|
||||
if (pk.Format == 3 && !ParseSettings.AllowGBACrossTransferRSE(pk))
|
||||
{
|
||||
VerifyNone(data, pk);
|
||||
return;
|
||||
}
|
||||
|
||||
var strain = pk.PokerusStrain;
|
||||
var days = pk.PokerusDays;
|
||||
var enc = data.Info.EncounterMatch;
|
||||
|
|
@ -28,14 +22,4 @@ internal void Verify(LegalityAnalysis data, PKM pk)
|
|||
if (!Pokerus.IsDurationValid(strain, days, out var max))
|
||||
data.AddLine(GetInvalid(PokerusDaysLEQ_0, (ushort)max));
|
||||
}
|
||||
|
||||
private void VerifyNone(LegalityAnalysis data, PKM pk)
|
||||
{
|
||||
var strain = pk.PokerusStrain;
|
||||
var days = pk.PokerusDays;
|
||||
if (strain != 0)
|
||||
data.AddLine(GetInvalid(PokerusStrainUnobtainable_0, (ushort)strain));
|
||||
if (days != 0)
|
||||
data.AddLine(GetInvalid(PokerusDaysLEQ_0, 0));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,277 +0,0 @@
|
|||
using System;
|
||||
using static PKHeX.Core.LegalityCheckResultCode;
|
||||
using static PKHeX.Core.CheckIdentifier;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public sealed class MiscVerifierG3 : Verifier
|
||||
{
|
||||
protected override CheckIdentifier Identifier => Misc;
|
||||
|
||||
public override void Verify(LegalityAnalysis data)
|
||||
{
|
||||
if (data.Entity is G3PKM pk)
|
||||
Verify(data, pk);
|
||||
}
|
||||
|
||||
internal void Verify(LegalityAnalysis data, G3PKM pk)
|
||||
{
|
||||
VerifyTrash(data, pk);
|
||||
|
||||
if (ParseSettings.AllowGBACrossTransferRSE(pk))
|
||||
return;
|
||||
|
||||
// Notes:
|
||||
// Nicknamed by player: all FF'd trash.
|
||||
// In-game trade: clean 00'd with a single FF on each.
|
||||
|
||||
// Only FR/LG are released. Only can originate from FR/LG.
|
||||
if (pk.Version is not (GameVersion.FR or GameVersion.LG))
|
||||
data.AddLine(GetInvalid(TradeNotAvailable));
|
||||
else if (Legal.IsForeignFRLG(pk.Species))
|
||||
data.AddLine(GetInvalid(TradeNotAvailable));
|
||||
|
||||
if (ItemStorage3FRLG_VC.IsUnreleasedHeld(pk.HeldItem))
|
||||
data.AddLine(GetInvalid(ItemUnreleased));
|
||||
|
||||
if ((Ball)pk.Ball is Ball.Dive or Ball.Premier)
|
||||
data.AddLine(GetInvalid(BallUnavailable));
|
||||
}
|
||||
|
||||
private void VerifyTrash(LegalityAnalysis data, G3PKM pk)
|
||||
{
|
||||
if (pk is PK3 pk3)
|
||||
VerifyTrash(data, pk3);
|
||||
else
|
||||
VerifyTrashCXD(data, pk);
|
||||
}
|
||||
|
||||
private static void VerifyTrashCXD(LegalityAnalysis data, G3PKM pk)
|
||||
{
|
||||
// Buffers should be entirely clean.
|
||||
var ot = pk.OriginalTrainerTrash;
|
||||
var result = TrashBytesUTF16.IsTrashNone(ot);
|
||||
if (result.IsInvalid)
|
||||
data.AddLine(Get(Trainer, Severity.Invalid, TrashBytesShouldBeEmpty));
|
||||
|
||||
var nick = pk.NicknameTrash;
|
||||
result = TrashBytesUTF16.IsTrashNone(nick);
|
||||
if (result.IsInvalid)
|
||||
data.AddLine(Get(Nickname, Severity.Invalid, TrashBytesShouldBeEmpty));
|
||||
}
|
||||
|
||||
private void VerifyTrash(LegalityAnalysis data, PK3 pk)
|
||||
{
|
||||
if (!pk.IsEgg && TrashByteRules3.IsResetTrash(pk))
|
||||
{
|
||||
data.AddLine(GetValid(TrashBytesResetViaTransfer));
|
||||
return; // OK
|
||||
}
|
||||
|
||||
var enc = data.EncounterOriginal;
|
||||
if (enc is EncounterTrade3)
|
||||
VerifyTrashTrade(data, pk);
|
||||
else if (enc is EncounterGift3 g3)
|
||||
VerifyTrashEvent3(data, pk, g3);
|
||||
else if (enc is EncounterGift3JPN jp)
|
||||
VerifyTrashEvent3(data, pk, jp);
|
||||
else if (enc is EncounterGift3NY ny)
|
||||
VerifyTrashEvent3(data, pk, ny);
|
||||
else if (pk.Japanese && !(pk.IsEgg && pk.OriginalTrainerTrash[^1] == 0xFF))
|
||||
VerifyTrashJPN(data, pk);
|
||||
else
|
||||
VerifyTrashINT(data, pk);
|
||||
}
|
||||
|
||||
private static void VerifyTrashEvent3(LegalityAnalysis data, PK3 pk, EncounterGift3NY ny)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
private static void VerifyTrashEvent3(LegalityAnalysis data, PK3 pk, EncounterGift3JPN jp)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
private static void VerifyTrashEvent3(LegalityAnalysis data, PK3 pk, EncounterGift3 g3)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
private static void VerifyTrashTrade(LegalityAnalysis data, PK3 pk)
|
||||
{
|
||||
if (!TrashByteRules3.IsTerminatedZero(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(Trainer, TrashBytesShouldBeEmpty));
|
||||
if (!TrashByteRules3.IsTerminatedZero(pk.NicknameTrash))
|
||||
data.AddLine(GetInvalid(Nickname, TrashBytesShouldBeEmpty));
|
||||
}
|
||||
|
||||
private static void VerifyTrashJPN(LegalityAnalysis data, PK3 pk)
|
||||
{
|
||||
var trash = pk.OriginalTrainerTrash;
|
||||
// OT name from save file is copied byte-for-byte. Byte 7 & 8 are always zero.
|
||||
if (!TrashByteRules3.IsTerminatedFFZero(trash, 6))
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
if (!TrashByteRules3.IsTerminatedFF(nick))
|
||||
data.AddLine(GetInvalid(Trainer, TrashBytesMismatchInitial));
|
||||
}
|
||||
}
|
||||
|
||||
public static class TrashByteRules3
|
||||
{
|
||||
// PK3 stores u8[length] for OT name and Nickname.
|
||||
// Due to how the game initializes the buffer for each, specific patterns in the unused bytes (after the string, within the allocated max buffer) can arise.
|
||||
// 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:
|
||||
// - OT Name: todo
|
||||
// - Nickname: todo
|
||||
// For event encounters directly injected into the save file via GBA multiboot:
|
||||
// - OT Name: todo
|
||||
// - Nickname: todo
|
||||
|
||||
private const byte Terminator = StringConverter3.TerminatorByte;
|
||||
|
||||
public static bool IsResetTrash(PK3 pk3)
|
||||
{
|
||||
if (!ParseSettings.AllowGBACrossTransferXD(pk3))
|
||||
return false;
|
||||
|
||||
if (!IsTerminatedZero(pk3.OriginalTrainerTrash))
|
||||
return false;
|
||||
if (pk3.IsNicknamed)
|
||||
return true;
|
||||
if (!IsTerminatedZero(pk3.NicknameTrash))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsTerminatedZero(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var first = TrashBytes8.GetTerminatorIndex(data);
|
||||
if (first == -1 || first >= data.Length - 1)
|
||||
return true;
|
||||
return !data[(first+1)..].ContainsAnyExcept<byte>(0);
|
||||
}
|
||||
|
||||
public static bool IsTerminatedFF(ReadOnlySpan<byte> data)
|
||||
{
|
||||
var first = TrashBytes8.GetTerminatorIndex(data);
|
||||
if (first == -1 || first >= data.Length - 1)
|
||||
return true;
|
||||
return !data[(first + 1)..].ContainsAnyExcept<byte>(0xFF);
|
||||
}
|
||||
|
||||
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)
|
||||
return true;
|
||||
|
||||
first++;
|
||||
if (first < preFill)
|
||||
{
|
||||
var inner = data[first..preFill];
|
||||
if (inner.ContainsAnyExcept(Terminator))
|
||||
return false;
|
||||
first = preFill;
|
||||
first++;
|
||||
if (first >= data.Length - 1)
|
||||
return true;
|
||||
}
|
||||
return !data[first..].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,
|
||||
};
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ public sealed class MiscVerifier : Verifier
|
|||
private static readonly MiscG1Verifier Gen1 = new();
|
||||
private static readonly MiscEvolutionVerifier Evolution = new();
|
||||
private static readonly MiscVerifierSK2 Stadium2 = new();
|
||||
private static readonly MiscVerifierG3 Gen3 = new();
|
||||
private static readonly MiscVerifierG4 Gen4 = new();
|
||||
private static readonly MiscVerifierPK6 Gen6 = new();
|
||||
private static readonly MiscVerifierPK5 Gen5 = new();
|
||||
|
|
@ -42,7 +41,6 @@ public override void Verify(LegalityAnalysis data)
|
|||
// Verify gimmick data
|
||||
switch (pk)
|
||||
{
|
||||
case G3PKM pk3: Gen3.Verify(data, pk3); break;
|
||||
case G4PKM pk4: Gen4.Verify(data, pk4); break;
|
||||
case PK5 pk5: Gen5.Verify(data, pk5); break;
|
||||
case PK6 pk6: Gen6.Verify(data, pk6); break;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -324,21 +324,6 @@ public static bool GetValidRibbonStateNational(PKM pk, IEncounterTemplate enc)
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the input can receive the <see cref="IRibbonSetEvent3.RibbonEarth"/> ribbon.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If returns true, can have the ribbon. If returns false, must not have the ribbon.
|
||||
/// </remarks>
|
||||
public static bool IsEarthRibbonAllowed(PKM pk, IEncounterTemplate enc)
|
||||
{
|
||||
if (enc.Generation != 3)
|
||||
return false;
|
||||
if (!ParseSettings.AllowGBACrossTransferXD(pk))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max count values the input can receive for the <see cref="IRibbonSetMemory6.RibbonCountMemoryContest"/> and <see cref="IRibbonSetMemory6.RibbonCountMemoryBattle"/> ribbon counts.
|
||||
/// </summary>
|
||||
|
|
@ -375,11 +360,9 @@ public static (byte Contest, byte Battle) GetMaxMemoryCounts(EvolutionHistory ev
|
|||
/// <summary>
|
||||
/// Checks if the input evolution history could have participated in Generation 3 contests.
|
||||
/// </summary>
|
||||
public static bool IsAllowedContest3(EvolutionHistory evos, PKM pk)
|
||||
public static bool IsAllowedContest3(EvolutionHistory evos)
|
||||
{
|
||||
// Any species can enter contests in Gen3.
|
||||
if (!ParseSettings.AllowGBACrossTransferRSE(pk))
|
||||
return false;
|
||||
return evos.HasVisitedGen3;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public void Parse(in RibbonVerifierArguments args, ref RibbonResultList list)
|
|||
if (!r.RibbonEarth)
|
||||
list.Add(Earth, true);
|
||||
}
|
||||
else if (r.RibbonEarth && !RibbonRules.IsEarthRibbonAllowed(args.Entity, enc))
|
||||
else if (r.RibbonEarth && enc.Generation != 3)
|
||||
{
|
||||
list.Add(Earth);
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ public void Parse(in RibbonVerifierArguments args, ref RibbonResultList list)
|
|||
{
|
||||
// The Earth Ribbon is a ribbon exclusive to Pokémon Colosseum and Pokémon XD: Gale of Darkness
|
||||
// Awarded to all Pokémon on the player's team when they complete the Mt. Battle challenge without switching the team at any point.
|
||||
if (r.RibbonEarth && !RibbonRules.IsEarthRibbonAllowed(args.Entity, enc))
|
||||
if (r.RibbonEarth && enc.Generation != 3)
|
||||
list.Add(Earth);
|
||||
|
||||
var nationalRequired = RibbonRules.GetValidRibbonStateNational(args.Entity, enc);
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public static void Parse(this IRibbonSetOnly3 r, in RibbonVerifierArguments args
|
|||
if (r.RibbonWorld)
|
||||
list.Add(RibbonIndex.World);
|
||||
|
||||
if (!RibbonRules.IsAllowedContest3(args.History, args.Entity))
|
||||
if (!RibbonRules.IsAllowedContest3(args.History))
|
||||
FlagContestAny(r, ref list);
|
||||
else
|
||||
FlagContest(r, ref list);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public void Parse(in RibbonVerifierArguments args, ref RibbonResultList list)
|
|||
if (!RibbonRules.IsAllowedBattleFrontier4(evos))
|
||||
FlagAnyAbility(r, ref list);
|
||||
|
||||
if (RibbonRules.IsAllowedContest3(evos, args.Entity))
|
||||
if (RibbonRules.IsAllowedContest3(evos))
|
||||
AddMissingContest3(r, ref list);
|
||||
else
|
||||
FlagAnyContest3(r, ref list);
|
||||
|
|
|
|||
|
|
@ -62,11 +62,11 @@ private void VerifyTrashBytesPCD(LegalityAnalysis data, PKM pk, PCD pcd)
|
|||
private void VerifyTrashBytesHOME(LegalityAnalysis data, PKM pk)
|
||||
{
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.NicknameTrash))
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Nickname, TrashBytesMissingTerminatorFinal));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Nickname, TrashBytesMissingTerminator));
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.OriginalTrainerTrash))
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, TrashBytesMissingTerminatorFinal));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Trainer, TrashBytesMissingTerminator));
|
||||
if (!TrashBytesUTF16.IsFinalTerminatorPresent(pk.HandlingTrainerTrash))
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Handler, TrashBytesMissingTerminatorFinal));
|
||||
data.AddLine(GetInvalid(CheckIdentifier.Handler, TrashBytesMissingTerminator));
|
||||
|
||||
if (pk.IsEgg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -626,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)
|
||||
|
|
|
|||
|
|
@ -398,15 +398,4 @@ public override int GetStringTerminatorIndex(ReadOnlySpan<byte> data)
|
|||
public override int GetStringLength(ReadOnlySpan<byte> data)
|
||||
=> TrashBytes8.GetStringLength(data);
|
||||
public override int GetBytesPerChar() => 1;
|
||||
|
||||
public override void PrepareNickname() => GetNicknamePrefillRegion().Fill(StringConverter3.TerminatorByte);
|
||||
|
||||
public Span<byte> GetNicknamePrefillRegion()
|
||||
{
|
||||
// Japanese only fills the first 5+1 bytes; everything else is trash.
|
||||
// International games are 10 chars (full buffer) max; implicit terminator if full.
|
||||
if (Japanese)
|
||||
return NicknameTrash[..6];
|
||||
return NicknameTrash;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace PKHeX.Core;
|
|||
/// Object representing a <see cref="PKM"/>'s data and derived properties.
|
||||
/// </summary>
|
||||
[DynamicallyAccessedMembers(PublicProperties | NonPublicProperties | PublicParameterlessConstructor)]
|
||||
public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILangNick, IGameValueLimit, INature, IFatefulEncounter, IStringConverter, ITrashIntrospection, IContext
|
||||
public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILangNick, IGameValueLimit, INature, IFatefulEncounter, IStringConverter, ITrashIntrospection
|
||||
{
|
||||
public abstract int SIZE_PARTY { get; }
|
||||
public abstract int SIZE_STORED { get; }
|
||||
|
|
@ -44,11 +44,6 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
|
|||
public abstract Span<byte> OriginalTrainerTrash { get; }
|
||||
public virtual Span<byte> HandlingTrainerTrash => [];
|
||||
|
||||
/// <summary>
|
||||
/// Conditions the <see cref="NicknameTrash"/> data to safely terminate the Nickname string from the text entry screen.
|
||||
/// </summary>
|
||||
public virtual void PrepareNickname() { }
|
||||
|
||||
protected abstract byte[] Encrypt();
|
||||
public abstract EntityContext Context { get; }
|
||||
public byte Format => Context.Generation;
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ private bool SearchSimple(PKM pk)
|
|||
return false;
|
||||
if (Ability > -1 && pk.Ability != Ability)
|
||||
return false;
|
||||
if (Nature.IsFixed && pk.StatNature != Nature)
|
||||
if (Nature.IsFixed() && pk.StatNature != Nature)
|
||||
return false;
|
||||
if (Item > -1 && pk.HeldItem != Item)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public abstract class G3PKM : PKM, IRibbonSetEvent3, IRibbonSetCommon3, IRibbonS
|
|||
public sealed override ushort MaxMoveID => Legal.MaxMoveID_3;
|
||||
public sealed override ushort MaxSpeciesID => Legal.MaxSpeciesID_3;
|
||||
public sealed override int MaxAbilityID => Legal.MaxAbilityID_3;
|
||||
public sealed override int MaxItemID => Legal.MaxItemID_3_E;
|
||||
public sealed override int MaxItemID => Legal.MaxItemID_3;
|
||||
public sealed override int MaxBallID => Legal.MaxBallID_3;
|
||||
public sealed override GameVersion MaxGameID => Legal.MaxGameID_3;
|
||||
public sealed override int MaxIV => 31;
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ public sealed override byte Ball
|
|||
BallDPPt = Clamp(value, Core.Ball.Cherish);
|
||||
|
||||
// Only set the HG/SS value if it originated in HG/SS and was not an event.
|
||||
if (WasCreatedInHGSS && this is not BK4)
|
||||
if (WasCreatedInHGSS)
|
||||
BallHGSS = Clamp(value, Core.Ball.Sport);
|
||||
else
|
||||
BallHGSS = 0;
|
||||
|
|
|
|||
|
|
@ -35,20 +35,3 @@ public interface IStringConverter
|
|||
/// <returns>Count of bytes written to <see cref="data"/>.</returns>
|
||||
int SetString(Span<byte> data, ReadOnlySpan<char> text, int length, StringConverterOption option);
|
||||
}
|
||||
|
||||
public delegate string StringGetter(ReadOnlySpan<byte> data);
|
||||
public delegate int StringLoader(ReadOnlySpan<byte> data, Span<char> text);
|
||||
public delegate int StringSetter(Span<byte> data, ReadOnlySpan<char> text, int length, StringConverterOption option);
|
||||
|
||||
public sealed class CustomStringConverter : IStringConverter, IGeneration, IContext
|
||||
{
|
||||
public required StringGetter Get { get; init; }
|
||||
public required StringLoader Load { get; init; }
|
||||
public required StringSetter Set { get; init; }
|
||||
public required byte Generation { get; init; }
|
||||
public required EntityContext Context { get; init; }
|
||||
|
||||
public string GetString(ReadOnlySpan<byte> data) => Get(data);
|
||||
public int LoadString(ReadOnlySpan<byte> data, Span<char> text) => Load(data, text);
|
||||
public int SetString(Span<byte> data, ReadOnlySpan<char> text, int length, StringConverterOption option) => Set(data, text, length, option);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ public static byte GetItemOld2(ushort item)
|
|||
NaN, 058, 059, 061, 444, NaN, NaN, 216, 445, 446, // 5
|
||||
NaN, 447, 051, 038, 039, 040, 478, 464, 456, 484, // 6
|
||||
NaN, 482, 033, 217, 151, NaN, 237, 244, 149, 153, // 7
|
||||
152, 245, 221, 156, 150, 485, 086, 087, 222, 486, // 8
|
||||
NaN, 223, 487, 488, 224, 243, 248, 490, 241, 491, // 9
|
||||
152, 245, 221, 156, 150, 485, 086, 087, 222, 487, // 8
|
||||
NaN, 223, 486, 488, 224, 243, 248, 490, 241, 491, // 9
|
||||
NaN, 489, 240, 473, NaN, 259, 228, 246, 242, 157, // 10
|
||||
088, 089, 229, 247, 504, NaN, NaN, 239, 258, 230, // 11
|
||||
NaN, 034, 035, 036, 037, 238, 231, 475, 481, NaN, // 12
|
||||
|
|
|
|||
|
|
@ -85,15 +85,6 @@ public static byte GetGenderRatio(ushort species)
|
|||
return NG;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the species (base form) is a single gender.
|
||||
/// </summary>
|
||||
public static bool IsSingleGender(ushort species)
|
||||
{
|
||||
var ratio = GetGenderRatio(species);
|
||||
return ratio is OM or OF or NG;
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> GenderRatios =>
|
||||
[
|
||||
NG, VM, VM, VM, VM, VM, VM, VM, VM, VM, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, HH, OF, OF, OF, OM, OM, OM, MF, MF, MF, MF, MF,
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "Neues Spiel: 0x{0}, Frame: {1}",
|
||||
"FrameInitial": "Initial: 0x{0}, Frame: {1}",
|
||||
"SuffixDays": " (tage: {0})"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "New Game: 0x{0}, Frame: {1}",
|
||||
"FrameInitial": "Initial: 0x{0}, Frame: {1}",
|
||||
"SuffixDays": " (days: {0})"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "Nuevo juego: 0x{0}, Fotograma: {1}",
|
||||
"FrameInitial": "Inicial: 0x{0}, Fotograma: {1}",
|
||||
"SuffixDays": " (días: {0})"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "Nuevo juego: 0x{0}, Fotograma: {1}",
|
||||
"FrameInitial": "Inicial: 0x{0}, Fotograma: {1}",
|
||||
"SuffixDays": " (días: {0})"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "Nouvelle partie : 0x{0}, Image : {1}",
|
||||
"FrameInitial": "Initial : 0x{0}, Image : {1}",
|
||||
"SuffixDays": " (jours : {0})"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "Nuova partita: 0x{0}, Frame: {1}",
|
||||
"FrameInitial": "Iniziale: 0x{0}, Frame: {1}",
|
||||
"SuffixDays": " (giorni: {0})"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "新規ゲーム: 0x{0}, フレーム: {1}",
|
||||
"FrameInitial": "初期値: 0x{0}, フレーム: {1}",
|
||||
"SuffixDays": " (日数: {0})"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "새 게임: 0x{0}, 프레임: {1}",
|
||||
"FrameInitial": "초기값: 0x{0}, 프레임: {1}",
|
||||
"SuffixDays": " (일: {0})"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,4 +12,4 @@
|
|||
"FrameNewGame": "新游戏:0x{0},帧:{1}",
|
||||
"FrameInitial": "初始:0x{0},帧:{1}",
|
||||
"SuffixDays": " (天数:{0})"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user