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 Move4_PPUps => Data[0xB];
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_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 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_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 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_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); }
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 = (IV32 & ~(0x1Fu << 05)) | (uint)((value > 31 ? 31 : value) << 05); }
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 = (IV32 & ~(0x1Fu << 15)) | (uint)((value > 31 ? 31 : value) << 15); }
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 = (IV32 & ~(0x1Fu << 25)) | (uint)((value > 31 ? 31 : value) << 25); }
public uint PID => ReadUInt32LittleEndian(Data.AsSpan(0x10));
public ushort Species => ReadUInt16LittleEndian(Data.AsSpan(0x14));
public ushort HeldItem => ReadUInt16LittleEndian(Data.AsSpan(0x16));

View file

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

View file

@ -273,22 +273,33 @@ public sealed class MetDataSource
BD or SP => Partition2(MetGen8b, IsMetLocation8BDSP),
PLA => Partition2(MetGen8a, IsMetLocation8LA),
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];
return GetOrderedList(list, result, criteria);
ReorderList(span, result, criteria);
return result;
}
static IReadOnlyList<ComboItem> GetOrderedList(IReadOnlyList<ComboItem> list, ComboItem[] result,
Func<ushort, bool> criteria, int start = 0)
static ComboItem[] Partition2(List<ComboItem> list, Func<ushort, bool> criteria, int keepFirst = 3)
{
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 non-matches starting at the end. reverse before returning
int end = list.Count - 1;
for (var index = start; index < list.Count; index++)
int end = list.Length - 1;
for (var index = start; index < list.Length; index++)
{
var item = list[index];
if (criteria((ushort)item.Value))
@ -297,18 +308,8 @@ public sealed class MetDataSource
result[end--] = item;
}
// since the non-matches are reversed in order, we swap them back since we know where they end up at.
Array.Reverse(result, start, list.Count - start);
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);
// since the non-matches are reversed in order, we reverse that section.
result[start..].Reverse();
}
}

View file

@ -8,7 +8,7 @@ namespace PKHeX.Core;
public static class LocationsHOME
{
// 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 SWSL = 59997; // SL traded to SW(SH)
public const ushort SHSP = 59998; // SP traded to (SW)SH

View file

@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
@ -59,7 +60,13 @@ public static class BulkGenerator
template.Form = form;
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)
return null;

View file

@ -61,7 +61,7 @@ internal static class Encounters2
new(223, 40, GS) { Location = 039 }, // Remoraid Swarm @ Route 44 (Super Rod)
// Roamer
new(245, 40, GS) { Location = 002 }, // Suicune
new(245, 40, GS) { Location = 002 }, // Suicune
};
public static readonly EncounterStatic2[] StaticGD =

View file

@ -167,23 +167,23 @@ public static class EncounterFinder
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;
if (WasEventEgg(pk, gen))
if (WasEventEgg(pk, generation))
return LEncGiftEggEvent;
if (WasEvent(pk, gen))
if (WasEvent(pk, generation))
return LEncGiftNotFound;
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
4 => (uint)(loc - 2009) <= (2014 - 2009) || (pk.Format != 4 && (loc == Locations.Faraway4 && pk.HGSS)),
5 => loc is Locations.Breeder5,
_ => loc is Locations.Breeder6,
4 => eggLocation - 2009u <= (2014 - 2009) || (pk.Format != 4 && (eggLocation == Locations.Faraway4 && pk.HGSS)),
5 => eggLocation is Locations.Breeder5,
_ => eggLocation is Locations.Breeder6,
};
private static bool WasEventEgg(PKM pk, int gen) => gen switch

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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 IEncounterable Current { get; private set; }

View file

@ -5,6 +5,9 @@ using System.Diagnostics;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// Iterates to find potentially matched encounters for <see cref="GameVersion.Gen2"/>.
/// </summary>
public record struct EncounterEnumerator2 : IEnumerator<MatchedEncounter<IEncounterable>>
{
private IEncounterable? Deferred;
@ -79,25 +82,7 @@ public record struct EncounterEnumerator2 : IEnumerator<MatchedEncounter<IEncoun
case YieldState.Start:
if (Chain.Length == 0)
break;
goto case YieldState.EventStart;
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;
State = YieldState.Trade; goto case YieldState.Trade;
case YieldState.Trade:
if (TryGetNext(Encounters2.TradeGift_GSC))
@ -170,7 +155,25 @@ public record struct EncounterEnumerator2 : IEnumerator<MatchedEncounter<IEncoun
return true;
Index = 0; goto 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;
case YieldState.EventGB:
if (TryGetNext(Encounters2GBEra.StaticEventsGB))
return true;
State = YieldState.Fallback; goto case YieldState.Fallback;
case YieldState.Fallback:
State = YieldState.End;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
private IEncounterable? Deferred;

View file

@ -5,6 +5,9 @@ using System.Diagnostics;
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>>
{
private IEncounterable? Deferred;

View file

@ -4,6 +4,9 @@ using System.Collections.Generic;
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>>
{
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;
/// <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>
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>
byte MagnetPullCount { get; }
/// <summary>
/// Indicates if the slot can be yielded by <see cref="Ability.Static"/>.
/// </summary>
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 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)
{
var shift = (uint)((int)game - (int)W);
var shift = (uint)(game - W);
if (shift >= 4)
return false;

View file

@ -24,8 +24,12 @@ public static class MoveBreed4
if (count == -1)
count = moves.Length;
var learn = GameData.GetLearnSource(version);
var learnset = learn.GetLearnset(species, 0);
var learnset = version switch
{
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
{
HG or SS => PersonalTable.HGSS,

View file

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

View file

@ -9,6 +9,16 @@ public static class Tera9RNG
{
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)
{
if (gem.IsSpecified(out var type))
@ -43,6 +53,14 @@ public static class Tera9RNG
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)
{
if (gem.IsSpecified(out var type))
@ -61,6 +79,9 @@ public static class Tera9RNG
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;
public static bool IsMatchTeraTypePersonalEgg(in ushort species, in byte form, in byte original) =>
@ -68,7 +89,16 @@ public static class Tera9RNG
? IsMatchTeraTypePersonalAnyForm(species, 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);
/// <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)
{
var pt = PersonalTable.SV;
@ -85,18 +115,27 @@ public static class Tera9RNG
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)
{
var import = TeraTypeUtil.GetTeraTypeImport(pi.Type1, pi.Type2);
return (MoveType)original == import;
}
/// <inheritdoc cref="IsMatchTeraTypeImport"/>
public static bool IsMatchTeraTypePersonalImport(in ushort species, in byte form, in byte original)
{
var pi = PersonalTable.SV[species, form];
return IsMatchTeraTypeImport(pi, original);
}
/// <inheritdoc cref="IsMatchTeraTypeImport"/>
public static bool IsMatchTeraTypePersonalAnyFormImport(in ushort species, in byte original)
{
var pt = PersonalTable.SV;
@ -113,12 +152,29 @@ public static class Tera9RNG
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)
{
var pi = PersonalTable.SV[species, form];
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)
{
// 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);
}
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.
// We need to call this for each Gen1/2 encounter as Version is not stored for those origins.
Generation = gen;
if (gen == -1 && Entity is PK9 { IsUnhatchedEgg: true })
Generation = 9;
}
-1 when Entity is PK9 { IsUnhatchedEgg: true } => 9,
_ => generation,
};
}

View file

@ -185,7 +185,7 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
var rnd = Util.Rand;
var dt = DateTime.Now;
var dt = EncounterDate.GetDateNDS();
if (Day == 0)
{
Day = (byte)dt.Day;
@ -202,7 +202,7 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
Met_Level = currentLevel,
Nature = Nature != -1 ? Nature : rnd.Next(25),
Form = Form,
Version = OriginGame == 0 ? tr.Game : OriginGame,
Version = GetVersion(tr, rnd),
Language = Language == 0 ? tr.Language : Language,
Ball = Ball,
Move1 = Move1,
@ -241,8 +241,6 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
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
{
@ -285,6 +283,23 @@ public sealed class PGF : DataMysteryGift, IRibbonSetEvent3, IRibbonSetEvent4, I
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)
{
pk.IsEgg = true;

View file

@ -17,7 +17,7 @@ public sealed class WC6Full
Data = data;
var wc6 = data.AsSpan(GiftStart).ToArray();
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.RestrictVersion = RestrictVersion;
@ -30,7 +30,7 @@ public sealed class WC6Full
var countgift = data.Length / WC6.Size;
var result = new WC6[countfull + countgift];
var now = DateTime.Now;
var now = EncounterDate.GetDate3DS();
for (int i = 0; i < countfull; i++)
result[i] = ReadWC6(WC6Full, i * Size, now);
for (int i = 0; i < countgift; i++)
@ -39,7 +39,7 @@ public sealed class WC6Full
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();
return new WC6(slice)

View file

@ -17,7 +17,7 @@ public sealed class WC7Full
Data = data;
var wc7 = data.AsSpan(GiftStart).ToArray();
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.RestrictVersion = RestrictVersion;
@ -30,7 +30,7 @@ public sealed class WC7Full
var countgift = data.Length / WC7.Size;
var result = new WC7[countfull + countgift];
var now = DateTime.Now;
var now = EncounterDate.GetDate3DS();
for (int i = 0; i < countfull; i++)
result[i] = ReadWC7(wc7Full, i * Size, now);
for (int i = 0; i < countgift; i++)
@ -39,7 +39,7 @@ public sealed class WC7Full
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();
return new WC7(slice)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -111,8 +111,8 @@ public abstract class SAV6 : SAV_BEEF, ITrainerStatRecord, ISaveBlock6Core, IReg
PK6 pk6 = (PK6)pk;
// Apply to this Save File
int CT = pk6.CurrentHandler;
DateTime Date = DateTime.Now;
pk6.Trade(this, Date.Day, Date.Month, Date.Year);
var now = EncounterDate.GetDate3DS();
pk6.Trade(this, now.Day, now.Month, now.Year);
if (CT != pk6.CurrentHandler) // Logic updated Friendship
{
// 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;
// Apply to this Save File
int CT = pk7.CurrentHandler;
DateTime Date = DateTime.Now;
pk7.Trade(this, Date.Day, Date.Month, Date.Year);
var now = EncounterDate.GetDate3DS();
pk7.Trade(this, now.Day, now.Month, now.Year);
if (CT != pk7.CurrentHandler) // Logic updated Friendship
{
// 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;
// Apply to this Save File
var Date = DateTime.Now;
pb7.Trade(this, Date.Day, Date.Month, Date.Year);
var now = EncounterDate.GetDateSwitch();
pb7.Trade(this, now.Day, now.Month, now.Year);
pb7.RefreshChecksum();
}

View file

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

View file

@ -195,8 +195,8 @@ public sealed class SAV8SWSH : SaveFile, ISaveBlock8SWSH, ITrainerStatRecord, IS
{
PK8 pk8 = (PK8)pk;
// Apply to this Save File
DateTime Date = DateTime.Now;
pk8.Trade(this, Date.Day, Date.Month, Date.Year);
var now = EncounterDate.GetDateSwitch();
pk8.Trade(this, now.Day, now.Month, now.Year);
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;
// Apply to this Save File
DateTime Date = DateTime.Now;
pk9.Trade(this, Date.Day, Date.Month, Date.Year);
var now = EncounterDate.GetDateSwitch();
pk9.Trade(this, now.Day, now.Month, now.Year);
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; }
// 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_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 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_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 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_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); }
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 = (IV32 & ~(0x1Fu << 05)) | (uint)((value > 31 ? 31 : value) << 05); }
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 = (IV32 & ~(0x1Fu << 15)) | (uint)((value > 31 ? 31 : value) << 15); }
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 = (IV32 & ~(0x1Fu << 25)) | (uint)((value > 31 ? 31 : value) << 25); }
/// <summary>
/// Roamer's IVs.

View file

@ -28,12 +28,12 @@ public sealed class Roamer4
// 0x13 alignment, unused
// 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_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 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_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 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_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); }
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 = (IV32 & ~(0x1Fu << 05)) | (uint)((value > 31 ? 31 : value) << 05); }
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 = (IV32 & ~(0x1Fu << 15)) | (uint)((value > 31 ? 31 : value) << 15); }
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 = (IV32 & ~(0x1Fu << 25)) | (uint)((value > 31 ? 31 : value) << 25); }
/// <summary>
/// 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;
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 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;
namespace PKHeX.Core;
@ -50,7 +50,7 @@ public sealed class EntreeForest
{
var slots = new EntreeSlot[TotalSlots];
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;
}
}
@ -116,6 +116,6 @@ public sealed class EntreeForest
0 => EntreeForestArea.Center,
1 => EntreeForestArea.Left,
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
{
get => (ushort)((RawValue & 0x3FF) >> 0);
set => RawValue = (RawValue & 0xFFFF_F800) | ((uint)(value & 0x3FF) << 0);
set => RawValue = (RawValue & 0xFFFF_F800) | ((value & 0x3FFu) << 0);
}
/// <summary>
@ -23,7 +23,7 @@ public sealed class EntreeSlot : ISpeciesForm
public ushort Move // bits 11-20
{
get => (ushort)((RawValue & 0x001F_F800) >> 11);
set => RawValue = (RawValue & 0xFFE0_07FF) | ((uint)(value & 0x3FF) << 11);
set => RawValue = (RawValue & 0xFFE0_07FF) | ((value & 0x3FFu) << 11);
}
/// <summary>
@ -41,7 +41,7 @@ public sealed class EntreeSlot : ISpeciesForm
public byte Form // bits 23-27
{
get => (byte)((RawValue & 0x0F80_0000) >> 23);
set => RawValue = (RawValue & 0xF07F_FFFF) | ((uint)(value & 0x1F) << 23);
set => RawValue = (RawValue & 0xF07F_FFFF) | ((value & 0x1Fu) << 23);
}
/// <summary>
@ -62,25 +62,29 @@ public sealed class EntreeSlot : ISpeciesForm
set => RawValue = ((RawValue << 3) >> 3) | (uint)((value & 0x7) << 29);
}
private readonly byte[] Data;
private readonly int Offset;
private Memory<byte> Data { get; }
/// <summary>
/// Raw Data Value
/// </summary>
public uint RawValue
{
get => ReadUInt32LittleEndian(Data.AsSpan(Offset));
set => WriteUInt32LittleEndian(Data.AsSpan(Offset), value);
get => ReadUInt32LittleEndian(Data.Span);
set => WriteUInt32LittleEndian(Data.Span, value);
}
/// <summary>
/// Resets the raw data value to 0.
/// </summary>
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 EntreeSlot(byte[] data, int ofs)
{
Data = data;
Offset = ofs;
}
public EntreeSlot(Memory<byte> data) => Data = data;
}

View file

@ -29,12 +29,12 @@ public sealed class Roamer5
public byte Unk13 { get => Data[0x13]; set => Data[0x13] = value; } // likely just alignment
// 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_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 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_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 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_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (uint)((IV32 & ~(0x1F << 25)) | (uint)((value > 31 ? 31 : value) << 25)); }
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 = (IV32 & ~(0x1Fu << 05)) | (uint)((value > 31 ? 31 : value) << 05); }
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 = (IV32 & ~(0x1Fu << 15)) | (uint)((value > 31 ? 31 : value) << 15); }
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 = (IV32 & ~(0x1Fu << 25)) | (uint)((value > 31 ? 31 : value) << 25); }
/// <summary>
/// Roamer's IVs.

View file

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

View file

@ -1121,16 +1121,19 @@ public sealed class PokedexSave8a
if (hash == 0xCBF29CE484222645)
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)
{
if (hash is 0xC0EA47549AB5F3D9 or 0xCBF29CE484222645)
return 0;
// 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)

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 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 SetPokedexPerfect(PokedexType8a which) => GlobalData.Flags |= (uint)((which < PokedexType8a.Count ? (1 << (int)which) : 1) << 6);
public void SetPokedexCompleted(PokedexType8a which) => GlobalData.Flags |= which < PokedexType8a.Count ? (1u << (int)which) : 1;
public void SetPokedexPerfect(PokedexType8a which) => GlobalData.Flags |= (which < PokedexType8a.Count ? (1u << (int)which) : 1) << 6;
public PokedexSaveResearchEntry GetResearchEntry(ushort species) => ResearchEntries[species];

View file

@ -86,7 +86,9 @@ public sealed class Zukan4 : ZukanBase<SAV4>
const int brSize = 0x40;
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);
}