Misc tweaks

Add xmldoc
Simplify some casts
Demote priority of Gen2 event yielding
Remove old EncounterMatchUtil code
Repoint DateTime.Now to console specific date provider stubs
This commit is contained in:
Kurt 2023-09-10 21:17:47 -07:00
parent c7aa497e73
commit b536388d0d
71 changed files with 376 additions and 212 deletions

View file

@ -30,12 +30,12 @@ public sealed class QRPK7 : IEncounterInfo
public int Move3_PPUps => Data[0xA]; public int Move3_PPUps => Data[0xA];
public int Move4_PPUps => Data[0xB]; public int Move4_PPUps => Data[0xB];
public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0xC)); set => WriteUInt32LittleEndian(Data.AsSpan(0xC), value); } public uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0xC)); set => WriteUInt32LittleEndian(Data.AsSpan(0xC), value); }
public int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); } public int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | (uint)((value > 31 ? 31 : value) << 00); }
public int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 05)) | (uint)((value > 31 ? 31 : value) << 05)); } public int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | (uint)((value > 31 ? 31 : value) << 05); }
public int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 10)) | (uint)((value > 31 ? 31 : value) << 10)); } public int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | (uint)((value > 31 ? 31 : value) << 10); }
public int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 15)) | (uint)((value > 31 ? 31 : value) << 15)); } public int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 15)) | (uint)((value > 31 ? 31 : value) << 15); }
public int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); } public int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 20)) | (uint)((value > 31 ? 31 : value) << 20); }
public int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); } public int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 25)) | (uint)((value > 31 ? 31 : value) << 25); }
public uint PID => ReadUInt32LittleEndian(Data.AsSpan(0x10)); public uint PID => ReadUInt32LittleEndian(Data.AsSpan(0x10));
public ushort Species => ReadUInt16LittleEndian(Data.AsSpan(0x14)); public ushort Species => ReadUInt16LittleEndian(Data.AsSpan(0x14));
public ushort HeldItem => ReadUInt16LittleEndian(Data.AsSpan(0x16)); public ushort HeldItem => ReadUInt16LittleEndian(Data.AsSpan(0x16));

View file

@ -135,9 +135,9 @@ public sealed class GameDataSource
{ {
var languages = new List<ComboItem>(LanguageList); var languages = new List<ComboItem>(LanguageList);
if (gen == 3) if (gen == 3)
languages.RemoveAll(l => l.Value >= (int)LanguageID.Korean); languages.RemoveAll(static l => l.Value >= (int)LanguageID.Korean);
else if (gen < 7) else if (gen < 7)
languages.RemoveAll(l => l.Value > (int)LanguageID.Korean); languages.RemoveAll(static l => l.Value > (int)LanguageID.Korean);
return languages; return languages;
} }
} }

View file

@ -273,22 +273,33 @@ public sealed class MetDataSource
BD or SP => Partition2(MetGen8b, IsMetLocation8BDSP), BD or SP => Partition2(MetGen8b, IsMetLocation8BDSP),
PLA => Partition2(MetGen8a, IsMetLocation8LA), PLA => Partition2(MetGen8a, IsMetLocation8LA),
SL or VL => Partition2(MetGen9, IsMetLocation9SV), SL or VL => Partition2(MetGen9, IsMetLocation9SV),
_ => new List<ComboItem>(GetLocationListModified(version, context)), _ => GetLocationListModified(version, context),
}; };
static IReadOnlyList<ComboItem> Partition1(IReadOnlyList<ComboItem> list, Func<ushort, bool> criteria) static ComboItem[] Partition1(List<ComboItem> list, Func<ushort, bool> criteria)
{ {
var span = CollectionsMarshal.AsSpan(list);
var result = new ComboItem[list.Count]; var result = new ComboItem[list.Count];
return GetOrderedList(list, result, criteria); ReorderList(span, result, criteria);
return result;
} }
static IReadOnlyList<ComboItem> GetOrderedList(IReadOnlyList<ComboItem> list, ComboItem[] result, static ComboItem[] Partition2(List<ComboItem> list, Func<ushort, bool> criteria, int keepFirst = 3)
Func<ushort, bool> criteria, int start = 0) {
var span = CollectionsMarshal.AsSpan(list);
var result = new ComboItem[span.Length];
for (int i = 0; i < keepFirst; i++)
result[i] = list[i];
ReorderList(span, result, criteria, keepFirst);
return result;
}
static void ReorderList(Span<ComboItem> list, Span<ComboItem> result, Func<ushort, bool> criteria, int start = 0)
{ {
// store values that match criteria at the next available position of the array // store values that match criteria at the next available position of the array
// store non-matches starting at the end. reverse before returning // store non-matches starting at the end. reverse before returning
int end = list.Count - 1; int end = list.Length - 1;
for (var index = start; index < list.Count; index++) for (var index = start; index < list.Length; index++)
{ {
var item = list[index]; var item = list[index];
if (criteria((ushort)item.Value)) if (criteria((ushort)item.Value))
@ -297,18 +308,8 @@ public sealed class MetDataSource
result[end--] = item; result[end--] = item;
} }
// since the non-matches are reversed in order, we swap them back since we know where they end up at. // since the non-matches are reversed in order, we reverse that section.
Array.Reverse(result, start, list.Count - start); result[start..].Reverse();
return result;
}
static IReadOnlyList<ComboItem> Partition2(IReadOnlyList<ComboItem> list, Func<ushort, bool> criteria,
int keepFirst = 3)
{
var result = new ComboItem[list.Count];
for (int i = 0; i < keepFirst; i++)
result[i] = list[i];
return GetOrderedList(list, result, criteria, keepFirst);
} }
} }

View file

@ -8,7 +8,7 @@ namespace PKHeX.Core;
public static class LocationsHOME public static class LocationsHOME
{ {
// 60000 - (version - PLA) // 60000 - (version - PLA)
private const int RemapCount = 5; private const int RemapCount = 5; // Count of future game version IDs that can transfer back into SW/SH.
public const ushort SHVL = 59996; // VL traded to (SW)SH public const ushort SHVL = 59996; // VL traded to (SW)SH
public const ushort SWSL = 59997; // SL traded to SW(SH) public const ushort SWSL = 59997; // SL traded to SW(SH)
public const ushort SHSP = 59998; // SP traded to (SW)SH public const ushort SHSP = 59998; // SP traded to (SW)SH

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -59,7 +60,13 @@ public static class BulkGenerator
template.Form = form; template.Form = form;
template.Gender = template.GetSaneGender(); template.Gender = template.GetSaneGender();
var f = EncounterMovesetGenerator.GeneratePKMs(template, tr, template.Moves).FirstOrDefault(); var moves = ArrayPool<ushort>.Shared.Rent(4);
var memory = moves.AsMemory(0, 4);
var span = memory.Span;
template.GetMoves(span);
var f = EncounterMovesetGenerator.GeneratePKMs(template, tr, memory).FirstOrDefault();
span.Clear();
ArrayPool<ushort>.Shared.Return(moves);
if (f == null) if (f == null)
return null; return null;

View file

@ -167,23 +167,23 @@ public static class EncounterFinder
LearnVerifier.Verify(info.Moves, pk, info.EncounterMatch, info.EvoChainsAllGens); LearnVerifier.Verify(info.Moves, pk, info.EncounterMatch, info.EvoChainsAllGens);
} }
private static string GetHintWhyNotFound(PKM pk, int gen) private static string GetHintWhyNotFound(PKM pk, int generation)
{ {
if (WasGiftEgg(pk, gen, (ushort)pk.Egg_Location)) if (WasGiftEgg(pk, generation, (ushort)pk.Egg_Location))
return LEncGift; return LEncGift;
if (WasEventEgg(pk, gen)) if (WasEventEgg(pk, generation))
return LEncGiftEggEvent; return LEncGiftEggEvent;
if (WasEvent(pk, gen)) if (WasEvent(pk, generation))
return LEncGiftNotFound; return LEncGiftNotFound;
return LEncInvalid; return LEncInvalid;
} }
private static bool WasGiftEgg(PKM pk, int gen, ushort loc) => !pk.FatefulEncounter && gen switch private static bool WasGiftEgg(PKM pk, int generation, ushort eggLocation) => !pk.FatefulEncounter && generation switch
{ {
3 => pk.IsEgg && (byte)pk.Met_Location == 253, // Gift Egg, indistinguishable from normal eggs after hatch 3 => pk.IsEgg && (byte)pk.Met_Location == 253, // Gift Egg, indistinguishable from normal eggs after hatch
4 => (uint)(loc - 2009) <= (2014 - 2009) || (pk.Format != 4 && (loc == Locations.Faraway4 && pk.HGSS)), 4 => eggLocation - 2009u <= (2014 - 2009) || (pk.Format != 4 && (eggLocation == Locations.Faraway4 && pk.HGSS)),
5 => loc is Locations.Breeder5, 5 => eggLocation is Locations.Breeder5,
_ => loc is Locations.Breeder6, _ => eggLocation is Locations.Breeder6,
}; };
private static bool WasEventEgg(PKM pk, int gen) => gen switch private static bool WasEventEgg(PKM pk, int gen) => gen switch

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.Gen1"/> encounters.
/// </summary>
public record struct EncounterPossible1(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible1(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.Gen2"/> encounters.
/// </summary>
public record struct EncounterPossible2(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version, PKM Entity) : IEnumerator<IEncounterable> public record struct EncounterPossible2(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version, PKM Entity) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.Gen3"/> encounters.
/// </summary>
public record struct EncounterPossible3(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible3(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.CXD"/> encounters.
/// </summary>
public record struct EncounterPossible3GC(EvoCriteria[] Chain, EncounterTypeGroup Flags) : IEnumerator<IEncounterable> public record struct EncounterPossible3GC(EvoCriteria[] Chain, EncounterTypeGroup Flags) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.Gen4"/> encounters.
/// </summary>
public record struct EncounterPossible4(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible4(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.Gen5"/> encounters.
/// </summary>
public record struct EncounterPossible5(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible5(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.Gen6"/> encounters.
/// </summary>
public record struct EncounterPossible6(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible6(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.Gen7"/> encounters.
/// </summary>
public record struct EncounterPossible7(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible7(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.GG"/> encounters.
/// </summary>
public record struct EncounterPossible7GG(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible7GG(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.GO"/> encounters from LGP/E's GO Park.
/// </summary>
public record struct EncounterPossible7GO(EvoCriteria[] Chain, EncounterTypeGroup Flags) : IEnumerator<IEncounterable> public record struct EncounterPossible7GO(EvoCriteria[] Chain, EncounterTypeGroup Flags) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.SWSH"/> encounters.
/// </summary>
public record struct EncounterPossible8(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible8(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.GO"/> encounters.
/// </summary>
public record struct EncounterPossible8GO(EvoCriteria[] Chain, EncounterTypeGroup Flags) : IEnumerator<IEncounterable> public record struct EncounterPossible8GO(EvoCriteria[] Chain, EncounterTypeGroup Flags) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.PLA"/> encounters.
/// </summary>
public record struct EncounterPossible8a(EvoCriteria[] Chain, EncounterTypeGroup Flags) : IEnumerator<IEncounterable> public record struct EncounterPossible8a(EvoCriteria[] Chain, EncounterTypeGroup Flags) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.BDSP"/> encounters.
/// </summary>
public record struct EncounterPossible8b(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible8b(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find possible encounters for <see cref="GameVersion.SV"/> encounters.
/// </summary>
public record struct EncounterPossible9(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable> public record struct EncounterPossible9(EvoCriteria[] Chain, EncounterTypeGroup Flags, GameVersion Version) : IEnumerator<IEncounterable>
{ {
public IEncounterable Current { get; private set; } public IEncounterable Current { get; private set; }

View file

@ -5,6 +5,9 @@ using System.Diagnostics;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.BDSP"/> encounters while in the <see cref="PK8"/> format.
/// </summary>
public record struct EncounterEnumerator8bSWSH(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator8bSWSH(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.SV"/> encounters while in the <see cref="PK8"/> format.
/// </summary>
public record struct EncounterEnumerator9SWSH(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator9SWSH(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.Gen1"/>.
/// </summary>
public record struct EncounterEnumerator1(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator1(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.Gen2"/>.
/// </summary>
public record struct EncounterEnumerator2 : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator2 : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;
@ -79,25 +82,7 @@ public record struct EncounterEnumerator2 : IEnumerator<MatchedEncounter<IEncoun
case YieldState.Start: case YieldState.Start:
if (Chain.Length == 0) if (Chain.Length == 0)
break; break;
goto case YieldState.EventStart; State = YieldState.Trade; goto case YieldState.Trade;
case YieldState.EventStart:
if (Entity.Korean)
{ State = YieldState.Trade; goto case YieldState.Trade; }
if (ParseSettings.AllowGBVirtualConsole3DS)
{ State = YieldState.EventVC; goto case YieldState.EventVC; }
if (ParseSettings.AllowGBEraEvents)
{ State = YieldState.EventGB; goto case YieldState.EventGB; }
throw new InvalidOperationException("No events allowed");
case YieldState.EventVC:
State = YieldState.Trade;
if (IsMatch(Encounters2.CelebiVC))
return SetCurrent(Encounters2.CelebiVC);
goto case YieldState.Trade;
case YieldState.EventGB:
if (TryGetNext(Encounters2GBEra.StaticEventsGB))
return true;
Index = 0; State = YieldState.Trade; goto case YieldState.Trade;
case YieldState.Trade: case YieldState.Trade:
if (TryGetNext(Encounters2.TradeGift_GSC)) if (TryGetNext(Encounters2.TradeGift_GSC))
@ -170,7 +155,25 @@ public record struct EncounterEnumerator2 : IEnumerator<MatchedEncounter<IEncoun
return true; return true;
Index = 0; goto case YieldState.SlotEnd; Index = 0; goto case YieldState.SlotEnd;
case YieldState.SlotEnd: case YieldState.SlotEnd:
goto case YieldState.EventStart;
case YieldState.EventStart:
if (Entity.Korean)
{ State = YieldState.Fallback; goto case YieldState.Fallback; }
if (ParseSettings.AllowGBVirtualConsole3DS)
{ State = YieldState.EventVC; goto case YieldState.EventVC; }
if (ParseSettings.AllowGBEraEvents)
{ State = YieldState.EventGB; goto case YieldState.EventGB; }
throw new InvalidOperationException("No events allowed");
case YieldState.EventVC:
State = YieldState.Fallback;
if (IsMatch(Encounters2.CelebiVC))
return SetCurrent(Encounters2.CelebiVC);
goto case YieldState.Fallback; goto case YieldState.Fallback;
case YieldState.EventGB:
if (TryGetNext(Encounters2GBEra.StaticEventsGB))
return true;
State = YieldState.Fallback; goto case YieldState.Fallback;
case YieldState.Fallback: case YieldState.Fallback:
State = YieldState.End; State = YieldState.End;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.Gen3"/>.
/// </summary>
public record struct EncounterEnumerator3(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator3(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.CXD"/>.
/// </summary>
public record struct EncounterEnumerator3GC(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator3GC(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.Gen4"/>.
/// </summary>
public record struct EncounterEnumerator4(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator4(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.Gen5"/>.
/// </summary>
public record struct EncounterEnumerator5(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator5(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.Gen6"/>.
/// </summary>
public record struct EncounterEnumerator6(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator6(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.Gen7"/>.
/// </summary>
public record struct EncounterEnumerator7(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator7(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.GG"/>.
/// </summary>
public record struct EncounterEnumerator7GG(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator7GG(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.GG"/> encounters from GO Park.
/// </summary>
public record struct EncounterEnumerator7GO(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator7GO(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.SWSH"/>.
/// </summary>
public record struct EncounterEnumerator8(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator8(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.GO"/>.
/// </summary>
public record struct EncounterEnumerator8GO(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator8GO(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.PLA"/>.
/// </summary>
public record struct EncounterEnumerator8a(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator8a(PKM Entity, EvoCriteria[] Chain) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -5,6 +5,9 @@ using System.Diagnostics;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.BDSP"/>.
/// </summary>
public record struct EncounterEnumerator8b(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator8b(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.SV"/>.
/// </summary>
public record struct EncounterEnumerator9(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>> public record struct EncounterEnumerator9(PKM Entity, EvoCriteria[] Chain, GameVersion Version) : IEnumerator<MatchedEncounter<IEncounterable>>
{ {
private IEncounterable? Deferred; private IEncounterable? Deferred;

View file

@ -0,0 +1,6 @@
namespace PKHeX.Core;
/// <summary>
/// Tuple of matched encounter info.
/// </summary>
public readonly record struct MatchedEncounter<T>(T Encounter, EncounterMatchRating Rating);

View file

@ -1,6 +1,3 @@
using System.Collections;
using System.Collections.Generic;
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary> /// <summary>
@ -23,58 +20,3 @@ public enum EncounterMatchRating : ushort
/// <summary> Unused -- only used as an initial "max" value that anything else will be more suitable of a match. </summary> /// <summary> Unused -- only used as an initial "max" value that anything else will be more suitable of a match. </summary>
MaxNotMatch, MaxNotMatch,
} }
public readonly record struct MatchedEncounter<T>(T Encounter, EncounterMatchRating Rating);
public static class EncounterMatchUtil
{
public static EncounterEnumerator<T> Enumerate<T>(this IReadOnlyList<T> encounters, EvoCriteria[] chain, PKM pk)
where T : IEncounterMatch, IEncounterable
{
return new EncounterEnumerator<T>(encounters, chain, pk);
}
public struct EncounterEnumerator<T> : IEnumerator<MatchedEncounter<T>> where T : IEncounterMatch, IEncounterable
{
private readonly IReadOnlyList<T> _encounters;
private readonly EvoCriteria[] _chain;
private int _index = 0;
private readonly PKM _pk;
public EncounterEnumerator(IReadOnlyList<T> encounters, EvoCriteria[] chain, PKM pk)
{
_encounters = encounters;
_chain = chain;
_pk = pk;
}
public MatchedEncounter<T> Current { get; private set; } = default!;
readonly object IEnumerator.Current => Current;
public bool MoveNext()
{
for (; _index < _encounters.Count;)
{
var enc = _encounters[_index++];
foreach (var evo in _chain)
{
if (enc.Species != evo.Species)
continue;
var exact = enc.IsMatchExact(_pk, evo);
if (!exact)
break;
Current = new(enc, enc.GetMatchRating(_pk));
return true;
}
}
return false;
}
public void Reset() => _index = 0;
public readonly void Dispose() { }
public readonly IEnumerator<MatchedEncounter<T>> GetEnumerator() => this;
}
}

View file

@ -20,7 +20,14 @@ public interface IMagnetStatic
/// <summary> Count of slots in the parent area that can be yielded by <see cref="Ability.MagnetPull"/> </summary> /// <summary> Count of slots in the parent area that can be yielded by <see cref="Ability.MagnetPull"/> </summary>
byte MagnetPullCount { get; } byte MagnetPullCount { get; }
/// <summary>
/// Indicates if the slot can be yielded by <see cref="Ability.Static"/>.
/// </summary>
bool IsStaticSlot => StaticCount != 0 && StaticIndex != byte.MaxValue; bool IsStaticSlot => StaticCount != 0 && StaticIndex != byte.MaxValue;
/// <summary>
/// Indicates if the slot can be yielded by <see cref="Ability.MagnetPull"/>.
/// </summary>
bool IsMagnetSlot => MagnetPullCount != 0 && MagnetPullIndex != byte.MaxValue; bool IsMagnetSlot => MagnetPullCount != 0 && MagnetPullIndex != byte.MaxValue;
bool IsMatchStatic(int index, int count) => index == StaticIndex && count == StaticCount; bool IsMatchStatic(int index, int count) => index == StaticIndex && count == StaticCount;

View file

@ -12,7 +12,7 @@ public static class EggHatchLocation5
public static bool IsValidMet5(int location, GameVersion game) public static bool IsValidMet5(int location, GameVersion game)
{ {
var shift = (uint)((int)game - (int)W); var shift = (uint)(game - W);
if (shift >= 4) if (shift >= 4)
return false; return false;

View file

@ -24,8 +24,12 @@ public static class MoveBreed4
if (count == -1) if (count == -1)
count = moves.Length; count = moves.Length;
var learn = GameData.GetLearnSource(version); var learnset = version switch
var learnset = learn.GetLearnset(species, 0); {
HG or SS => LearnSource4HGSS.Instance.GetLearnset(species, 0),
D or P => LearnSource4DP.Instance.GetLearnset(species, 0),
_ => LearnSource4Pt.Instance.GetLearnset(species, 0),
};
var table = version switch var table = version switch
{ {
HG or SS => PersonalTable.HGSS, HG or SS => PersonalTable.HGSS,

View file

@ -22,8 +22,11 @@ public static class MoveBreed5
if (count == -1) if (count == -1)
count = moves.Length; count = moves.Length;
var learn = GameData.GetLearnSource(version); var learnset = version switch
var learnset = learn.GetLearnset(species, 0); {
GameVersion.B or GameVersion.W => LearnSource5BW.Instance.GetLearnset(species, 0),
_ => LearnSource5B2W2.Instance.GetLearnset(species, 0),
};
IPersonalInfoTM pi = version switch IPersonalInfoTM pi = version switch
{ {
GameVersion.B or GameVersion.W => PersonalTable.BW[species], GameVersion.B or GameVersion.W => PersonalTable.BW[species],

View file

@ -9,6 +9,16 @@ public static class Tera9RNG
{ {
private const uint TeraTypeCount = 18; private const uint TeraTypeCount = 18;
/// <summary>
/// Checks if the <see cref="ITeraType.TeraTypeOriginal"/> matches the specification of the <see cref="gem"/> value.
/// </summary>
/// <param name="seed">Seed used to generate the Tera Type</param>
/// <param name="gem">Encounter specified Gem Type</param>
/// <param name="species">Encounter Species</param>
/// <param name="form">Encounter Form</param>
/// <param name="original">Original Tera Type from the Entity</param>
/// <returns>True if the Tera Type matches the RNG and specification</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static bool IsMatchTeraType(in uint seed, in GemType gem, in ushort species, in byte form, in byte original) public static bool IsMatchTeraType(in uint seed, in GemType gem, in ushort species, in byte form, in byte original)
{ {
if (gem.IsSpecified(out var type)) if (gem.IsSpecified(out var type))
@ -43,6 +53,14 @@ public static class Tera9RNG
throw new ArgumentOutOfRangeException(nameof(gem), gem, null); throw new ArgumentOutOfRangeException(nameof(gem), gem, null);
} }
/// <summary>
/// Gets the expected Tera Type from the Personal Info and RNG seed.
/// </summary>
/// <param name="seed">Seed used to generate the Tera Type</param>
/// <param name="gem">Encounter specified Gem Type</param>
/// <param name="species">Encounter Species</param>
/// <param name="form">Encounter Form</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static byte GetTeraType(in ulong seed, GemType gem, in ushort species, in byte form) public static byte GetTeraType(in ulong seed, GemType gem, in ushort species, in byte form)
{ {
if (gem.IsSpecified(out var type)) if (gem.IsSpecified(out var type))
@ -61,6 +79,9 @@ public static class Tera9RNG
throw new ArgumentOutOfRangeException(nameof(gem), gem, null); throw new ArgumentOutOfRangeException(nameof(gem), gem, null);
} }
/// <summary>
/// Checks if the original Tera Type matches either of the Personal Info types.
/// </summary>
private static bool IsMatchType(IPersonalType pi, in byte original) => original == pi.Type1 || original == pi.Type2; private static bool IsMatchType(IPersonalType pi, in byte original) => original == pi.Type1 || original == pi.Type2;
public static bool IsMatchTeraTypePersonalEgg(in ushort species, in byte form, in byte original) => public static bool IsMatchTeraTypePersonalEgg(in ushort species, in byte form, in byte original) =>
@ -68,7 +89,16 @@ public static class Tera9RNG
? IsMatchTeraTypePersonalAnyForm(species, original) ? IsMatchTeraTypePersonalAnyForm(species, original)
: IsMatchTeraTypePersonal(species, form, original); : IsMatchTeraTypePersonal(species, form, original);
/// <inheritdoc cref="IsMatchType"/>
public static bool IsMatchTeraTypePersonal(in ushort species, in byte form, in byte original) => IsMatchType(PersonalTable.SV[species, form], original); public static bool IsMatchTeraTypePersonal(in ushort species, in byte form, in byte original) => IsMatchType(PersonalTable.SV[species, form], original);
/// <summary>
/// Checks if the original Tera Type matches any of the Personal Info types for any form it may change into.
/// </summary>
/// <remarks>Only enter this method if it is permitted to change into all forms.</remarks>
/// <param name="species">Encounter Species</param>
/// <param name="original">Entity's original Tera Type</param>
/// <returns>True if the Tera Type matches any of the Personal Info types</returns>
public static bool IsMatchTeraTypePersonalAnyForm(in ushort species, in byte original) public static bool IsMatchTeraTypePersonalAnyForm(in ushort species, in byte original)
{ {
var pt = PersonalTable.SV; var pt = PersonalTable.SV;
@ -85,18 +115,27 @@ public static class Tera9RNG
return false; return false;
} }
/// <summary>
/// Checks if the original Tera Type matches the Personal Info type for the specified form.
/// </summary>
/// <remarks>Used for HOME imports into <see cref="GameVersion.SV"/>.</remarks>
/// <param name="pi">Arrival Personal Info</param>
/// <param name="original">Entity's original Tera Type</param>
/// <returns>True if the Tera Type matches the expected Personal Info type</returns>
private static bool IsMatchTeraTypeImport(PersonalInfo9SV pi, in byte original) private static bool IsMatchTeraTypeImport(PersonalInfo9SV pi, in byte original)
{ {
var import = TeraTypeUtil.GetTeraTypeImport(pi.Type1, pi.Type2); var import = TeraTypeUtil.GetTeraTypeImport(pi.Type1, pi.Type2);
return (MoveType)original == import; return (MoveType)original == import;
} }
/// <inheritdoc cref="IsMatchTeraTypeImport"/>
public static bool IsMatchTeraTypePersonalImport(in ushort species, in byte form, in byte original) public static bool IsMatchTeraTypePersonalImport(in ushort species, in byte form, in byte original)
{ {
var pi = PersonalTable.SV[species, form]; var pi = PersonalTable.SV[species, form];
return IsMatchTeraTypeImport(pi, original); return IsMatchTeraTypeImport(pi, original);
} }
/// <inheritdoc cref="IsMatchTeraTypeImport"/>
public static bool IsMatchTeraTypePersonalAnyFormImport(in ushort species, in byte original) public static bool IsMatchTeraTypePersonalAnyFormImport(in ushort species, in byte original)
{ {
var pt = PersonalTable.SV; var pt = PersonalTable.SV;
@ -113,12 +152,29 @@ public static class Tera9RNG
return false; return false;
} }
/// <summary>
/// Gets the expected Tera Type from the Personal Info and pivot.
/// </summary>
/// <param name="species">Encounter Species</param>
/// <param name="form">Encounter Form</param>
/// <param name="pivot">Random pivot to determine which Personal Info type to use</param>
/// <returns>Expected Tera Type</returns>
public static byte GetTeraTypeFromPersonal(in ushort species, in byte form, in ulong pivot) public static byte GetTeraTypeFromPersonal(in ushort species, in byte form, in ulong pivot)
{ {
var pi = PersonalTable.SV[species, form]; var pi = PersonalTable.SV[species, form];
return pivot == 0 ? pi.Type1 : pi.Type2; return pivot == 0 ? pi.Type1 : pi.Type2;
} }
/// <summary>
/// Compares the Raid Seed to the possible raid bounds to see if the encounter can originate from that seed.
/// </summary>
/// <remarks>A given raid seed might yield a different encounter or star count, hence why we need to check.</remarks>
/// <param name="seed">Random seed used to generate the raid.</param>
/// <param name="stars">Difficulty rating.</param>
/// <param name="raidRate">Random weight of the raid to be used in the comparison with the game specific min rates.</param>
/// <param name="randRateMinScarlet">Total weight of all Scarlet raids prior to this encounter.</param>
/// <param name="randRateMinViolet">Total weight of all Violet raids prior to this encounter.</param>
/// <returns>True if the raid seed can generate the encounter.</returns>
public static bool IsMatchStarChoice(in uint seed, in byte stars, in byte raidRate, in short randRateMinScarlet, in short randRateMinViolet) public static bool IsMatchStarChoice(in uint seed, in byte stars, in byte raidRate, in short randRateMinScarlet, in short randRateMinViolet)
{ {
// When determining a raid, the game takes the u32 seed and does two rand calls. // When determining a raid, the game takes the u32 seed and does two rand calls.

View file

@ -77,13 +77,14 @@ public sealed class LegalInfo : IGeneration
StoreMetadata(pk.Generation); StoreMetadata(pk.Generation);
} }
internal void StoreMetadata(int gen) /// <summary>
/// We can call this method at the start for any Gen3+ encounter iteration.
/// Additionally, We need to call this for each Gen1/2 encounter as Version is not stored for those origins.
/// </summary>
/// <param name="generation">Encounter generation</param>
internal void StoreMetadata(int generation) => Generation = generation switch
{ {
// We can call this method at the start for any Gen3+ encounter iteration. -1 when Entity is PK9 { IsUnhatchedEgg: true } => 9,
// We need to call this for each Gen1/2 encounter as Version is not stored for those origins. _ => generation,
Generation = gen; };
if (gen == -1 && Entity is PK9 { IsUnhatchedEgg: true })
Generation = 9;
}
} }

View file

@ -185,7 +185,7 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
var rnd = Util.Rand; var rnd = Util.Rand;
var dt = DateTime.Now; var dt = EncounterDate.GetDateNDS();
if (Day == 0) if (Day == 0)
{ {
Day = (byte)dt.Day; Day = (byte)dt.Day;
@ -202,7 +202,7 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
Met_Level = currentLevel, Met_Level = currentLevel,
Nature = Nature != -1 ? Nature : rnd.Next(25), Nature = Nature != -1 ? Nature : rnd.Next(25),
Form = Form, Form = Form,
Version = OriginGame == 0 ? tr.Game : OriginGame, Version = GetVersion(tr, rnd),
Language = Language == 0 ? tr.Language : Language, Language = Language == 0 ? tr.Language : Language,
Ball = Ball, Ball = Ball,
Move1 = Move1, Move1 = Move1,
@ -241,8 +241,6 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
FatefulEncounter = true, FatefulEncounter = true,
}; };
if (tr.Generation > 5 && OriginGame == 0) // Gen6+, give random gen5 game
pk.Version = (int)GameVersion.W + rnd.Next(4);
if (Move1 == 0) // No moves defined if (Move1 == 0) // No moves defined
{ {
@ -285,6 +283,23 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
return pk; return pk;
} }
private int GetVersion(ITrainerInfo tr, Random rnd)
{
if (OriginGame != 0)
return OriginGame;
if (tr.Generation <= 5)
return tr.Game;
// Gen6+, give random gen5 game
var bias = rnd.Next(4);
for (int i = 0; i < 4; i++)
{
var ver = (int)GameVersion.W + ((bias + i) % 4);
if (CanBeReceivedByVersion(ver))
return ver;
}
return (int)GameVersion.W; // should never hit this for any distributed card
}
private void SetEggMetDetails(PK5 pk) private void SetEggMetDetails(PK5 pk)
{ {
pk.IsEgg = true; pk.IsEgg = true;

View file

@ -17,7 +17,7 @@ public sealed class WC6Full
Data = data; Data = data;
var wc6 = data.AsSpan(GiftStart).ToArray(); var wc6 = data.AsSpan(GiftStart).ToArray();
Gift = new WC6(wc6); Gift = new WC6(wc6);
var now = DateTime.Now; var now = EncounterDate.GetDate3DS();
Gift.RawDate = WC6.SetDate((uint)now.Year, (uint)now.Month, (uint)now.Day); Gift.RawDate = WC6.SetDate((uint)now.Year, (uint)now.Month, (uint)now.Day);
Gift.RestrictVersion = RestrictVersion; Gift.RestrictVersion = RestrictVersion;
@ -30,7 +30,7 @@ public sealed class WC6Full
var countgift = data.Length / WC6.Size; var countgift = data.Length / WC6.Size;
var result = new WC6[countfull + countgift]; var result = new WC6[countfull + countgift];
var now = DateTime.Now; var now = EncounterDate.GetDate3DS();
for (int i = 0; i < countfull; i++) for (int i = 0; i < countfull; i++)
result[i] = ReadWC6(WC6Full, i * Size, now); result[i] = ReadWC6(WC6Full, i * Size, now);
for (int i = 0; i < countgift; i++) for (int i = 0; i < countgift; i++)
@ -39,7 +39,7 @@ public sealed class WC6Full
return result; return result;
} }
private static WC6 ReadWC6(ReadOnlySpan<byte> data, int ofs, DateTime date) private static WC6 ReadWC6(ReadOnlySpan<byte> data, int ofs, DateOnly date)
{ {
var slice = data.Slice(ofs + GiftStart, WC6.Size).ToArray(); var slice = data.Slice(ofs + GiftStart, WC6.Size).ToArray();
return new WC6(slice) return new WC6(slice)

View file

@ -17,7 +17,7 @@ public sealed class WC7Full
Data = data; Data = data;
var wc7 = data.AsSpan(GiftStart).ToArray(); var wc7 = data.AsSpan(GiftStart).ToArray();
Gift = new WC7(wc7); Gift = new WC7(wc7);
var now = DateTime.Now; var now = EncounterDate.GetDate3DS();
Gift.RawDate = WC7.SetDate((uint)now.Year, (uint)now.Month, (uint)now.Day); Gift.RawDate = WC7.SetDate((uint)now.Year, (uint)now.Month, (uint)now.Day);
Gift.RestrictVersion = RestrictVersion; Gift.RestrictVersion = RestrictVersion;
@ -30,7 +30,7 @@ public sealed class WC7Full
var countgift = data.Length / WC7.Size; var countgift = data.Length / WC7.Size;
var result = new WC7[countfull + countgift]; var result = new WC7[countfull + countgift];
var now = DateTime.Now; var now = EncounterDate.GetDate3DS();
for (int i = 0; i < countfull; i++) for (int i = 0; i < countfull; i++)
result[i] = ReadWC7(wc7Full, i * Size, now); result[i] = ReadWC7(wc7Full, i * Size, now);
for (int i = 0; i < countgift; i++) for (int i = 0; i < countgift; i++)
@ -39,7 +39,7 @@ public sealed class WC7Full
return result; return result;
} }
private static WC7 ReadWC7(ReadOnlySpan<byte> data, int ofs, DateTime date) private static WC7 ReadWC7(ReadOnlySpan<byte> data, int ofs, DateOnly date)
{ {
var slice = data.Slice(ofs + GiftStart, WC7.Size).ToArray(); var slice = data.Slice(ofs + GiftStart, WC7.Size).ToArray();
return new WC7(slice) return new WC7(slice)

View file

@ -7,12 +7,22 @@ namespace PKHeX.Core;
/// </summary> /// </summary>
public interface ITrainerID32 : ITrainerID16 public interface ITrainerID32 : ITrainerID16
{ {
/// <summary>
/// 32-bit Trainer ID (0-4294967295)
/// </summary>
uint ID32 { get; set; } uint ID32 { get; set; }
/// <summary>
/// 16-bit Secret ID (0-65535)
/// </summary>
ushort SID16 { get; set; } ushort SID16 { get; set; }
} }
public interface ITrainerID16 : ITrainerID public interface ITrainerID16 : ITrainerID
{ {
/// <summary>
/// 16-bit Trainer ID (0-65535)
/// </summary>
ushort TID16 { get; set; } ushort TID16 { get; set; }
} }
@ -28,10 +38,13 @@ public static class ITrainerID32Extensions
public static bool IsShiny(this ITrainerID32 tr, uint pid, int gen = 7) public static bool IsShiny(this ITrainerID32 tr, uint pid, int gen = 7)
{ {
var xor = GetShinyXor(tr, pid); var xor = GetShinyXor(tr, pid);
var threshold = (gen >= 7 ? 16 : 8); var threshold = (gen >= 7 ? ShinyXorThreshold7 : ShinyXorThreshold36);
return xor < threshold; return xor < threshold;
} }
private const int ShinyXorThreshold36 = 8; // 1:8192
private const int ShinyXorThreshold7 = 16; // 1:4096
/// <summary> /// <summary>
/// Calculates the <see cref="pid"/> and <see cref="ITrainerID32.ID32"/> xor. /// Calculates the <see cref="pid"/> and <see cref="ITrainerID32.ID32"/> xor.
/// </summary> /// </summary>

View file

@ -455,7 +455,7 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
Move2 = value.Move2; Move2 = value.Move2;
Move3 = value.Move3; Move3 = value.Move3;
Move4 = value.Move4; Move4 = value.Move4;
this.SetMaximumPPCurrent(Moves); this.SetMaximumPPCurrent(value);
} }
public void SetMoves(ReadOnlySpan<ushort> value) public void SetMoves(ReadOnlySpan<ushort> value)
@ -464,7 +464,7 @@ public abstract class PKM : ISpeciesForm, ITrainerID32, IGeneration, IShiny, ILa
Move2 = value.Length > 1 ? value[1] : default; Move2 = value.Length > 1 ? value[1] : default;
Move3 = value.Length > 2 ? value[2] : default; Move3 = value.Length > 2 ? value[2] : default;
Move4 = value.Length > 3 ? value[3] : default; Move4 = value.Length > 3 ? value[3] : default;
this.SetMaximumPPCurrent(Moves); this.SetMaximumPPCurrent(value);
} }
public ushort[] RelearnMoves public ushort[] RelearnMoves

View file

@ -275,8 +275,8 @@ public abstract class SAV4 : SaveFile, IEventFlag37
{ {
var pk4 = (PK4)pk; var pk4 = (PK4)pk;
// Apply to this Save File // Apply to this Save File
DateTime Date = DateTime.Now; var now = EncounterDate.GetDateNDS();
if (pk4.Trade(OT, ID32, Gender, Date.Day, Date.Month, Date.Year)) if (pk4.Trade(OT, ID32, Gender, now.Day, now.Month, now.Year))
pk.RefreshChecksum(); pk.RefreshChecksum();
} }

View file

@ -244,8 +244,8 @@ public sealed class SAV4BR : SaveFile
{ {
var pk4 = (BK4)pk; var pk4 = (BK4)pk;
// Apply to this Save File // Apply to this Save File
DateTime Date = DateTime.Now; var now = EncounterDate.GetDateNDS();
if (pk4.Trade(OT, ID32, Gender, Date.Day, Date.Month, Date.Year)) if (pk4.Trade(OT, ID32, Gender, now.Day, now.Month, now.Year))
pk.RefreshChecksum(); pk.RefreshChecksum();
} }

View file

@ -115,8 +115,8 @@ public abstract class SAV5 : SaveFile, ISaveBlock5BW, IEventFlag37
{ {
var pk5 = (PK5)pk; var pk5 = (PK5)pk;
// Apply to this Save File // Apply to this Save File
DateTime Date = DateTime.Now; var now = EncounterDate.GetDateNDS();
if (pk5.Trade(OT, ID32, Gender, Date.Day, Date.Month, Date.Year)) if (pk5.Trade(OT, ID32, Gender, now.Day, now.Month, now.Year))
pk.RefreshChecksum(); pk.RefreshChecksum();
} }

View file

@ -111,8 +111,8 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg
PK6 pk6 = (PK6)pk; PK6 pk6 = (PK6)pk;
// Apply to this Save File // Apply to this Save File
int CT = pk6.CurrentHandler; int CT = pk6.CurrentHandler;
DateTime Date = DateTime.Now; var now = EncounterDate.GetDate3DS();
pk6.Trade(this, Date.Day, Date.Month, Date.Year); pk6.Trade(this, now.Day, now.Month, now.Year);
if (CT != pk6.CurrentHandler) // Logic updated Friendship if (CT != pk6.CurrentHandler) // Logic updated Friendship
{ {
// Copy over the Friendship Value only under certain circumstances // Copy over the Friendship Value only under certain circumstances

View file

@ -175,8 +175,8 @@ public abstract class SAV7 : SAV_BEEF, ITrainerStatRecord, ISaveBlock7Main, IReg
PK7 pk7 = (PK7)pk; PK7 pk7 = (PK7)pk;
// Apply to this Save File // Apply to this Save File
int CT = pk7.CurrentHandler; int CT = pk7.CurrentHandler;
DateTime Date = DateTime.Now; var now = EncounterDate.GetDate3DS();
pk7.Trade(this, Date.Day, Date.Month, Date.Year); pk7.Trade(this, now.Day, now.Month, now.Year);
if (CT != pk7.CurrentHandler) // Logic updated Friendship if (CT != pk7.CurrentHandler) // Logic updated Friendship
{ {
// Copy over the Friendship Value only under certain circumstances // Copy over the Friendship Value only under certain circumstances

View file

@ -93,8 +93,8 @@ public sealed class SAV7b : SAV_BEEF, ISaveBlock7b, IGameSync, IEventFlagArray
{ {
var pb7 = (PB7)pk; var pb7 = (PB7)pk;
// Apply to this Save File // Apply to this Save File
var Date = DateTime.Now; var now = EncounterDate.GetDateSwitch();
pb7.Trade(this, Date.Day, Date.Month, Date.Year); pb7.Trade(this, now.Day, now.Month, now.Year);
pb7.RefreshChecksum(); pb7.RefreshChecksum();
} }

View file

@ -309,8 +309,8 @@ public sealed class SAV8BS : SaveFile, ISaveFileRevision, ITrainerStatRecord, IE
{ {
var pb8 = (PB8)pk; var pb8 = (PB8)pk;
// Apply to this Save File // Apply to this Save File
DateTime Date = DateTime.Now; var now = EncounterDate.GetDateSwitch();
pb8.Trade(this, Date.Day, Date.Month, Date.Year); pb8.Trade(this, now.Day, now.Month, now.Year);
pb8.RefreshChecksum(); pb8.RefreshChecksum();
AddCountAcquired(pb8); AddCountAcquired(pb8);

View file

@ -195,8 +195,8 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS
{ {
PK8 pk8 = (PK8)pk; PK8 pk8 = (PK8)pk;
// Apply to this Save File // Apply to this Save File
DateTime Date = DateTime.Now; var now = EncounterDate.GetDateSwitch();
pk8.Trade(this, Date.Day, Date.Month, Date.Year); pk8.Trade(this, now.Day, now.Month, now.Year);
if (FormArgumentUtil.IsFormArgumentTypeDatePair(pk8.Species, pk8.Form)) if (FormArgumentUtil.IsFormArgumentTypeDatePair(pk8.Species, pk8.Form))
{ {

View file

@ -164,8 +164,8 @@ public sealed class SAV9SV : SaveFile, ISaveBlock9Main, ISCBlockArray, ISaveFile
{ {
PK9 pk9 = (PK9)pk; PK9 pk9 = (PK9)pk;
// Apply to this Save File // Apply to this Save File
DateTime Date = DateTime.Now; var now = EncounterDate.GetDateSwitch();
pk9.Trade(this, Date.Day, Date.Month, Date.Year); pk9.Trade(this, now.Day, now.Month, now.Year);
if (FormArgumentUtil.IsFormArgumentTypeDatePair(pk9.Species, pk9.Form)) if (FormArgumentUtil.IsFormArgumentTypeDatePair(pk9.Species, pk9.Form))
{ {

View file

@ -63,12 +63,12 @@ public sealed class Roamer3 : IContestStats
public bool Active { get => Data[Offset + 0x13] == 1; set => Data[Offset + 0x13] = value ? (byte)1 : (byte)0; } public bool Active { get => Data[Offset + 0x13] == 1; set => Data[Offset + 0x13] = value ? (byte)1 : (byte)0; }
// Derived Properties // Derived Properties
private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); } private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | (uint)((value > 31 ? 31 : value) << 00); }
private int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 05)) | (uint)((value > 31 ? 31 : value) << 05)); } private int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | (uint)((value > 31 ? 31 : value) << 05); }
private int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 10)) | (uint)((value > 31 ? 31 : value) << 10)); } private int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | (uint)((value > 31 ? 31 : value) << 10); }
private int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 15)) | (uint)((value > 31 ? 31 : value) << 15)); } private int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 15)) | (uint)((value > 31 ? 31 : value) << 15); }
private int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); } private int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 20)) | (uint)((value > 31 ? 31 : value) << 20); }
private int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); } private int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 25)) | (uint)((value > 31 ? 31 : value) << 25); }
/// <summary> /// <summary>
/// Roamer's IVs. /// Roamer's IVs.

View file

@ -28,12 +28,12 @@ public sealed class Roamer4
// 0x13 alignment, unused // 0x13 alignment, unused
// Derived Properties // Derived Properties
private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); } private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | (uint)((value > 31 ? 31 : value) << 00); }
private int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 05)) | (uint)((value > 31 ? 31 : value) << 05)); } private int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | (uint)((value > 31 ? 31 : value) << 05); }
private int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 10)) | (uint)((value > 31 ? 31 : value) << 10)); } private int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | (uint)((value > 31 ? 31 : value) << 10); }
private int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 15)) | (uint)((value > 31 ? 31 : value) << 15)); } private int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 15)) | (uint)((value > 31 ? 31 : value) << 15); }
private int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); } private int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 20)) | (uint)((value > 31 ? 31 : value) << 20); }
private int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); } private int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 25)) | (uint)((value > 31 ? 31 : value) << 25); }
/// <summary> /// <summary>
/// Roamer's IVs. /// Roamer's IVs.

View file

@ -39,7 +39,7 @@ public sealed class Daycare5 : SaveBlock<SAV5>
private int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(slot) + 4 + PokeCrypto.SIZE_5PARTY; private int GetDaycareEXPOffset(int slot) => GetDaycareSlotOffset(slot) + 4 + PokeCrypto.SIZE_5PARTY;
public bool? IsOccupied(int slot) => ReadUInt32LittleEndian(Data.AsSpan(GetDaycareSlotOffset(slot))) == 1; public bool? IsOccupied(int slot) => ReadUInt32LittleEndian(Data.AsSpan(GetDaycareSlotOffset(slot))) == 1;
public void SetOccupied(int slot, bool occupied) => WriteUInt32LittleEndian(Data.AsSpan(GetDaycareSlotOffset(slot)), (uint)(occupied ? 1 : 0)); public void SetOccupied(int slot, bool occupied) => WriteUInt32LittleEndian(Data.AsSpan(GetDaycareSlotOffset(slot)), occupied ? 1u : 0);
public uint? GetEXP(int slot) => ReadUInt32LittleEndian(Data.AsSpan(GetDaycareEXPOffset(slot))); public uint? GetEXP(int slot) => ReadUInt32LittleEndian(Data.AsSpan(GetDaycareEXPOffset(slot)));
public void SetEXP(int slot, uint EXP) => WriteUInt32LittleEndian(Data.AsSpan(GetDaycareEXPOffset(slot)), EXP); public void SetEXP(int slot, uint EXP) => WriteUInt32LittleEndian(Data.AsSpan(GetDaycareEXPOffset(slot)), EXP);

View file

@ -1,4 +1,4 @@
using System; using System;
using static System.Buffers.Binary.BinaryPrimitives; using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core; namespace PKHeX.Core;
@ -50,7 +50,7 @@ public sealed class EntreeForest
{ {
var slots = new EntreeSlot[TotalSlots]; var slots = new EntreeSlot[TotalSlots];
for (int i = 0; i < slots.Length; i++) for (int i = 0; i < slots.Length; i++)
slots[i] = new EntreeSlot(Data, i * 4) { Area = GetSlotArea(i) }; slots[i] = new EntreeSlot(Data.AsMemory(i * EntreeSlot.SIZE, EntreeSlot.SIZE)) { Area = GetSlotArea(i) };
return slots; return slots;
} }
} }
@ -116,6 +116,6 @@ public sealed class EntreeForest
0 => EntreeForestArea.Center, 0 => EntreeForestArea.Center,
1 => EntreeForestArea.Left, 1 => EntreeForestArea.Left,
2 => EntreeForestArea.Right, 2 => EntreeForestArea.Right,
_ => throw new ArgumentOutOfRangeException(), _ => throw new ArgumentOutOfRangeException(nameof(index)),
}; };
} }

View file

@ -14,7 +14,7 @@ public sealed class EntreeSlot : ISpeciesForm
public ushort Species // bits 0-10 public ushort Species // bits 0-10
{ {
get => (ushort)((RawValue & 0x3FF) >> 0); get => (ushort)((RawValue & 0x3FF) >> 0);
set => RawValue = (RawValue & 0xFFFF_F800) | ((uint)(value & 0x3FF) << 0); set => RawValue = (RawValue & 0xFFFF_F800) | ((value & 0x3FFu) << 0);
} }
/// <summary> /// <summary>
@ -23,7 +23,7 @@ public sealed class EntreeSlot : ISpeciesForm
public ushort Move // bits 11-20 public ushort Move // bits 11-20
{ {
get => (ushort)((RawValue & 0x001F_F800) >> 11); get => (ushort)((RawValue & 0x001F_F800) >> 11);
set => RawValue = (RawValue & 0xFFE0_07FF) | ((uint)(value & 0x3FF) << 11); set => RawValue = (RawValue & 0xFFE0_07FF) | ((value & 0x3FFu) << 11);
} }
/// <summary> /// <summary>
@ -41,7 +41,7 @@ public sealed class EntreeSlot : ISpeciesForm
public byte Form // bits 23-27 public byte Form // bits 23-27
{ {
get => (byte)((RawValue & 0x0F80_0000) >> 23); get => (byte)((RawValue & 0x0F80_0000) >> 23);
set => RawValue = (RawValue & 0xF07F_FFFF) | ((uint)(value & 0x1F) << 23); set => RawValue = (RawValue & 0xF07F_FFFF) | ((value & 0x1Fu) << 23);
} }
/// <summary> /// <summary>
@ -62,25 +62,29 @@ public sealed class EntreeSlot : ISpeciesForm
set => RawValue = ((RawValue << 3) >> 3) | (uint)((value & 0x7) << 29); set => RawValue = ((RawValue << 3) >> 3) | (uint)((value & 0x7) << 29);
} }
private readonly byte[] Data; private Memory<byte> Data { get; }
private readonly int Offset;
/// <summary> /// <summary>
/// Raw Data Value /// Raw Data Value
/// </summary> /// </summary>
public uint RawValue public uint RawValue
{ {
get => ReadUInt32LittleEndian(Data.AsSpan(Offset)); get => ReadUInt32LittleEndian(Data.Span);
set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value); set => WriteUInt32LittleEndian(Data.Span, value);
} }
/// <summary>
/// Resets the raw data value to 0.
/// </summary>
public void Delete() => RawValue = 0; public void Delete() => RawValue = 0;
public const int SIZE = 4;
/// <summary>
/// Indicates which area the slot data originated from.
/// Extra metadata for the slot which is not stored in the raw data.
/// </summary>
public EntreeForestArea Area { get; init; } public EntreeForestArea Area { get; init; }
public EntreeSlot(byte[] data, int ofs) public EntreeSlot(Memory<byte> data) => Data = data;
{
Data = data;
Offset = ofs;
}
} }

View file

@ -29,12 +29,12 @@ public sealed class Roamer5
public byte Unk13 { get => Data[0x13]; set => Data[0x13] = value; } // likely just alignment public byte Unk13 { get => Data[0x13]; set => Data[0x13] = value; } // likely just alignment
// Derived Properties // Derived Properties
private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 00)) | (uint)((value > 31 ? 31 : value) << 00)); } private int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | (uint)((value > 31 ? 31 : value) << 00); }
private int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 05)) | (uint)((value > 31 ? 31 : value) << 05)); } private int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | (uint)((value > 31 ? 31 : value) << 05); }
private int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 10)) | (uint)((value > 31 ? 31 : value) << 10)); } private int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | (uint)((value > 31 ? 31 : value) << 10); }
private int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 15)) | (uint)((value > 31 ? 31 : value) << 15)); } private int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 15)) | (uint)((value > 31 ? 31 : value) << 15); }
private int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 20)) | (uint)((value > 31 ? 31 : value) << 20)); } private int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 20)) | (uint)((value > 31 ? 31 : value) << 20); }
private int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); } private int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 25)) | (uint)((value > 31 ? 31 : value) << 25); }
/// <summary> /// <summary>
/// Roamer's IVs. /// Roamer's IVs.

View file

@ -5,7 +5,7 @@ namespace PKHeX.Core;
public sealed class Puff6 : SaveBlock<SAV6> public sealed class Puff6 : SaveBlock<SAV6>
{ {
private const int MaxPuffID = 26; // Supreme Winter Poké Puff private const byte MaxPuffID = 26; // Supreme Winter Poké Puff
private const int PuffSlots = 100; private const int PuffSlots = 100;
public Puff6(SAV6 SAV, int offset) : base(SAV) => Offset = offset; public Puff6(SAV6 SAV, int offset) : base(SAV) => Offset = offset;
@ -21,37 +21,41 @@ public sealed class Puff6 : SaveBlock<SAV6>
public void Reset() public void Reset()
{ {
Array.Clear(Data, Offset, PuffSlots); var puffs = GetPuffs();
puffs.Clear();
// Set the first few default Puffs // Set the first few default Puffs
Data[Offset + 0] = 1; puffs[0] = 1;
Data[Offset + 1] = 2; puffs[1] = 2;
Data[Offset + 2] = 3; puffs[2] = 3;
Data[Offset + 3] = 4; puffs[3] = 4;
Data[Offset + 4] = 5; puffs[4] = 5;
PuffCount = 5; PuffCount = 5;
} }
public void MaxCheat(bool special = false) public void MaxCheat(bool special = false)
{ {
var rnd = Util.Rand; var rnd = Util.Rand;
var puffs = GetPuffs();
if (special) if (special)
{ {
for (int i = 0; i < PuffSlots; i++) foreach (ref var puff in puffs)
Data[Offset + i] = (byte)(21 + rnd.Next(2)); // Supreme Wish or Honor puff = (byte)(21 + rnd.Next(2)); // Supreme Wish or Honor
} }
else else
{ {
for (int i = 0; i < PuffSlots; i++) int i = 0;
Data[Offset + i] = (byte)((i % MaxPuffID) + 1); foreach (ref var puff in puffs)
rnd.Shuffle(Data.AsSpan(Offset, PuffSlots)); puff = (byte)((i++ % MaxPuffID) + 1);
rnd.Shuffle(puffs);
} }
PuffCount = PuffSlots; PuffCount = PuffSlots;
} }
public void Sort(bool reverse = false) public void Sort(bool reverse = false)
{ {
Array.Sort(Data, Offset, PuffCount); var puffs = GetPuffs();
puffs.Sort();
if (reverse) if (reverse)
Array.Reverse(Data, Offset, PuffCount); puffs.Reverse();
} }
} }

View file

@ -1121,16 +1121,19 @@ public sealed class PokedexSave8a
if (hash == 0xCBF29CE484222645) if (hash == 0xCBF29CE484222645)
return 0; return 0;
return (int)(uint)SaveFile.Accessor.GetBlockValue((uint)(hash & 0xFFFFFFFF)); return (int)(uint)SaveFile.Accessor.GetBlockValue(GetSaveBlockKey(hash));
} }
private static uint GetSaveBlockKey(ulong hash) => (uint)hash; // truncate to 32-bit
private int GetSpeciesQuestState(ulong hash) private int GetSpeciesQuestState(ulong hash)
{ {
if (hash is 0xC0EA47549AB5F3D9 or 0xCBF29CE484222645) if (hash is 0xC0EA47549AB5F3D9 or 0xCBF29CE484222645)
return 0; return 0;
// These are single-byte blocks, but type is "object"... // These are single-byte blocks, but type is "object"...
return SaveFile.Accessor.GetBlock((uint)(hash & 0xFFFFFFFF)).Data[0]; var key = GetSaveBlockKey(hash);
return SaveFile.Accessor.GetBlock(key).Data[0];
} }
public static bool IsAnyTaskTriggered(ushort species, PokedexResearchTaskType8a which, MoveType moveType, int move, PokedexTimeOfDay8a timeOfDay) public static bool IsAnyTaskTriggered(ushort species, PokedexResearchTaskType8a which, MoveType moveType, int move, PokedexTimeOfDay8a timeOfDay)

View file

@ -40,8 +40,8 @@ public sealed class PokedexSaveData
public bool IsPokedexCompleted(PokedexType8a which) => (GlobalData.Flags & (which < PokedexType8a.Count ? (1 << (int)which) : 1)) != 0; public bool IsPokedexCompleted(PokedexType8a which) => (GlobalData.Flags & (which < PokedexType8a.Count ? (1 << (int)which) : 1)) != 0;
public bool IsPokedexPerfect(PokedexType8a which) => (GlobalData.Flags & ((which < PokedexType8a.Count ? (1 << (int)which) : 1) << 6)) != 0; public bool IsPokedexPerfect(PokedexType8a which) => (GlobalData.Flags & ((which < PokedexType8a.Count ? (1 << (int)which) : 1) << 6)) != 0;
public void SetPokedexCompleted(PokedexType8a which) => GlobalData.Flags |= (uint)(which < PokedexType8a.Count ? (1 << (int)which) : 1); public void SetPokedexCompleted(PokedexType8a which) => GlobalData.Flags |= which < PokedexType8a.Count ? (1u << (int)which) : 1;
public void SetPokedexPerfect(PokedexType8a which) => GlobalData.Flags |= (uint)((which < PokedexType8a.Count ? (1 << (int)which) : 1) << 6); public void SetPokedexPerfect(PokedexType8a which) => GlobalData.Flags |= (which < PokedexType8a.Count ? (1u << (int)which) : 1) << 6;
public PokedexSaveResearchEntry GetResearchEntry(ushort species) => ResearchEntries[species]; public PokedexSaveResearchEntry GetResearchEntry(ushort species) => ResearchEntries[species];

View file

@ -86,7 +86,9 @@ public sealed class Zukan4 : ZukanBase<SAV4>
const int brSize = 0x40; const int brSize = 0x40;
if (species == (int)Species.Deoxys) if (species == (int)Species.Deoxys)
{ {
uint val = (uint)(Data[0x4 + (1 * brSize) - 1] | (Data[0x4 + (2 * brSize) - 1] << 8)); var br1 = Data[0x4 + (1 * brSize) - 1];
var br2 = Data[0x4 + (2 * brSize) - 1];
uint val = (uint)(br1 | (br2 << 8));
return GetDexFormValues(val, 4, 4); return GetDexFormValues(val, 4, 4);
} }