Refactoring

Catch_Rate => CatchRate
Make Location* classes public
Extract a few methods, make public
Merge EncounterUtil & EncounterUtil1
add xmldoc
add missing deferral for Nest8 templates
improve binlinker span fetch to a single read (-1)
This commit is contained in:
Kurt 2024-01-03 23:06:09 -08:00
parent cca9d55013
commit 8fa951bcd7
103 changed files with 625 additions and 384 deletions

View File

@ -75,13 +75,15 @@ public static int ApplyBallNext(PKM pk)
private static int ApplyFirstLegalBall(PKM pk, ReadOnlySpan<Ball> balls)
{
var initial = pk.Ball;
foreach (var b in balls)
{
pk.Ball = (int)b;
var test = (int)b;
pk.Ball = test;
if (new LegalityAnalysis(pk).Valid)
break;
return test;
}
return pk.Ball;
return initial; // fail, revert
}
private static int GetBallList(int ball, Span<Ball> result)

View File

@ -1,12 +1,12 @@
namespace PKHeX.Core;
/// <summary>
/// Logic for applying a <see cref="PK1.Catch_Rate"/> value.
/// Logic for applying a <see cref="PK1.CatchRate"/> value.
/// </summary>
public static class CatchRateApplicator
{
/// <summary>
/// Gets the suggested <see cref="PK1.Catch_Rate"/> for the entity.
/// Gets the suggested <see cref="PK1.CatchRate"/> for the entity.
/// </summary>
public static int GetSuggestedCatchRate(PK1 pk, SaveFile sav)
{
@ -15,13 +15,13 @@ public static int GetSuggestedCatchRate(PK1 pk, SaveFile sav)
}
/// <summary>
/// Gets the suggested <see cref="PK1.Catch_Rate"/> for the entity.
/// Gets the suggested <see cref="PK1.CatchRate"/> for the entity.
/// </summary>
public static byte GetSuggestedCatchRate(PK1 pk, SaveFile sav, LegalityAnalysis la)
{
// If it is already valid, just use the current value.
if (la.Valid)
return pk.Catch_Rate;
return pk.CatchRate;
// If it has ever visited generation 2, the Held Item can be removed prior to trade back.
if (la.Info.Generation == 2)
@ -36,7 +36,7 @@ public static byte GetSuggestedCatchRate(PK1 pk, SaveFile sav, LegalityAnalysis
default:
var pt = GetPersonalTable(sav, enc.Version);
var pi = pt[enc.Species];
return (byte)pi.CatchRate;
return pi.CatchRate;
}
}

View File

@ -232,69 +232,68 @@ public IReadOnlyList<ComboItem> GetLocationList(GameVersion version, EntityConte
return result;
}
private IReadOnlyList<ComboItem> GetLocationListInternal(GameVersion version, EntityContext context)
private IReadOnlyList<ComboItem> GetLocationListInternal(GameVersion version, EntityContext context) => version switch
{
return version switch
{
CXD when context == EntityContext.Gen3 => MetGen3CXD,
R or S when context == EntityContext.Gen3 => Partition1(MetGen3, IsMetLocation3RS),
E when context == EntityContext.Gen3 => Partition1(MetGen3, IsMetLocation3E),
FR or LG when context == EntityContext.Gen3 => Partition1(MetGen3, IsMetLocation3FRLG),
D or P when context == EntityContext.Gen4 => Partition2(MetGen4, IsMetLocation4DP, 4),
Pt when context == EntityContext.Gen4 => Partition2(MetGen4, IsMetLocation4Pt, 4),
HG or SS when context == EntityContext.Gen4 => Partition2(MetGen4, IsMetLocation4HGSS, 4),
CXD when context == EntityContext.Gen3 => MetGen3CXD,
R or S when context == EntityContext.Gen3 => Partition1(MetGen3, IsMetLocation3RS),
E when context == EntityContext.Gen3 => Partition1(MetGen3, IsMetLocation3E),
FR or LG when context == EntityContext.Gen3 => Partition1(MetGen3, IsMetLocation3FRLG),
D or P when context == EntityContext.Gen4 => Partition2(MetGen4, IsMetLocation4DP, 4),
Pt when context == EntityContext.Gen4 => Partition2(MetGen4, IsMetLocation4Pt, 4),
HG or SS when context == EntityContext.Gen4 => Partition2(MetGen4, IsMetLocation4HGSS, 4),
B or W => Partition2(MetGen5, IsMetLocation5BW), // Abyssal Ruins
B2 or W2 => MetGen5,
X or Y => Partition2(MetGen6, IsMetLocation6XY),
OR or AS => Partition2(MetGen6, IsMetLocation6AO),
SN or MN => Partition2(MetGen7, IsMetLocation7SM),
US or UM
or RD or BU or GN or YW
or GD or SI or C => Partition2(MetGen7, IsMetLocation7USUM),
GP or GE or GO => Partition2(MetGen7GG, IsMetLocation7GG),
SW or SH => Partition2(MetGen8, IsMetLocation8SWSH),
BD or SP => Partition2(MetGen8b, IsMetLocation8BDSP),
PLA => Partition2(MetGen8a, IsMetLocation8LA),
SL or VL => Partition2(MetGen9, IsMetLocation9SV),
_ => GetLocationListModified(version, context),
};
B or W => Partition2(MetGen5, IsMetLocation5BW), // Abyssal Ruins
B2 or W2 => MetGen5,
static ComboItem[] Partition1(List<ComboItem> list, Func<ushort, bool> criteria)
X or Y => Partition2(MetGen6, IsMetLocation6XY),
OR or AS => Partition2(MetGen6, IsMetLocation6AO),
SN or MN => Partition2(MetGen7, IsMetLocation7SM),
US or UM
or RD or BU or GN or YW
or GD or SI or C => Partition2(MetGen7, IsMetLocation7USUM),
GP or GE or GO => Partition2(MetGen7GG, IsMetLocation7GG),
SW or SH => Partition2(MetGen8, IsMetLocation8SWSH),
BD or SP => Partition2(MetGen8b, IsMetLocation8BDSP),
PLA => Partition2(MetGen8a, IsMetLocation8LA),
SL or VL => Partition2(MetGen9, IsMetLocation9SV),
_ => GetLocationListModified(version, context),
};
private static ComboItem[] Partition1(List<ComboItem> list, Func<ushort, bool> criteria)
{
var span = CollectionsMarshal.AsSpan(list);
var result = new ComboItem[list.Count];
ReorderList(span, result, criteria);
return result;
}
private 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;
}
private 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.Length - 1;
for (var index = start; index < list.Length; index++)
{
var span = CollectionsMarshal.AsSpan(list);
var result = new ComboItem[list.Count];
ReorderList(span, result, criteria);
return result;
var item = list[index];
if (criteria((ushort)item.Value))
result[start++] = item;
else
result[end--] = item;
}
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.Length - 1;
for (var index = start; index < list.Length; index++)
{
var item = list[index];
if (criteria((ushort)item.Value))
result[start++] = item;
else
result[end--] = item;
}
// since the non-matches are reversed in order, we reverse that section.
result[start..].Reverse();
}
// since the non-matches are reversed in order, we reverse that section.
result[start..].Reverse();
}
/// <summary>

View File

@ -129,11 +129,7 @@ public static class Locations
private const int SafariLocation_RSE = 57;
private const int SafariLocation_FRLG = 136;
private const int SafariLocation_HGSS = 202;
private const int MarshLocation_DPPt = 52;
public static bool IsSafariZoneLocation3(int loc) => loc is SafariLocation_RSE or SafariLocation_FRLG;
public static bool IsSafariZoneLocation4(int loc) => loc is MarshLocation_DPPt or SafariLocation_HGSS;
public static bool IsSafariZoneLocation8b(int loc) => loc is (>= 219 and <= 224);
public static bool IsSafariZoneLocation3(byte loc) => loc is SafariLocation_RSE or SafariLocation_FRLG;
public static bool IsEggLocationBred4(int loc, GameVersion ver)
{

View File

@ -2,8 +2,18 @@
namespace PKHeX.Core;
internal static class Locations4
/// <summary>
/// Locations for <see cref="GameVersion.Gen4"/>.
/// </summary>
public static class Locations4
{
private const int SafariLocation_HGSS = 202;
private const int MarshLocation_DPPt = 52;
public static bool IsSafariZoneLocation(ushort loc) => loc is MarshLocation_DPPt or SafariLocation_HGSS;
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<byte> Met0 =>
[
000, 001, 002, 003, 004, 005, 006, 007, 008, 009,
@ -32,13 +42,19 @@ internal static class Locations4
230, 231, 232, 233, 234,
];
// Ignore the --- met location at index 7.
/// <summary>
/// Available location list for the 02000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met2 =>
[
// Ignore the --- met location at index 7.
2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009,
2010, 2011, 2012, 2013, 2014,
];
/// <summary>
/// Available location list for the 03000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009,

View File

@ -2,8 +2,14 @@
namespace PKHeX.Core;
internal static class Locations5
/// <summary>
/// Locations for <see cref="GameVersion.Gen5"/>.
/// </summary>
public static class Locations5
{
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<byte> Met0 =>
[
001, 002, 004, 005, 006, 007, 008, 009,
@ -24,12 +30,18 @@ internal static class Locations5
150, 151, 152, 153,
];
/// <summary>
/// Available location list for the 30000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
30001, 30002, 30004, 30005, 30006, 30007, 30008,
30010, 30011, 30012, 30013, 30014, 30015,
];
/// <summary>
/// Available location list for the 40000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met4 =>
[
40001, 40002, 40003, 40004, 40005, 40006, 40007, 40008, 40009,
@ -45,5 +57,8 @@ internal static class Locations5
40100, 40101, 40102, 40103, 40104, 40105, 40106, 40107, 40108, 40109,
];
/// <summary>
/// Available location list for the 60000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met6 => [60001, 60003];
}

View File

@ -2,8 +2,14 @@
namespace PKHeX.Core;
internal static class Locations6
/// <summary>
/// Locations for <see cref="GameVersion.Gen6"/>.
/// </summary>
public static class Locations6
{
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met0 =>
[
/* X/Y */
@ -47,12 +53,18 @@ internal static class Locations6
350, 352, 354,
];
/// <summary>
/// Available location list for the 30000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
30001, 30003, 30004, 30005, 30006, 30007, 30008, 30009,
30010, 30011,
];
/// <summary>
/// Available location list for the 40000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met4 =>
[
40001, 40002, 40003, 40004, 40005, 40006, 40007, 40008, 40009,
@ -65,5 +77,8 @@ internal static class Locations6
40070, 40071, 40072, 40073, 40074, 40075, 40076, 40077, 40078, 40079,
];
/// <summary>
/// Available location list for the 60000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met6 => [/* X/Y */ 60001, 60003, /* OR/AS */ 60004];
}

View File

@ -2,8 +2,14 @@
namespace PKHeX.Core;
internal static class Locations7
/// <summary>
/// Locations for <see cref="GameVersion.Gen7"/>.
/// </summary>
public static class Locations7
{
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<byte> Met0 =>
[
002, 004, 006, 008,
@ -32,12 +38,18 @@ internal static class Locations7
230, 232,
];
/// <summary>
/// Available location list for the 30000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
30001, 30003, 30004, 30005, 30006, 30007, 30008, 30009,
30010, 30011, 30012, 30013, 30014, 30015, 30016, 30017,
];
/// <summary>
/// Available location list for the 40000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met4 =>
[
40001, 40002, 40003, 40004, 40005, 40006, 40007, 40008, 40009,
@ -51,5 +63,8 @@ internal static class Locations7
40080, 40081, 40082, 40083, 40084, 40085, 40086, 40087, 40088,
];
/// <summary>
/// Available location list for the 60000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met6 => [/* X/Y */ 60001, 60003, /* OR/AS */ 60004];
}

View File

@ -2,8 +2,14 @@
namespace PKHeX.Core;
internal static class Locations7b
/// <summary>
/// Locations for <see cref="GameVersion.GG"/>.
/// </summary>
public static class Locations7b
{
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<byte> Met0 =>
[
002, 003, 004, 005, 006, 007, 008, 009,
@ -14,12 +20,18 @@ internal static class Locations7b
050, 051, 052, 053,
];
/// <summary>
/// Available location list for the 30000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
30001, 30003, 30004, 30005, 30006, 30007, 30008, 30009,
30010, 30011, 30012, 30013, 30014, 30015, 30016, 30017,
];
/// <summary>
/// Available location list for the 40000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met4 =>
[
40001, 40002, 40003, 40004, 40005, 40006, 40007, 40008, 40009,
@ -32,5 +44,8 @@ internal static class Locations7b
40070, 40071, 40072, 40073, 40074, 40075, 40076, 40077,
];
/// <summary>
/// Available location list for the 60000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met6 => [/* X/Y */ 60001, 60003, /* OR/AS */ 60004];
}

View File

@ -2,8 +2,14 @@
namespace PKHeX.Core;
internal static class Locations8
/// <summary>
/// Locations for <see cref="GameVersion.SWSH"/>.
/// </summary>
public static class Locations8
{
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<byte> Met0 =>
[
002, 004, 006, 008,
@ -33,12 +39,18 @@ internal static class Locations8
240, 242, 244, 246,
];
/// <summary>
/// Available location list for the 30000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
30001, 30003, 30004, 30005, 30006, 30007, 30008, 30009,
30010, 30011, 30012, 30013, 30014, 30015, 30016, 30017, 30018,
];
/// <summary>
/// Available location list for the 40000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met4 =>
[
40001, 40002, 40003, 40005, 40006, 40007, 40008, 40009,
@ -52,5 +64,8 @@ internal static class Locations8
40080, 40081, 40082, 40083, 40084, 40085, 40086,
];
/// <summary>
/// Available location list for the 60000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met6 => [/* X/Y */ 60001, 60003, /* OR/AS */ 60004];
}

View File

@ -2,8 +2,14 @@
namespace PKHeX.Core;
internal static class Locations8a
/// <summary>
/// Locations for <see cref="GameVersion.PLA"/>.
/// </summary>
public static class Locations8a
{
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<byte> Met0 =>
[
000, 002, 004, 006, 007, 008, 009,
@ -24,6 +30,9 @@ internal static class Locations8a
150, 151, 152, 153, 154, 155,
];
/// <summary>
/// Available location list for the 30000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
30001, 30002, 30003, 30004, 30005, 30006, 30007, 30008, 30009,
@ -31,6 +40,9 @@ internal static class Locations8a
30020, 30021, 30022,
];
/// <summary>
/// Available location list for the 40000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met4 =>
[
40001, 40002, 40003, 40005, 40006, 40007, 40008, 40009,
@ -44,5 +56,8 @@ internal static class Locations8a
40080, 40081, 40082, 40083, 40084, 40085, 40086,
];
/// <summary>
/// Available location list for the 60000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met6 => [/* X/Y */ 60001, 60003, /* OR/AS */ 60004];
}

View File

@ -2,11 +2,28 @@
namespace PKHeX.Core;
internal static class Locations8b
/// <summary>
/// Locations for <see cref="GameVersion.BDSP"/>.
/// </summary>
public static class Locations8b
{
/// <summary>
/// Checks if the location is a Grand Underground location.
/// </summary>
/// <param name="location">Location ID to check</param>
/// <returns>True if the location is a Grand Underground location</returns>
public static bool IsUnderground(ushort location) => location is (>= 508 and <= 617);
/// <summary>
/// Checks if the location is a Great Marsh location.
/// </summary>
/// <param name="location">Location ID to check</param>
/// <returns>True if the location is a Great Marsh location</returns>
public static bool IsMarsh(ushort location) => location is (>= 219 and <= 224);
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met0 =>
[
000, 001, 002, 003, 004, 005, 006, 007, 008, 009,
@ -79,6 +96,9 @@ internal static class Locations8b
648, 649, 650, 651, 652, 653, 654, 655, 656, 657,
];
/// <summary>
/// Available location list for the 30000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
30001, 30003, 30004, 30005, 30006, 30007, 30009,
@ -86,6 +106,9 @@ internal static class Locations8b
30020, 30022,
];
/// <summary>
/// Available location list for the 40000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met4 =>
[
40001, 40002, 40003, 40005, 40006, 40007, 40008, 40009,
@ -98,5 +121,8 @@ internal static class Locations8b
40070, 40071, 40072, 40074, 40075, 40076, 40077,
];
/// <summary>
/// Available location list for the 60000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met6 => [/* X/Y */ 60001, 60003, /* OR/AS */ 60004, /* BD/SP */ 60005, 60006, 60007, 60010];
}

View File

@ -2,6 +2,9 @@
namespace PKHeX.Core;
/// <summary>
/// Locations for <see cref="GameVersion.SV"/>.
/// </summary>
internal static class Locations9
{
/// <summary>
@ -24,6 +27,9 @@ internal static class Locations9
/// </summary>
public static bool IsPaldeaDLC(ushort location) => location is 196 or 198; // Area Zero Underdepths
/// <summary>
/// Available location list for the 00000 set of location names.
/// </summary>
public static ReadOnlySpan<byte> Met0 =>
[
002, 004, 006, 008,
@ -52,6 +58,9 @@ internal static class Locations9
200,
];
/// <summary>
/// Available location list for the 30000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met3 =>
[
30001, 30003, 30004, 30005, 30006, 30007, 30008, 30009,
@ -59,6 +68,9 @@ internal static class Locations9
30020, 30021, 30022, 30023, 30024,
];
/// <summary>
/// Available location list for the 40000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met4 =>
[
40001, 40002, 40003, 40004, 40005, 40006, 40007, 40008, 40009,
@ -71,5 +83,8 @@ internal static class Locations9
40070, 40071, 40072, 40073, 40074, 40075, 40076, 40077, 40078,
];
/// <summary>
/// Available location list for the 60000 set of location names.
/// </summary>
public static ReadOnlySpan<ushort> Met6 => [/* X/Y */ 60001, 60003, /* OR/AS */ 60004, /* S/V */ 60005];
}

View File

@ -15,7 +15,7 @@ namespace PKHeX.Core;
private readonly ReadOnlySpan<byte> Data;
/// <summary> Total count of files available for accessing. </summary>
public int Length => ReadUInt16LittleEndian(Data[2..]);
public ushort Length => ReadUInt16LittleEndian(Data[2..]);
/// <summary> Magic identifier for the file. </summary>
public string Identifier => new([(char)Data[0], (char)Data[1]]);
@ -31,8 +31,11 @@ namespace PKHeX.Core;
private ReadOnlySpan<byte> GetEntry(int index)
{
int offset = 4 + (index * sizeof(int));
int end = ReadInt32LittleEndian(Data[(offset + 4)..]);
int start = ReadInt32LittleEndian(Data[offset..]);
// Start and End are both 32-bit integers, sequentially.
// Read them in one shot a 64-bit integer and decompose.
var startEnd = ReadUInt64LittleEndian(Data[offset..]);
int start = (int)startEnd;
int end = (int)(startEnd >> 32);
return Data[start..end];
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace PKHeX.Core;
@ -7,37 +6,24 @@ namespace PKHeX.Core;
/// <summary>
/// Miscellaneous setup utility for legality checking <see cref="IEncounterTemplate"/> data sources.
/// </summary>
internal static class EncounterUtil
public static class EncounterUtil
{
internal static ReadOnlySpan<byte> Get([Length(2, 2)] string resource) => Util.GetBinaryResource($"encounter_{resource}.pkl");
internal static BinLinkerAccessor Get([Length(2, 2)] string resource, [Length(2, 2)] ReadOnlySpan<byte> ident) => BinLinkerAccessor.Get(Get(resource), ident);
/// <summary> Magic value to indicate the form is dynamic and should not be used literally. </summary>
public const byte FormDynamic = FormVivillon;
/// <summary> Magic value to indicate the form is set by the save file and can be one of many within the permitted range. </summary>
public const byte FormVivillon = 30;
/// <summary> Magic value to indicate the form is random and can be one of many within the permitted range. </summary>
public const byte FormRandom = 31;
internal static T? GetMinByLevel<T>(ReadOnlySpan<EvoCriteria> chain, IEnumerable<T> possible) where T : class, IEncounterTemplate
{
// MinBy grading: prefer species-form match, select lowest min level encounter.
// Minimum allocation :)
T? result = null;
int min = int.MaxValue;
/// <summary>
/// Gets a raw chunk of data from the specified resource.
/// </summary>
public static ReadOnlySpan<byte> Get([Length(2, 2)] string resource) => Util.GetBinaryResource($"encounter_{resource}.pkl");
foreach (var enc in possible)
{
int m = int.MaxValue;
foreach (var evo in chain)
{
bool specDiff = enc.Species != evo.Species || enc.Form != evo.Form;
var val = (Convert.ToInt32(specDiff) << 16) | enc.LevelMin;
if (val < m)
m = val;
}
if (m >= min)
continue;
min = m;
result = enc;
}
return result;
}
/// <summary>
/// Gets an index-able accessor for the specified resource.
/// </summary>
public static BinLinkerAccessor Get([Length(2, 2)] string resource, [Length(2, 2)] ReadOnlySpan<byte> ident) => BinLinkerAccessor.Get(Get(resource), ident);
/// <summary>
/// Grabs the localized names for individual templates for all languages from the specified <see cref="index"/> of the <see cref="names"/> list.
@ -55,4 +41,62 @@ public static string[] GetNamesForLanguage(ReadOnlySpan<string[]> names, uint in
}
return result;
}
/// <summary>
/// Applies the encounter moves for the given level and version to the provided PKM.
/// </summary>
/// <typeparam name="T">Entity type</typeparam>
/// <param name="pk">Entity to apply the moves to</param>
/// <param name="version">Version to apply the moves from</param>
/// <param name="level">Level to apply the moves at</param>
public static void SetEncounterMoves<T>(T pk, GameVersion version, byte level) where T : PKM
{
Span<ushort> moves = stackalloc ushort[4];
var source = GameData.GetLearnSource(version);
source.SetEncounterMoves(pk.Species, pk.Form, level, moves);
pk.SetMoves(moves);
}
/// <summary>
/// Gets a Generation 1-3 trainer name ensuring each character is present in the game's available character set.
/// </summary>
/// <remarks>Only falls back to a valid Name if the language is incompatible with the trainer's language. Doesn't sanity check otherwise.</remarks>
/// <param name="tr">Trainer to apply the name from</param>
/// <param name="lang">Language to apply the name in</param>
public static string GetTrainerName(ITrainerInfo tr, int lang) => lang switch
{
(int)LanguageID.Japanese => tr.Language == 1 ? tr.OT : "ゲーフリ",
_ => tr.Language == 1 ? "GF" : tr.OT,
};
/// <summary>
/// Gets a random DV16 value.
/// </summary>
/// <param name="rand">Random number generator to use</param>
/// <returns>Value between 0 and 65535 (inclusive)</returns>
public static ushort GetRandomDVs(Random rand) => (ushort)rand.Next(ushort.MaxValue + 1);
/// <summary>
/// Mashes the IVs into a DV16 value.
/// </summary>
public static ushort GetDV16(in IndividualValueSet actual)
{
ushort result = 0;
result |= (ushort)(actual.SPA << 0);
result |= (ushort)(actual.SPE << 4);
result |= (ushort)(actual.DEF << 8);
result |= (ushort)(actual.ATK << 12);
return result;
}
/// <summary>
/// Gets the Generation 1 personal info for a Species.
/// </summary>
/// <param name="version">Pivot version</param>
/// <param name="species">Species to get the personal info for</param>
public static PersonalInfo1 GetPersonal1(GameVersion version, ushort species)
{
var pt = version == GameVersion.YW ? PersonalTable.Y : PersonalTable.RB;
return pt[species];
}
}

View File

@ -142,7 +142,7 @@ private static bool VerifySecondaryChecks(PKM pk, LegalInfo info, PeekEnumerator
{
if (!ParseSettings.AllowGen1Tradeback)
return false;
if (!PK1.IsCatchRateHeldItem(pk1.Catch_Rate))
if (!PK1.IsCatchRateHeldItem(pk1.CatchRate))
return false;
}
}

View File

@ -33,8 +33,17 @@ public static class EncounterGenerator
_ => EncounterGeneratorDummy.Instance.GetEncounters(pk, info),
};
/// <summary>
/// Gets the <see cref="IEncounterGenerator"/> for the given <see cref="GameVersion"/>.
/// </summary>
/// <param name="version">Original encounter version</param>
public static IEncounterGenerator GetGenerator(GameVersion version) => GetGeneration(version, version.GetGeneration());
/// <summary>
/// Gets the <see cref="IEncounterGenerator"/> for the given <see cref="GameVersion"/>.
/// </summary>
/// <param name="version">Original encounter version</param>
/// <param name="generation">Generation group</param>
public static IEncounterGenerator GetGeneration(GameVersion version, int generation) => generation switch
{
1 => EncounterGenerator1.Instance,

View File

@ -14,13 +14,14 @@ public static class EncounterMovesetGenerator
/// <summary>
/// Order in which <see cref="IEncounterable"/> objects are yielded from the <see cref="GenerateVersionEncounters(PKM,ReadOnlyMemory{ushort},GameVersion)"/> method.
/// </summary>
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
public static IReadOnlyCollection<EncounterTypeGroup> PriorityList { get; set; } = PriorityList = (EncounterTypeGroup[])Enum.GetValues(typeof(EncounterTypeGroup));
public static IReadOnlyCollection<EncounterTypeGroup> PriorityList { get; set; } = GetAllGroups();
/// <summary>
/// Resets the <see cref="PriorityList"/> to the default values.
/// </summary>
public static void ResetFilters() => PriorityList = (EncounterTypeGroup[])Enum.GetValues(typeof(EncounterTypeGroup));
public static void ResetFilters() => PriorityList = GetAllGroups();
private static EncounterTypeGroup[] GetAllGroups() => (EncounterTypeGroup[])Enum.GetValues(typeof(EncounterTypeGroup));
/// <summary>
/// Gets possible <see cref="IEncounterable"/> objects that allow all moves requested to be learned.

View File

@ -40,6 +40,9 @@ public static class EncounterDate
_ => throw new ArgumentOutOfRangeException(nameof(console), console, null),
};
/// <summary>
/// Checks if the date is valid for the Nintendo DS.
/// </summary>
public static bool IsValidDateNDS(DateOnly date)
{
if (date.Year is < 2000 or > 2099)
@ -47,6 +50,9 @@ public static bool IsValidDateNDS(DateOnly date)
return true;
}
/// <summary>
/// Checks if the date is valid for the Nintendo 3DS.
/// </summary>
public static bool IsValidDate3DS(DateOnly date)
{
if (date.Year is < 2000 or > 2050)
@ -54,6 +60,9 @@ public static bool IsValidDate3DS(DateOnly date)
return true;
}
/// <summary>
/// Checks if the date is valid for the Nintendo Switch.
/// </summary>
public static bool IsValidDateSwitch(DateOnly date)
{
if (date.Year is < 2000 or > 2050)
@ -72,6 +81,7 @@ public sealed class DefaultTimeProvider : ITimeProvider
/// </summary>
public static readonly DefaultTimeProvider Instance = new();
/// <inheritdoc cref="DateTime.Now"/>
public DateTime Now => DateTime.Now;
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
@ -25,8 +26,8 @@ public static class EncounterSuggestion
var generator = EncounterGenerator.GetGenerator(ver);
var evos = chain[..count].ToArray();
var w = EncounterUtil.GetMinByLevel(evos, generator.GetPossible(pk, evos, ver, EncounterTypeGroup.Slot));
var s = EncounterUtil.GetMinByLevel(evos, generator.GetPossible(pk, evos, ver, EncounterTypeGroup.Static));
var w = EncounterSelection.GetMinByLevel(evos, generator.GetPossible(pk, evos, ver, EncounterTypeGroup.Slot));
var s = EncounterSelection.GetMinByLevel(evos, generator.GetPossible(pk, evos, ver, EncounterTypeGroup.Static));
if (w is null)
return s is null ? null : GetSuggestedEncounter(pk, s, loc);
@ -41,7 +42,7 @@ public static class EncounterSuggestion
return GetSuggestedEncounter(pk, w, loc);
}
private static bool IsSpeciesFormMatch(ReadOnlySpan<EvoCriteria> evos, ISpeciesForm encounter)
private static bool IsSpeciesFormMatch<T>(ReadOnlySpan<EvoCriteria> evos, T encounter) where T : ISpeciesForm
{
foreach (var evo in evos)
{
@ -143,11 +144,21 @@ private static int GetMaxLevelMax(ReadOnlySpan<EvoCriteria> evos)
return max;
}
public static bool IterateMinimumCurrentLevel(PKM pk, bool isLegal, int max = 100)
/// <summary>
/// Iterates through all possible levels to drop to the lowest level possible.
/// </summary>
/// <param name="pk">Entity to modify</param>
/// <param name="isLegal">Current state is legal or invalid (false)</param>
/// <param name="level">Maximum level to iterate down from</param>
/// <returns>True if the level was changed, false if it was already at the lowest level possible or impossible.</returns>
public static bool IterateMinimumCurrentLevel(PKM pk, bool isLegal, int level = 100)
{
// Find the lowest level possible while still remaining legal.
var original = pk.CurrentLevel;
var growth = pk.PersonalInfo.EXPGrowth;
var table = Experience.GetTable(growth);
var originalEXP = pk.EXP;
var original = Experience.GetLevel(originalEXP, table);
if (isLegal)
{
// If we can't go any lower, we're already at the lowest level possible.
@ -155,28 +166,31 @@ public static bool IterateMinimumCurrentLevel(PKM pk, bool isLegal, int max = 10
return false;
// Skip to original - 1, since all levels [original,max] are already legal.
max = original - 1;
level = original - 1;
}
// If it's not legal, then we'll first try the max level and abort if it will never be legal.
// Find the first level that is illegal via searching downwards, and set it to the level above it.
for (int i = max; i != 0; i--)
while (level != 0)
{
pk.CurrentLevel = i;
pk.EXP = table[level];
var la = new LegalityAnalysis(pk);
var valid = la.Valid;
if (valid)
{
--level;
continue;
}
// First illegal level found, revert to the previous level.
var revert = Math.Min(100, i + 1);
if (revert == original) // same, revert actual EXP value.
level = Math.Min(100, level + 1);
if (level == original) // same, revert actual EXP value.
{
pk.EXP = originalEXP;
return false;
}
pk.CurrentLevel = revert;
pk.EXP = table[level];
return true;
}
@ -208,3 +222,33 @@ public static int GetSuggestedMetLevel(PKM pk, int minLevel)
return Math.Max(minMove, minLevel);
}
}
internal static class EncounterSelection
{
internal static T? GetMinByLevel<T>(ReadOnlySpan<EvoCriteria> chain, IEnumerable<T> possible) where T : class, IEncounterTemplate
{
// MinBy grading: prefer species-form match, select lowest min level encounter.
// Minimum allocation :)
T? result = null;
int min = int.MaxValue;
foreach (var enc in possible)
{
int m = int.MaxValue;
foreach (var evo in chain)
{
bool specDiff = enc.Species != evo.Species || enc.Form != evo.Form;
var val = (Convert.ToInt32(specDiff) << 16) | enc.LevelMin;
if (val < m)
m = val;
}
if (m >= min)
continue;
min = m;
result = enc;
}
return result;
}
}

View File

@ -61,7 +61,7 @@ public PB7 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
ID32 = tr.ID32,
};
SetPINGA(pk, criteria);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation);
SetEncounterMoves(pk, LevelMin);
pk.AwakeningSetAllTo(2);

View File

@ -137,7 +137,7 @@ public PKM ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
l.HT_Language = 2;
}
SetPINGA(pk, criteria);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation);
SetEncounterMoves(pk, LevelMin);

View File

@ -51,15 +51,15 @@ public PK1 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
{
var lang = GetTemplateLanguage(tr);
var isJapanese = lang == (int)LanguageID.Japanese;
var pi = EncounterUtil1.GetPersonal1(Version, Species);
var pi = EncounterUtil.GetPersonal1(Version, Species);
var pk = new PK1(isJapanese)
{
Species = Species,
CurrentLevel = LevelMin,
Catch_Rate = GetInitialCatchRate(),
DV16 = IVs.IsSpecified ? EncounterUtil1.GetDV16(IVs) : EncounterUtil1.GetRandomDVs(Util.Rand),
CatchRate = GetInitialCatchRate(pi),
DV16 = IVs.IsSpecified ? EncounterUtil.GetDV16(IVs) : EncounterUtil.GetRandomDVs(Util.Rand),
OT_Name = EncounterUtil1.GetTrainerName(tr, lang),
OT_Name = EncounterUtil.GetTrainerName(tr, lang),
TID16 = tr.TID16,
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
Type1 = pi.Type1,
@ -77,15 +77,15 @@ public PK1 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
{
// Amnesia Psyduck has different catch rates depending on language
if (Species == (int)Core.Species.Psyduck)
pk.Catch_Rate = pk.Japanese ? (byte)167 : (byte)168;
pk.CatchRate = pk.Japanese ? (byte)167 : (byte)168;
else
pk.Catch_Rate = Util.Rand.Next(2) == 0 ? (byte)167 : (byte)168;
pk.CatchRate = Util.Rand.Next(2) == 0 ? (byte)167 : (byte)168;
}
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
@ -106,7 +106,7 @@ private int GetTemplateLanguage(ITrainerInfo tr)
#endregion
private byte GetInitialCatchRate()
private byte GetInitialCatchRate(PersonalInfo1 pi)
{
if (Version == GameVersion.Stadium)
{
@ -116,7 +116,7 @@ private byte GetInitialCatchRate()
}
// Encounters can have different Catch Rates (RBG vs Y)
return EncounterUtil1.GetWildCatchRate(Version, Species);
return pi.CatchRate;
}
#region Matching
@ -205,24 +205,24 @@ private bool IsMatchPartial(PKM pk)
{
if (pk is not PK1 pk1)
return false;
return !IsCatchRateValid(pk1.Catch_Rate);
return !IsCatchRateValid(pk1.CatchRate);
}
private bool IsCatchRateValid(byte catch_rate)
private bool IsCatchRateValid(byte rate)
{
if (ParseSettings.AllowGen1Tradeback && PK1.IsCatchRateHeldItem(catch_rate))
if (ParseSettings.AllowGen1Tradeback && PK1.IsCatchRateHeldItem(rate))
return true;
if (Version == GameVersion.Stadium)
{
// Amnesia Psyduck has different catch rates depending on language
if (Species == (int)Core.Species.Psyduck)
return catch_rate == (Language == EncounterGBLanguage.Japanese ? 167 : 168);
return catch_rate is 167 or 168;
return rate == (Language == EncounterGBLanguage.Japanese ? 167 : 168);
return rate is 167 or 168;
}
// Encounters can have different Catch Rates (RBG vs Y)
return GBRestrictions.RateMatchesEncounter(Species, Version, catch_rate);
return GBRestrictions.RateMatchesEncounter(Species, Version, rate);
}
#endregion

View File

@ -33,22 +33,22 @@ public PK1 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
{
int lang = (int)Language.GetSafeLanguage(Generation, (LanguageID)tr.Language, Version);
var isJapanese = lang == (int)LanguageID.Japanese;
var pi = EncounterUtil1.GetPersonal1(Version, Species);
var pi = EncounterUtil.GetPersonal1(Version, Species);
var pk = new PK1(isJapanese)
{
Species = Species,
CurrentLevel = LevelMin,
Catch_Rate = EncounterUtil1.GetWildCatchRate(Version, Species),
DV16 = EncounterUtil1.GetRandomDVs(Util.Rand),
CatchRate = pi.CatchRate,
DV16 = EncounterUtil.GetRandomDVs(Util.Rand),
OT_Name = EncounterUtil1.GetTrainerName(tr, lang),
OT_Name = EncounterUtil.GetTrainerName(tr, lang),
TID16 = tr.TID16,
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
Type1 = pi.Type1,
Type2 = pi.Type2,
};
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
@ -65,8 +65,8 @@ public bool IsMatchExact(PKM pk, EvoCriteria evo)
if (pk is not PK1 pk1)
return true;
var rate = pk1.Catch_Rate;
var expect = EncounterUtil1.GetWildCatchRate(Version, Species);
var rate = pk1.CatchRate;
var expect = EncounterUtil.GetPersonal1(Version, Species).CatchRate;
if (expect != rate && !(ParseSettings.AllowGen1Tradeback && GBRestrictions.IsTradebackCatchRate(rate)))
return false;
return true;

View File

@ -16,7 +16,7 @@ public sealed record EncounterStatic1(ushort Species, byte Level, GameVersion Ve
public bool IsShiny => false;
public int Location => 0;
private const int LightBallPikachuCatchRate = 0xA3; // 163
private const byte LightBallPikachuCatchRate = 0xA3; // 163 - Light Ball
public byte Form => 0;
public string Name => "Static Encounter";
@ -26,15 +26,6 @@ public sealed record EncounterStatic1(ushort Species, byte Level, GameVersion Ve
public bool IsStarterPikachu => Version == GameVersion.YW && Species == (int)Core.Species.Pikachu && Level == 5;
private byte GetInitialCatchRate()
{
if (IsStarterPikachu)
return LightBallPikachuCatchRate; // Light Ball
// Encounters can have different Catch Rates (RBG vs Y)
return EncounterUtil1.GetWildCatchRate(Version, Species);
}
#region Generating
PKM IEncounterConvertible.ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria) => ConvertToPKM(tr, criteria);
PKM IEncounterConvertible.ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr);
@ -45,22 +36,22 @@ public PK1 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
{
int lang = (int)Language.GetSafeLanguage(Generation, (LanguageID)tr.Language, Version);
var isJapanese = lang == (int)LanguageID.Japanese;
var pi = EncounterUtil1.GetPersonal1(Version, Species);
var pi = EncounterUtil.GetPersonal1(Version, Species);
var pk = new PK1(isJapanese)
{
Species = Species,
CurrentLevel = LevelMin,
Catch_Rate = GetInitialCatchRate(),
DV16 = EncounterUtil1.GetRandomDVs(Util.Rand),
CatchRate = IsStarterPikachu ? LightBallPikachuCatchRate : pi.CatchRate,
DV16 = EncounterUtil.GetRandomDVs(Util.Rand),
OT_Name = EncounterUtil1.GetTrainerName(tr, lang),
OT_Name = EncounterUtil.GetTrainerName(tr, lang),
TID16 = tr.TID16,
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
Type1 = pi.Type1,
Type2 = pi.Type2,
};
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
@ -113,20 +104,20 @@ private bool IsMatchPartial(PKM pk)
{
if (pk is not PK1 pk1)
return false;
return !IsCatchRateValid(pk1.Catch_Rate);
return !IsCatchRateValid(pk1.CatchRate);
}
private bool IsCatchRateValid(byte catch_rate)
private bool IsCatchRateValid(byte rate)
{
if (ParseSettings.AllowGen1Tradeback && PK1.IsCatchRateHeldItem(catch_rate))
if (ParseSettings.AllowGen1Tradeback && PK1.IsCatchRateHeldItem(rate))
return true;
// Light Ball (Yellow) starter
if (IsStarterPikachu)
return catch_rate == LightBallPikachuCatchRate;
return rate == LightBallPikachuCatchRate;
// Encounters can have different Catch Rates (RBG vs Y)
return GBRestrictions.RateMatchesEncounter(Species, Version, catch_rate);
return GBRestrictions.RateMatchesEncounter(Species, Version, rate);
}
#endregion

View File

@ -113,13 +113,13 @@ public PK1 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
var level = gsc ? LevelMinGSC : LevelMinRBY;
int lang = (int)Language.GetSafeLanguage(Generation, (LanguageID)tr.Language, Version);
var isJapanese = lang == (int)LanguageID.Japanese;
var pi = EncounterUtil1.GetPersonal1(Version, Species);
var pi = EncounterUtil.GetPersonal1(Version, Species);
var pk = new PK1(isJapanese)
{
Species = Species,
CurrentLevel = level,
Catch_Rate = EncounterUtil1.GetWildCatchRate(Version, Species),
DV16 = EncounterUtil1.GetRandomDVs(Util.Rand),
CatchRate = pi.CatchRate,
DV16 = EncounterUtil.GetRandomDVs(Util.Rand),
Nickname = Nicknames[lang],
TID16 = tr.TID16,
@ -128,7 +128,7 @@ public PK1 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
};
pk.OT_Trash[0] = StringConverter12.G1TradeOTCode;
EncounterUtil1.SetEncounterMoves(pk, Version, level);
EncounterUtil.SetEncounterMoves(pk, Version, level);
if (EvolveOnTrade)
pk.Species++;
@ -188,8 +188,8 @@ private bool IsMatchPartial(PKM pk)
if (pk is not PK1 pk1)
return false;
var req = EncounterUtil1.GetWildCatchRate(Version, Species);
return req != pk1.Catch_Rate;
var req = EncounterUtil.GetPersonal1(Version, Species).CatchRate;
return req != pk1.CatchRate;
}
#endregion

View File

@ -1,49 +0,0 @@
using System;
namespace PKHeX.Core;
public static class EncounterUtil1
{
public const int FormDynamic = FormVivillon;
public const byte FormVivillon = 30;
public const byte FormRandom = 31;
public static ushort GetRandomDVs(Random rand) => (ushort)rand.Next(ushort.MaxValue + 1);
public static byte GetWildCatchRate(GameVersion version, ushort species) => (byte)(version == GameVersion.YW ? PersonalTable.Y : PersonalTable.RB)[species].CatchRate;
public static (byte Type1, byte Type2) GetTypes(GameVersion version, ushort species)
{
var pi = GetPersonal1(version, species);
return (pi.Type1, pi.Type2);
}
public static PersonalInfo1 GetPersonal1(GameVersion version, ushort species)
{
var pt = version == GameVersion.YW ? PersonalTable.Y : PersonalTable.RB;
return pt[species];
}
public static void SetEncounterMoves<T>(T pk, GameVersion version, byte level) where T : PKM
{
Span<ushort> moves = stackalloc ushort[4];
var source = GameData.GetLearnSource(version);
source.SetEncounterMoves(pk.Species, pk.Form, level, moves);
pk.SetMoves(moves);
}
public static string GetTrainerName(ITrainerInfo tr, int lang) => lang switch
{
(int)LanguageID.Japanese => tr.Language == 1 ? tr.OT : "ゲーフリ",
_ => tr.Language == 1 ? "GF" : tr.OT,
};
public static ushort GetDV16(in IndividualValueSet actual)
{
ushort result = 0;
result |= (ushort)(actual.SPA << 0);
result |= (ushort)(actual.SPE << 4);
result |= (ushort)(actual.DEF << 8);
result |= (ushort)(actual.ATK << 12);
return result;
}
}

View File

@ -67,7 +67,7 @@ private EncounterSlot2[] ReadSlots(ReadOnlySpan<byte> data, int count)
byte min = entry[2];
byte slotNum = entry[1];
byte species = entry[0];
var form = species == (int)Species.Unown ? EncounterUtil1.FormRandom : (byte)0;
var form = species == (int)Species.Unown ? EncounterUtil.FormRandom : (byte)0;
slots[i] = new EncounterSlot2(this, species, form, min, max, slotNum);
}
return slots;

View File

@ -89,7 +89,7 @@ public PK2 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
if (IVs.IsSpecified)
criteria.SetRandomIVs(pk, IVs);

View File

@ -19,7 +19,7 @@ public sealed record EncounterSlot2(EncounterArea2 Parent, ushort Species, byte
public Shiny Shiny => Shiny.Random;
public bool IsShiny => false;
public int EggLocation => 0;
public bool IsRandomUnspecificForm => Form >= EncounterUtil1.FormDynamic;
public bool IsRandomUnspecificForm => Form >= EncounterUtil.FormDynamic;
public string Name => $"Wild Encounter ({Version})";
public string LongName => $"{Name} {Type.ToString().Replace('_', ' ')}";
@ -91,7 +91,7 @@ public PK2 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
// Form is only Unown and is derived from IVs.
CurrentLevel = LevelMin,
OT_Friendship = pi.BaseFriendship,
DV16 = EncounterUtil1.GetRandomDVs(Util.Rand),
DV16 = EncounterUtil.GetRandomDVs(Util.Rand),
Language = lang,
OT_Name = tr.OT,
@ -107,7 +107,7 @@ public PK2 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.Met_TimeOfDay = GetRandomTime();
}
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
if (IsHeadbutt)
{
var id = pk.TID16;

View File

@ -71,7 +71,7 @@ public PK2 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
if (IVs.IsSpecified)
criteria.SetRandomIVs(pk, IVs);

View File

@ -73,17 +73,17 @@ public PK2 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (IVs.IsSpecified)
{
pk.DV16 = EncounterUtil1.GetDV16(IVs);
pk.DV16 = EncounterUtil.GetDV16(IVs);
pk.OT_Gender = OTGender;
pk.TID16 = TID16;
}
else
{
pk.DV16 = EncounterUtil1.GetRandomDVs(Util.Rand);
pk.DV16 = EncounterUtil.GetRandomDVs(Util.Rand);
pk.TID16 = tr.TID16;
}
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
pk.ResetPartyStats();

View File

@ -75,7 +75,7 @@ public CK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -61,7 +61,7 @@ public CK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
Ball = (byte)Ball.Poke,
Language = lang,
OT_Name = tr.Language == lang ? tr.OT : lang == 1 ? "ゲーフリ" : "GF",
OT_Name = EncounterUtil.GetTrainerName(tr, lang),
OT_Gender = 0,
ID32 = tr.ID32,
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),

View File

@ -61,7 +61,7 @@ public CK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -7,6 +7,7 @@ public record EncounterSlot3(EncounterArea3 Parent, ushort Species, byte Form, b
: IEncounterable, IEncounterMatch, IEncounterConvertible<PK3>, IMagnetStatic, INumberedSlot, ISlotRNGType, IRandomCorrelation
{
public int Generation => 3;
int ILocation.Location => Location;
public EntityContext Context => EntityContext.Gen3;
public bool EggEncounter => false;
public Ball FixedBall => GetRequiredBall();
@ -19,7 +20,7 @@ public record EncounterSlot3(EncounterArea3 Parent, ushort Species, byte Form, b
public string Name => $"Wild Encounter ({Version})";
public string LongName => $"{Name} {Type.ToString().Replace('_', ' ')}";
public GameVersion Version => Parent.Version;
public int Location => Parent.Location;
public byte Location => Parent.Location;
public SlotType Type => Parent.Type;
private Ball GetRequiredBall(Ball fallback = Ball.None) => Locations.IsSafariZoneLocation3(Location) ? Ball.Safari : fallback;
@ -78,7 +79,7 @@ private void SetPINGA(PK3 pk, EncounterCriteria criteria, PersonalInfo3 pi)
}
}
protected virtual void SetEncounterMoves(PKM pk) => EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
protected virtual void SetEncounterMoves(PKM pk) => EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
#endregion
#region Matching

View File

@ -53,7 +53,7 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
FatefulEncounter = FatefulEncounter,
Language = lang,
OT_Name = tr.Language == lang ? tr.OT : lang == 1 ? "ゲーフリ" : "GF",
OT_Name = EncounterUtil.GetTrainerName(tr, lang),
OT_Gender = tr.Gender,
ID32 = tr.ID32,
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
@ -72,7 +72,7 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;

View File

@ -110,7 +110,7 @@ public PK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.Nickname = Nicknames[lang];
pk.OT_Name = TrainerNames[lang];
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
SetPINGA(pk, criteria);
pk.ResetPartyStats();

View File

@ -71,7 +71,7 @@ public XK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -52,7 +52,7 @@ public XK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
};
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, GameVersion.XD, LevelMin);
EncounterUtil.SetEncounterMoves(pk, GameVersion.XD, LevelMin);
pk.ResetPartyStats();
return pk;

View File

@ -63,7 +63,7 @@ public XK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -83,7 +83,7 @@ public XK3 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -7,6 +7,7 @@ public sealed record EncounterSlot4(EncounterArea4 Parent, ushort Species, byte
: IEncounterable, IEncounterMatch, IEncounterConvertible<PK4>, IMagnetStatic, INumberedSlot, IGroundTypeTile, ISlotRNGType, IEncounterFormRandom, IRandomCorrelation
{
public int Generation => 4;
int ILocation.Location => Location;
public EntityContext Context => EntityContext.Gen4;
public bool EggEncounter => false;
public AbilityPermission Ability => AbilityPermission.Any12;
@ -14,22 +15,22 @@ public sealed record EncounterSlot4(EncounterArea4 Parent, ushort Species, byte
public Shiny Shiny => Shiny.Random;
public bool IsShiny => false;
public int EggLocation => 0;
public bool IsRandomUnspecificForm => Form >= EncounterUtil1.FormDynamic;
public bool IsRandomUnspecificForm => Form >= EncounterUtil.FormDynamic;
public string Name => $"Wild Encounter ({Version})";
public string LongName => $"{Name} {Type.ToString().Replace('_', ' ')}";
public GameVersion Version => Parent.Version;
public int Location => Parent.Location;
public ushort Location => Parent.Location;
public SlotType Type => Parent.Type;
public GroundTileAllowed GroundTile => Parent.GroundTile;
public bool CanUseRadar => Version is not (GameVersion.HG or GameVersion.SS) && GroundTile.HasFlag(GroundTileAllowed.Grass) && !Locations.IsSafariZoneLocation4(Location);
public bool CanUseRadar => Version is not (GameVersion.HG or GameVersion.SS) && GroundTile.HasFlag(GroundTileAllowed.Grass) && !Locations4.IsSafariZoneLocation(Location);
private Ball GetRequiredBallValue(Ball fallback = Ball.None)
{
if (Type is SlotType.BugContest)
return Ball.Sport;
return Locations.IsSafariZoneLocation4(Location) ? Ball.Safari : fallback;
return Locations4.IsSafariZoneLocation(Location) ? Ball.Safari : fallback;
}
#region Generating
@ -63,7 +64,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
};
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
@ -71,7 +72,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
private byte GetWildForm(byte form)
{
if (form == EncounterUtil1.FormRandom) // flagged as totally random
if (form == EncounterUtil.FormRandom) // flagged as totally random
return (byte)Util.Rand.Next(PersonalTable.HGSS[Species].FormCount);
return form;
}
@ -137,7 +138,7 @@ public bool IsInvalidMunchlaxTree(PKM pk)
public EncounterMatchRating GetMatchRating(PKM pk)
{
if ((pk.Ball == (int)Ball.Safari) != Locations.IsSafariZoneLocation4(Location))
if ((pk.Ball == (int)Ball.Safari) != Locations4.IsSafariZoneLocation(Location))
return EncounterMatchRating.PartialMatch;
if ((pk.Ball == (int)Ball.Sport) != (Type == SlotType.BugContest))
{

View File

@ -93,7 +93,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
pk.ResetPartyStats();
return pk;

View File

@ -94,7 +94,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
pk.ResetPartyStats();
return pk;

View File

@ -122,7 +122,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
criteria.SetRandomIVs(pk, IVs);
pk.PID = PID;
pk.Gender = Gender;

View File

@ -114,7 +114,7 @@ public PK4 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, actualLevel);
EncounterUtil.SetEncounterMoves(pk, version, actualLevel);
SetPINGA(pk, criteria);
pk.ResetPartyStats();

View File

@ -67,14 +67,14 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
};
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
}
private byte GetWildForm(byte form)
{
if (form != EncounterUtil1.FormRandom)
if (form != EncounterUtil.FormRandom)
return form;
// flagged as totally random
return (byte)Util.Rand.Next(PersonalTable.B2W2[Species].FormCount);

View File

@ -73,7 +73,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.EggMetDate = pk.MetDate;
}
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
SetPINGA(pk, criteria, pi);
pk.ResetPartyStats();

View File

@ -59,7 +59,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
SetPINGA(pk, criteria, pi);
pk.ResetPartyStats();

View File

@ -84,7 +84,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
HiddenAbility = Ability == AbilityPermission.OnlyHidden,
};
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
pk.ResetPartyStats();

View File

@ -52,7 +52,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
};
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
SetPINGA(pk, criteria, pi);
pk.ResetPartyStats();

View File

@ -94,7 +94,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
Nickname = IsFixedNickname ? Nicknames[lang] : SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
};
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
SetPINGA(pk, criteria, pi);
pk.ResetPartyStats();

View File

@ -94,7 +94,7 @@ public PK5 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
Nickname = IsFixedNickname ? Nicknames[lang] : SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
};
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
criteria.SetRandomIVs(pk, IVs);
pk.RefreshAbility(criteria.GetAbilityFromNumber(Ability));
pk.ResetPartyStats();

View File

@ -62,7 +62,7 @@ private EncounterSlot6XY[] LoadSafariSlots()
slots[i++] = new EncounterSlot6XY(this, (int)Species.Floette, 3, Level, Level);
// Region Random Vivillon
slots[i] = new EncounterSlot6XY(this, (int)Species.Vivillon, EncounterUtil1.FormVivillon, Level, Level);
slots[i] = new EncounterSlot6XY(this, (int)Species.Vivillon, EncounterUtil.FormVivillon, Level, Level);
return slots;
}

View File

@ -15,7 +15,7 @@ public sealed record EncounterSlot6AO(EncounterArea6AO Parent, ushort Species, b
public Shiny Shiny => Shiny.Random;
public bool IsShiny => false;
public int EggLocation => 0;
public bool IsRandomUnspecificForm => Form >= EncounterUtil1.FormDynamic;
public bool IsRandomUnspecificForm => Form >= EncounterUtil.FormDynamic;
public string Name => $"Wild Encounter ({Version})";
public string LongName => $"{Name} {Type.ToString().Replace('_', ' ')}";
@ -82,7 +82,7 @@ public PK6 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.SetDefaultRegionOrigins(lang);
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
if (CanDexNav)
{
var eggMoves = GetDexNavMoves();
@ -96,7 +96,7 @@ public PK6 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
private byte GetWildForm(byte form)
{
if (form != EncounterUtil1.FormRandom)
if (form != EncounterUtil.FormRandom)
return form;
// flagged as totally random
return (byte)Util.Rand.Next(PersonalTable.AO[Species].FormCount);

View File

@ -13,7 +13,7 @@ public sealed record EncounterSlot6XY(EncounterArea6XY Parent, ushort Species, b
public Shiny Shiny => Shiny.Random;
public bool IsShiny => false;
public int EggLocation => 0;
public bool IsRandomUnspecificForm => Form >= EncounterUtil1.FormDynamic;
public bool IsRandomUnspecificForm => Form >= EncounterUtil.FormDynamic;
private PersonalInfo6XY PersonalInfo => PersonalTable.XY[Species];
public byte FlawlessIVCount => PersonalInfo.EggGroup1 == 15 ? (byte)3 : IsFriendSafari ? (byte)2 : (byte)0;
@ -76,11 +76,11 @@ public PK6 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
else
pk.SetDefaultRegionOrigins(lang);
if (IsRandomUnspecificForm && Form == EncounterUtil1.FormVivillon)
if (IsRandomUnspecificForm && Form == EncounterUtil.FormVivillon)
pk.Form = Vivillon3DS.GetPattern(pk.Country, pk.Region);
SetPINGA(pk, criteria);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.SetRandomMemory6();
pk.ResetPartyStats();
return pk;
@ -88,9 +88,9 @@ public PK6 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
private byte GetWildForm(byte form)
{
if (form < EncounterUtil1.FormDynamic)
if (form < EncounterUtil.FormDynamic)
return form;
if (form == EncounterUtil1.FormVivillon)
if (form == EncounterUtil.FormVivillon)
return 0; // rectify later
// flagged as totally random

View File

@ -92,7 +92,7 @@ public PK6 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
this.CopyContestStatsTo(pk);
pk.SetRandomMemory6();

View File

@ -104,7 +104,7 @@ public PK6 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
else
pk.SetDefaultRegionOrigins(lang);
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
if (pk.IsShiny)
pk.PID ^= 0x1000_0000;
criteria.SetRandomIVs(pk, IVs);

View File

@ -13,7 +13,7 @@ public sealed record EncounterSlot7(EncounterArea7 Parent, ushort Species, byte
public Shiny Shiny => Shiny.Random;
public bool IsShiny => false;
public int EggLocation => 0;
public bool IsRandomUnspecificForm => Form >= EncounterUtil1.FormDynamic;
public bool IsRandomUnspecificForm => Form >= EncounterUtil.FormDynamic;
public string Name => $"Wild Encounter ({Version})";
public string LongName => $"{Name} {Type.ToString().Replace('_', ' ')}";
@ -72,14 +72,14 @@ public PK7 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.SetDefaultRegionOrigins(lang);
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
}
private byte GetWildForm(byte form)
{
if (form != EncounterUtil1.FormRandom)
if (form != EncounterUtil.FormRandom)
return form; // flagged as totally random
if (Species == (int)Core.Species.Minior)
return (byte)Util.Rand.Next(7, 14);

View File

@ -97,7 +97,7 @@ public PK7 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Relearn.HasMoves)
pk.SetRelearnMoves(Relearn);
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
SetPINGA(pk, criteria, pi);
pk.ResetPartyStats();

View File

@ -103,7 +103,7 @@ public PK7 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
else
pk.SetDefaultRegionOrigins(lang);
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
if (pk.IsShiny)
pk.PID ^= 0x1000_0000;
criteria.SetRandomIVs(pk, IVs);

View File

@ -54,7 +54,7 @@ public PB7 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.ResetHeight();
pk.ResetWeight();
pk.ResetCP();
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
}

View File

@ -62,7 +62,7 @@ public PB7 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.ResetHeight();
pk.ResetWeight();
pk.ResetCP();
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
}

View File

@ -75,7 +75,7 @@ public PB7 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
HT_Friendship = pi.BaseFriendship,
};
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
pk.ResetHeight();
pk.ResetWeight();
pk.ResetCP();

View File

@ -69,7 +69,7 @@ public PK8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
OT_Friendship = pi.BaseFriendship,
};
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
bool symbol = Parent.PermitCrossover;
if (!symbol && Location is 30 or 54 && (Weather & AreaWeather8.Fishing) == 0)
@ -83,7 +83,7 @@ public PK8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
private byte GetWildForm(byte form)
{
if (form == EncounterUtil1.FormRandom) // flagged as totally random
if (form == EncounterUtil.FormRandom) // flagged as totally random
return (byte)Util.Rand.Next(PersonalTable.SWSH[Species].FormCount);
return form;
}

View File

@ -105,7 +105,7 @@ public PK8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -81,7 +81,7 @@ public PK8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
pk.ResetPartyStats();
return pk;
@ -199,6 +199,8 @@ private EncounterMatchRating IsMatchDeferred(PKM pk)
{
if (Ability is OnlyFirst or OnlySecond && !AbilityVerifier.CanAbilityCapsule(8, PersonalTable.SWSH.GetFormEntry(Species, Form)))
return EncounterMatchRating.DeferredErrors;
if (Ability is OnlyHidden) // Can't revert to hidden ability even if transferred from HOME and another game with HA reversion.
return EncounterMatchRating.DeferredErrors;
}
}

View File

@ -136,7 +136,7 @@ public PK8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.PID ^= 0x1000_0000u;
pk.SetRelearnMoves(Relearn);
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
SetPINGA(pk, criteria, pi);
pk.ResetPartyStats();

View File

@ -85,7 +85,7 @@ public PB8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
OT_Friendship = pi.BaseFriendship,
};
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
if (IsUnderground && GetBaseEggMove(out var move1, pi))
pk.RelearnMove1 = move1;
pk.ResetPartyStats();

View File

@ -97,7 +97,7 @@ public PB8 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
SetPINGA(pk, criteria);
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -113,7 +113,7 @@ public PK9 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -111,7 +111,7 @@ public PK9 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
pk.Scale = !IsForcedScaleRange ? PokeSizeUtil.GetRandomScalar() : (byte)Util.Rand.Next(ScaleMin, ScaleMax + 1);
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, Version, Level);
EncounterUtil.SetEncounterMoves(pk, Version, Level);
pk.ResetPartyStats();
return pk;

View File

@ -16,7 +16,7 @@ public sealed record EncounterSlot9(EncounterArea9 Parent, ushort Species, byte
public Shiny Shiny => Shiny.Random;
public bool IsShiny => false;
public int EggLocation => 0;
public bool IsRandomUnspecificForm => Form >= EncounterUtil1.FormDynamic;
public bool IsRandomUnspecificForm => Form >= EncounterUtil.FormDynamic;
public string Name => $"Wild Encounter ({Version})";
public string LongName => $"{Name}";
@ -151,16 +151,16 @@ public PK9 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation),
};
SetPINGA(pk, criteria, pi);
EncounterUtil1.SetEncounterMoves(pk, Version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, Version, LevelMin);
pk.ResetPartyStats();
return pk;
}
private byte GetWildForm(byte form)
{
if (form < EncounterUtil1.FormDynamic)
if (form < EncounterUtil.FormDynamic)
return form;
if (form == EncounterUtil1.FormVivillon)
if (form == EncounterUtil.FormVivillon)
return Vivillon3DS.FancyFormID; // Fancy Vivillon
// flagged as totally random
return (byte)Util.Rand.Next(PersonalTable.SV[Species].FormCount);

View File

@ -118,7 +118,7 @@ public PK9 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
if (Moves.HasMoves)
pk.SetMoves(Moves);
else
EncounterUtil1.SetEncounterMoves(pk, version, LevelMin);
EncounterUtil.SetEncounterMoves(pk, version, LevelMin);
pk.ResetPartyStats();
return pk;

View File

@ -39,7 +39,7 @@ public sealed record EncounterTera9
public bool IsAvailableHostViolet => RandRateMinViolet != -1;
public required TeraRaidMapParent Map { get; init; }
public bool IsRandomUnspecificForm => Form >= EncounterUtil1.FormDynamic;
public bool IsRandomUnspecificForm => Form >= EncounterUtil.FormDynamic;
public string Name => $"Tera Raid Encounter [{(Index == 0 ? "Base" : Index)}] {Stars}★";
public string LongName => Name;

View File

@ -107,7 +107,7 @@ public PK9 ConvertToPKM(ITrainerInfo tr, EncounterCriteria criteria)
Obedience_Level = Level,
};
EncounterUtil1.SetEncounterMoves(pk, version, Level);
EncounterUtil.SetEncounterMoves(pk, version, Level);
SetPINGA(pk, criteria, pi);
if (EvolveOnTrade)
pk.Species++;

View File

@ -148,7 +148,7 @@ private static bool HasDefinitelyVisitedGen2(PK1 pk1)
{
if (!ParseSettings.AllowGen1Tradeback)
return false;
var rate = pk1.Catch_Rate;
var rate = pk1.CatchRate;
return rate is 0 || GBRestrictions.IsTradebackCatchRate(rate);
}

View File

@ -53,7 +53,7 @@ public sealed record Frame(uint Seed, FrameType FrameType, LeadRequired Lead)
}
// Check Level Now
int lvl = SlotRange.GetLevel(slot, Lead, RandLevel);
var lvl = SlotRange.GetLevel(slot, Lead, RandLevel);
if (pk.HasOriginalMetLocation)
{
if (lvl != pk.Met_Level)

View File

@ -72,6 +72,14 @@ private static int JSlot(SlotType type, uint rand)
};
}
/// <summary>
/// Calculates the level for the given slot.
/// </summary>
/// <typeparam name="T">Slot type</typeparam>
/// <param name="slot">Slot reference</param>
/// <param name="lead">Player's lead Pokémon</param>
/// <param name="lvlrand">Random value to use for level calculation</param>
/// <returns>Actual level of the encounter</returns>
public static int GetLevel<T>(T slot, LeadRequired lead, uint lvlrand) where T : ILevelRange
{
if ((lead & LeadRequired.PressureHustleSpiritFail) == LeadRequired.PressureHustleSpirit)

View File

@ -93,25 +93,25 @@ public static bool RateMatchesEncounter(ushort species, GameVersion version, byt
return rate == PersonalTable.RB[species].CatchRate;
}
private static bool RateMatchesEither(byte catch_rate, ushort rate)
private static bool RateMatchesEither(byte rate, ushort species)
{
return catch_rate == PersonalTable.RB[rate].CatchRate || catch_rate == PersonalTable.Y[rate].CatchRate;
return rate == PersonalTable.RB[species].CatchRate || rate == PersonalTable.Y[species].CatchRate;
}
private static bool GetCatchRateMatchesPreEvolution(PK1 pk, byte catch_rate)
private static bool GetCatchRateMatchesPreEvolution(PK1 pk, byte rate)
{
// For species catch rate, discard any species that has no valid encounters and a different catch rate than their pre-evolutions
var head = new EvoCriteria { Species = pk.Species, Form = pk.Form, LevelMax = (byte)pk.CurrentLevel }; // as struct to avoid boxing
do
{
var s = head.Species;
if (!IsSpeciesNotAvailableCatchRate((byte)s) && RateMatchesEither(catch_rate, s))
var species = head.Species;
if (!IsSpeciesNotAvailableCatchRate((byte)species) && RateMatchesEither(rate, species))
return true;
}
while (EvolutionGroup1.Instance.TryDevolve(head, pk, head.LevelMax, 2, false, out head));
// Account for oddities via special catch rate encounters
if (catch_rate is 167 or 168 && IsStadiumGiftSpecies((byte)head.Species))
if (rate is 167 or 168 && IsStadiumGiftSpecies((byte)head.Species))
return true;
return false;
}
@ -160,7 +160,7 @@ internal static PotentialGBOrigin GetTradebackStatusInitial(PKM pk)
}
/// <summary>
/// Gets the Tradeback status depending on the <see cref="PK1.Catch_Rate"/>
/// Gets the Tradeback status depending on the <see cref="PK1.CatchRate"/>
/// </summary>
/// <param name="pk">Pokémon to guess the Tradeback status from.</param>
private static PotentialGBOrigin GetTradebackStatusRBY(PK1 pk)
@ -169,7 +169,7 @@ private static PotentialGBOrigin GetTradebackStatusRBY(PK1 pk)
return Gen1Only;
// Detect Tradeback status by comparing the catch rate(Gen1)/held item(Gen2) to the species in the Pokémon's evolution chain.
var catch_rate = pk.Catch_Rate;
var catch_rate = pk.CatchRate;
if (catch_rate == 0)
return Either;
@ -189,7 +189,7 @@ public static TimeCapsuleEvaluation IsTimeCapsuleTransferred(PKM pk, ReadOnlySpa
{
if (z.Generation == enc.Generation || z.Generation is not (1 or 2))
continue;
if (pk is PK1 {Catch_Rate: not 0} g1 && !IsTradebackCatchRate(g1.Catch_Rate))
if (pk is PK1 {CatchRate: not 0} g1 && !IsTradebackCatchRate(g1.CatchRate))
return BadCatchRate;
return enc.Generation == 2 ? Transferred21 : Transferred12;
}
@ -217,7 +217,7 @@ public static TimeCapsuleEvaluation IsTimeCapsuleTransferred(PKM pk, ReadOnlySpa
if (gb is PK1 pk1)
{
var rate = pk1.Catch_Rate;
var rate = pk1.CatchRate;
if (rate == 0)
return Transferred12;
@ -234,7 +234,7 @@ public static TimeCapsuleEvaluation IsTimeCapsuleTransferred(PKM pk, ReadOnlySpa
EncounterGift1 g when g.GetMatchRating(pk1) < EncounterMatchRating.PartialMatch => true,
EncounterStatic1 s when s.GetMatchRating(pk1) < EncounterMatchRating.PartialMatch => true,
EncounterTrade1 => true,
_ => RateMatchesEncounter(enc.Species, enc.Version, pk1.Catch_Rate),
_ => RateMatchesEncounter(enc.Species, enc.Version, pk1.CatchRate),
};
public static bool IsTradebackCatchRate(byte rate) => Array.IndexOf(HeldItems_GSC, rate) != -1;

View File

@ -370,48 +370,45 @@ private void VerifyMiscG1Types(LegalityAnalysis data, PK1 pk1)
private void VerifyMiscG1CatchRate(LegalityAnalysis data, PK1 pk1)
{
var catch_rate = pk1.Catch_Rate;
var tradeback = GBRestrictions.IsTimeCapsuleTransferred(pk1, data.Info.Moves, data.EncounterMatch);
var result = tradeback is TimeCapsuleEvaluation.NotTransferred or TimeCapsuleEvaluation.BadCatchRate
? GetWasNotTradeback(tradeback)
: GetWasTradeback(tradeback);
? GetWasNotTradeback(data, pk1, tradeback)
: GetWasTradeback(data, pk1, tradeback);
data.AddLine(result);
}
CheckResult GetWasTradeback(TimeCapsuleEvaluation timeCapsuleEvalution)
private CheckResult GetWasTradeback(LegalityAnalysis data, PK1 pk1, TimeCapsuleEvaluation eval)
{
var rate = pk1.CatchRate;
if (PK1.IsCatchRateHeldItem(rate))
return GetValid(LG1CatchRateMatchTradeback);
return GetWasNotTradeback(data, pk1, eval);
}
private CheckResult GetWasNotTradeback(LegalityAnalysis data, PK1 pk1, TimeCapsuleEvaluation eval)
{
var rate = pk1.CatchRate;
if (MoveInfo.IsAnyFromGeneration(2, data.Info.Moves))
return GetInvalid(LG1CatchRateItem);
var e = data.EncounterMatch;
if (e is EncounterGift1 { Version: GameVersion.Stadium } or EncounterTrade1)
return GetValid(LG1CatchRateMatchPrevious); // Encounters detected by the catch rate, cant be invalid if match this encounters
ushort species = pk1.Species;
if (GBRestrictions.IsSpeciesNotAvailableCatchRate((byte)species) && rate == PersonalTable.RB[species].CatchRate)
{
if (PK1.IsCatchRateHeldItem(catch_rate))
return GetValid(LG1CatchRateMatchTradeback);
if (timeCapsuleEvalution == TimeCapsuleEvaluation.BadCatchRate)
return GetInvalid(LG1CatchRateItem);
return GetWasNotTradeback(timeCapsuleEvalution);
}
CheckResult GetWasNotTradeback(TimeCapsuleEvaluation timeCapsuleEvalution)
{
if (MoveInfo.IsAnyFromGeneration(2, data.Info.Moves))
return GetInvalid(LG1CatchRateItem);
var e = data.EncounterMatch;
if (e is EncounterGift1 {Version: GameVersion.Stadium} or EncounterTrade1)
return GetValid(LG1CatchRateMatchPrevious); // Encounters detected by the catch rate, cant be invalid if match this encounters
ushort species = pk1.Species;
if (GBRestrictions.IsSpeciesNotAvailableCatchRate((byte)species) && catch_rate == PersonalTable.RB[species].CatchRate)
{
if (species != (int) Species.Dragonite || catch_rate != 45 || !(e.Version == GameVersion.BU || e.Version.Contains(GameVersion.YW)))
return GetInvalid(LG1CatchRateEvo);
}
if (!GBRestrictions.RateMatchesEncounter(e.Species, e.Version, catch_rate))
return GetInvalid(timeCapsuleEvalution == TimeCapsuleEvaluation.Transferred12 ? LG1CatchRateChain : LG1CatchRateNone);
return GetValid(LG1CatchRateMatchPrevious);
if (species != (int)Species.Dragonite || rate != 45 || !(e.Version == GameVersion.BU || e.Version.Contains(GameVersion.YW)))
return GetInvalid(LG1CatchRateEvo);
}
if (!GBRestrictions.RateMatchesEncounter(e.Species, e.Version, rate))
return GetInvalid(eval == TimeCapsuleEvaluation.Transferred12 ? LG1CatchRateChain : LG1CatchRateNone);
return GetValid(LG1CatchRateMatchPrevious);
}
private static void VerifyMiscFatefulEncounter(LegalityAnalysis data)
{
var pk = data.Entity;
var enc = data.EncounterMatch;
switch (enc)
switch (data.EncounterMatch)
{
case WC3 {FatefulEncounter: true} w:
if (w.IsEgg)
@ -420,7 +417,7 @@ private static void VerifyMiscFatefulEncounter(LegalityAnalysis data)
// Hatching in Gen3 doesn't change the origin version.
if (pk.Format != 3)
return; // possible hatched in either game, don't bother checking
if (pk.Met_Location <= 087) // hatched in RS or Emerald
if (Locations.IsMetLocation3RS((ushort)pk.Met_Location)) // hatched in RS or Emerald
return; // possible hatched in either game, don't bother checking
// else, ensure fateful is active (via below)
}
@ -484,7 +481,7 @@ private static void VerifyMiscMovePP(LegalityAnalysis data)
private static void VerifyMiscEggCommon(LegalityAnalysis data)
{
var pk = data.Entity;
if (pk.Move1_PPUps > 0 || pk.Move2_PPUps > 0 || pk.Move3_PPUps > 0 || pk.Move4_PPUps > 0)
if (pk.Move1_PPUps != 0 || pk.Move2_PPUps != 0 || pk.Move3_PPUps != 0 || pk.Move4_PPUps != 0)
data.AddLine(GetInvalid(LEggPPUp, Egg));
if (!IsZeroMovePP(pk))
data.AddLine(GetInvalid(LEggPP, Egg));

View File

@ -44,7 +44,7 @@ public override PK1 Clone()
public override int Status_Condition { get => Data[4]; set => Data[4] = (byte)value; }
public byte Type1 { get => Data[5]; set => Data[5] = value; }
public byte Type2 { get => Data[6]; set => Data[6] = value; }
public byte Catch_Rate { get => Data[7]; set => Data[7] = value; }
public byte CatchRate { get => Data[7]; set => Data[7] = value; }
public override ushort Move1 { get => Data[8]; set => Data[8] = (byte)value; }
public override ushort Move2 { get => Data[9]; set => Data[9] = (byte)value; }
public override ushort Move3 { get => Data[10]; set => Data[10] = (byte)value; }
@ -104,11 +104,11 @@ private void SetSpeciesValues(ushort value)
Type2 = pi.Type2;
// Before updating catch rate, check if non-standard
if (IsValidCatchRateAnyPreEvo((byte)value, Catch_Rate))
if (IsValidCatchRateAnyPreEvo((byte)value, CatchRate))
return;
// Matches nothing possible; just reset to current Species' rate.
Catch_Rate = (byte)pi.CatchRate;
CatchRate = pi.CatchRate;
}
private static bool IsValidCatchRateAnyPreEvo(byte species, byte rate)
@ -145,7 +145,7 @@ private static bool IsValidCatchRateAnyPreEvo(byte species, byte rate)
public override int MaxItemID => Legal.MaxItemID_1;
// Extra
public int Gen2Item => ItemConverter.GetItemFuture1(Catch_Rate);
public int Gen2Item => ItemConverter.GetItemFuture1(CatchRate);
public PK2 ConvertToPK2()
{

View File

@ -5,7 +5,7 @@ namespace PKHeX.Core;
/// <summary>
/// Logic for converting Item IDs between the generation specific value sets.
/// </summary>
internal static class ItemConverter
public static class ItemConverter
{
/// <summary>Unused item ID, placeholder for item/sprite finding in Generations 2-4.</summary>
private const ushort NaN = 128;
@ -15,32 +15,32 @@ internal static class ItemConverter
/// </summary>
/// <param name="item">Generation 3 Item ID.</param>
/// <returns>True if transferable, False if not transferable.</returns>
internal static bool IsItemTransferable34(ushort item) => item != NaN && item > 0;
public static bool IsItemTransferable34(ushort item) => item != NaN && item > 0;
/// <summary>
/// Converts a Generation 3 Item ID to Generation 4+ Item ID.
/// </summary>
/// <param name="item">Generation 3 Item ID.</param>
/// <returns>Generation 4+ Item ID.</returns>
internal static ushort GetItemFuture3(ushort item) => item > arr3.Length ? NaN : arr3[item];
public static ushort GetItemFuture3(ushort item) => item > Item3to4.Length ? NaN : Item3to4[item];
/// <summary>
/// Converts a Generation 2 Item ID to Generation 4+ Item ID.
/// </summary>
/// <param name="item">Generation 2 Item ID.</param>
/// <returns>Generation 4+ Item ID.</returns>
internal static ushort GetItemFuture2(byte item) => item > arr2.Length ? NaN : arr2[item];
public static ushort GetItemFuture2(byte item) => item > Item2to4.Length ? NaN : Item2to4[item];
/// <summary>
/// Converts a Generation 4+ Item ID to Generation 3 Item ID.
/// </summary>
/// <param name="item">Generation 4+ Item ID.</param>
/// <returns>Generation 3 Item ID.</returns>
private static ushort GetItemOld3(ushort item)
public static ushort GetItemOld3(ushort item)
{
if (item == NaN)
return 0;
int index = arr3.IndexOf(item);
int index = Item3to4.IndexOf(item);
return (ushort)Math.Max(0, index);
}
@ -49,17 +49,17 @@ private static ushort GetItemOld3(ushort item)
/// </summary>
/// <param name="item">Generation 4+ Item ID.</param>
/// <returns>Generation 2 Item ID.</returns>
private static byte GetItemOld2(ushort item)
public static byte GetItemOld2(ushort item)
{
if (item == NaN)
return 0;
int index = arr2.IndexOf(item);
int index = Item2to4.IndexOf(item);
return (byte)Math.Max(0, index);
}
#region Item Mapping Tables
/// <summary> Gen2 items (index) and their corresponding Gen4 item ID (value) </summary>
private static ReadOnlySpan<ushort> arr2 =>
private static ReadOnlySpan<ushort> Item2to4 =>
[
000, 001, 002, 213, 003, 004, NaN, 450, 081, 018, // 0
019, 020, 021, 022, 023, 024, 025, 026, 017, 078, // 1
@ -90,7 +90,7 @@ private static byte GetItemOld2(ushort item)
];
/// <summary> Gen3 items (index) and their corresponding Gen4 item ID (value) </summary>
private static ReadOnlySpan<ushort> arr3 =>
private static ReadOnlySpan<ushort> Item3to4 =>
[
000, 001, 002, 003, 004, 005, 006, 007, 008, 009,
010, 011, 012, 017, 018, 019, 020, 021, 022, 023,
@ -149,11 +149,11 @@ private static byte GetItemOld2(ushort item)
};
/// <summary>
/// Converts a Gen1 <see cref="PK1.Catch_Rate"/> value to Gen2 Item.
/// Converts a Gen1 <see cref="PK1.CatchRate"/> value to Gen2 Item.
/// </summary>
/// <param name="value">Gen1 Item</param>
/// <returns>Gen2 Item</returns>
internal static int GetItemFuture1(byte value)
public static int GetItemFuture1(byte value)
{
if (!IsItemTransferable12(value))
return GetTeruSamaItem(value);
@ -169,7 +169,7 @@ internal static int GetItemFuture1(byte value)
/// <param name="srcFormat">Format from importing</param>
/// <param name="destFormat">Format required for holder</param>
/// <returns>destItem</returns>
internal static int GetItemForFormat(int srcItem, EntityContext srcFormat, EntityContext destFormat)
public static int GetItemForFormat(int srcItem, EntityContext srcFormat, EntityContext destFormat)
{
if (srcItem <= 0)
return 0;
@ -208,7 +208,7 @@ internal static int GetItemForFormat(int srcItem, EntityContext srcFormat, Entit
/// <param name="item">Item ID</param>
/// <param name="generation">Generation the <see cref="item"/> exists in</param>
/// <returns>True if is an HM</returns>
internal static bool IsItemHM(ushort item, int generation) => generation switch
public static bool IsItemHM(ushort item, int generation) => generation switch
{
1 => item is (>= 196 and <= 200),
2 => item is (>= 243 and <= 249),

View File

@ -16,8 +16,24 @@ public static class Experience
public static int GetLevel(uint exp, byte growth)
{
var table = GetTable(growth);
return GetLevel(exp, table);
}
/// <summary>
/// Gets the current level of a species.
/// </summary>
/// <param name="exp">Experience points</param>
/// <param name="table">Experience growth table</param>
/// <returns>Current level of the species.</returns>
public static int GetLevel(uint exp, ReadOnlySpan<uint> table)
{
// Eagerly return 100 if the exp is at max
// Also avoids overflow issues with the table in the event EXP is out of bounds
if (exp >= table[^1])
return 100;
// Most will be below level 50, so start from the bottom
// Don't bother with binary search, as the table is small
int tl = 1; // Initial Level. Iterate upwards to find the level
while (exp >= table[tl])
++tl;
@ -41,7 +57,13 @@ public static uint GetEXP(int level, byte growth)
return table[level - 1];
}
private static ReadOnlySpan<uint> GetTable(byte growth) => growth switch
/// <summary>
/// Gets the minimum Experience points for all levels possible.
/// </summary>
/// <param name="growth">Growth Rate type</param>
/// <returns>Experience points needed to have an indexed level.</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static ReadOnlySpan<uint> GetTable(byte growth) => growth switch
{
0 => Growth0,
1 => Growth1,

View File

@ -21,7 +21,7 @@ public sealed class PersonalInfo1(byte[] Data) : PersonalInfo, IPersonalInfoTM
public override int SPD { get => SPC; set => SPC = value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int BaseEXP { get => Data[0x09]; set => Data[0x09] = (byte)value; }
public byte Move1 { get => Data[0x0F]; set => Data[0x0F] = value; }
public byte Move2 { get => Data[0x10]; set => Data[0x10] = value; }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo2(byte[] Data) : PersonalInfo, IPersonalInfoTM,
public override int SPD { get => Data[0x06]; set => Data[0x06] = (byte)value; }
public override byte Type1 { get => Data[0x07]; set => Data[0x07] = value; }
public override byte Type2 { get => Data[0x08]; set => Data[0x08] = value; }
public override int CatchRate { get => Data[0x09]; set => Data[0x09] = (byte)value; }
public override byte CatchRate { get => Data[0x09]; set => Data[0x09] = value; }
public override int BaseEXP { get => Data[0x0A]; set => Data[0x0A] = (byte)value; }
public int Item1 { get => Data[0xB]; set => Data[0xB] = (byte)value; }
public int Item2 { get => Data[0xC]; set => Data[0xC] = (byte)value; }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo3(byte[] Data) : PersonalInfo, IPersonalAbility1
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int BaseEXP { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo4(byte[] Data) : PersonalInfo, IPersonalAbility1
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int BaseEXP { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo5B2W2(byte[] Data) : PersonalInfo, IPersonalAbil
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo5BW(byte[] Data) : PersonalInfo, IPersonalAbilit
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo6AO(byte[] Data) : PersonalInfo, IPersonalAbilit
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo6XY(byte[] Data) : PersonalInfo, IPersonalAbilit
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -21,7 +21,7 @@ public sealed class PersonalInfo7(byte[] Data)
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo7GG(byte[] Data) : PersonalInfo, IPersonalAbilit
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -26,7 +26,7 @@ public sealed class PersonalInfo8BDSP(byte[] Data)
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo8LA(byte[] Data) : PersonalInfo, IPersonalAbilit
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -21,7 +21,7 @@ public sealed class PersonalInfo8SWSH(byte[] Data) : PersonalInfo, IPersonalAbil
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -20,7 +20,7 @@ public sealed class PersonalInfo9SV(byte[] Data) : PersonalInfo, IPersonalAbilit
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override byte Type1 { get => Data[0x06]; set => Data[0x06] = value; }
public override byte Type2 { get => Data[0x07]; set => Data[0x07] = value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override byte CatchRate { get => Data[0x08]; set => Data[0x08] = value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
public override int EV_HP { get => (EVYield >> 0) & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | ((value & 0x3) << 0); }

View File

@ -18,7 +18,7 @@ public interface IPersonalEncounter
/// <summary>
/// Catch Rate
/// </summary>
int CatchRate { get; set; }
byte CatchRate { get; set; }
/// <summary>
/// Initial Friendship when captured or received.

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