Update 23.07.09

This commit is contained in:
Kurt 2023-07-09 14:10:40 -07:00
parent b340e2327a
commit 26cabd022b
29 changed files with 242 additions and 135 deletions

View file

@ -1,6 +1,6 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>23.06.03</Version> <Version>23.07.09</Version>
<LangVersion>11</LangVersion> <LangVersion>11</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<NeutralLanguage>en</NeutralLanguage> <NeutralLanguage>en</NeutralLanguage>

View file

@ -298,7 +298,8 @@ public sealed class ShowdownSet : IBattleTemplate
var val = Util.ToInt32(value); var val = Util.ToInt32(value);
if ((uint)val > 10) if ((uint)val > 10)
return false; return false;
return (DynamaxLevel = (byte)val) is (>= 0 and <= 10); DynamaxLevel = (byte)val;
return true;
} }
private bool ParseTeraType(ReadOnlySpan<char> value) private bool ParseTeraType(ReadOnlySpan<char> value)

View file

@ -42,7 +42,7 @@ internal static class Encounters3FRLG
new(129, 05, FRLG) { Gift = true, Location = 099 }, // Magikarp @ Route 4 new(129, 05, FRLG) { Gift = true, Location = 099 }, // Magikarp @ Route 4
new(131, 25, FRLG) { Gift = true, Location = 134 }, // Lapras @ Silph Co. new(131, 25, FRLG) { Gift = true, Location = 134 }, // Lapras @ Silph Co.
new(133, 25, FRLG) { Gift = true, Location = 094 }, // Eevee @ Celadon City new(133, 25, FRLG) { Gift = true, Location = 094 }, // Eevee @ Celadon City
new(175, 05, FRLG) { Gift = true, EggLocation = 253 }, // Togepi Egg new(175, 05, FRLG) { Gift = true, EggLocation = 253, Moves = new(045,204,118) }, // Togepi Egg
// Celadon City Game Corner // Celadon City Game Corner
new(063, 09, FR) { Gift = true, Location = 94 }, // Abra new(063, 09, FR) { Gift = true, Location = 94 }, // Abra

View file

@ -171,7 +171,7 @@ public static class Encounters5B2W2
private static readonly EncounterStatic5[] Encounter_B2W2 = ArrayUtil.ConcatAll(Encounter_B2W2_Regular, Encounter_B2W2_N, Encounter_DreamRadar); private static readonly EncounterStatic5[] Encounter_B2W2 = ArrayUtil.ConcatAll(Encounter_B2W2_Regular, Encounter_B2W2_N, Encounter_DreamRadar);
#endregion #endregion
#region Trade Tables #region Trade Tables
internal static readonly EncounterTrade5[] TradeGift_B2W2_Regular = private static readonly EncounterTrade5[] TradeGift_B2W2_Regular =
{ {
new(B2 ) { Species = 548, Level = 20, Ability = OnlySecond, TID16 = 65217, SID16 = 00000, OTGender = 1, Gender = 1, IVs = new(20,20,20,20,31,20), Nature = Nature.Timid }, // Petilil new(B2 ) { Species = 548, Level = 20, Ability = OnlySecond, TID16 = 65217, SID16 = 00000, OTGender = 1, Gender = 1, IVs = new(20,20,20,20,31,20), Nature = Nature.Timid }, // Petilil
new( W2) { Species = 546, Level = 20, Ability = OnlyFirst, TID16 = 05720, SID16 = 00001, OTGender = 0, Gender = 0, IVs = new(20,20,20,20,31,20), Nature = Nature.Modest }, // Cottonee new( W2) { Species = 546, Level = 20, Ability = OnlyFirst, TID16 = 05720, SID16 = 00001, OTGender = 0, Gender = 0, IVs = new(20,20,20,20,31,20), Nature = Nature.Modest }, // Cottonee

View file

@ -21,7 +21,6 @@ public sealed record EncounterEgg(ushort Species, byte Form, byte Level, int Gen
public AbilityPermission Ability => AbilityPermission.Any12H; public AbilityPermission Ability => AbilityPermission.Any12H;
public bool CanHaveVoltTackle => Species is (int)Core.Species.Pichu && (Generation > 3 || Version is GameVersion.E); public bool CanHaveVoltTackle => Species is (int)Core.Species.Pichu && (Generation > 3 || Version is GameVersion.E);
public bool CanInheritMoves => Breeding.GetCanInheritMoves(Species);
public PKM ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr, EncounterCriteria.Unrestricted); public PKM ConvertToPKM(ITrainerInfo tr) => ConvertToPKM(tr, EncounterCriteria.Unrestricted);

View file

@ -1,4 +1,4 @@
using System.Collections.Generic; using System;
namespace PKHeX.Core; namespace PKHeX.Core;
@ -44,33 +44,40 @@ public sealed record EncounterSlot2 : EncounterSlot, INumberedSlot
pk2.Met_TimeOfDay = ((EncounterArea2)Area).Time.RandomValidTime(); pk2.Met_TimeOfDay = ((EncounterArea2)Area).Time.RandomValidTime();
} }
private static readonly Dictionary<int, int> Trees = new() private static ReadOnlySpan<byte> TreeIndexes => new byte[]
{ {
{ 02, 0x3FF_3FF }, // Route 29 02, 04, 05, 08, 11, 12, 14, 15, 18, 20, 21, 25, 26, 34, 37, 38, 39, 91, 92,
{ 04, 0x0FF_3FF }, // Route 30 };
{ 05, 0x3FE_3FF }, // Route 31
{ 08, 0x3EE_3FF }, // Route 32 private static ReadOnlySpan<int> Trees => new[]
{ 11, 0x240_3FF }, // Route 33 {
{ 12, 0x37F_3FF }, // Azalea Town 0x3FF_3FF, // Route 29
{ 14, 0x3FF_3FF }, // Ilex Forest 0x0FF_3FF, // Route 30
{ 15, 0x001_3FE }, // Route 34 0x3FE_3FF, // Route 31
{ 18, 0x261_3FF }, // Route 35 0x3EE_3FF, // Route 32
{ 20, 0x3FF_3FF }, // Route 36 0x240_3FF, // Route 33
{ 21, 0x2B9_3FF }, // Route 37 0x37F_3FF, // Azalea Town
{ 25, 0x3FF_3FF }, // Route 38 0x3FF_3FF, // Ilex Forest
{ 26, 0x184_3FF }, // Route 39 0x001_3FE, // Route 34
{ 34, 0x3FF_3FF }, // Route 42 0x261_3FF, // Route 35
{ 37, 0x3FF_3FF }, // Route 43 0x3FF_3FF, // Route 36
{ 38, 0x3FF_3FF }, // Lake of Rage 0x2B9_3FF, // Route 37
{ 39, 0x2FF_3FF }, // Route 44 0x3FF_3FF, // Route 38
{ 91, 0x200_1FF }, // Route 26 0x184_3FF, // Route 39
{ 92, 0x2BB_3FF }, // Route 27 0x3FF_3FF, // Route 42
0x3FF_3FF, // Route 43
0x3FF_3FF, // Lake of Rage
0x2FF_3FF, // Route 44
0x200_1FF, // Route 26
0x2BB_3FF, // Route 27
}; };
public bool IsTreeAvailable(ushort trainerID) public bool IsTreeAvailable(ushort trainerID)
{ {
if (!Trees.TryGetValue(Location, out var permissions)) var treeIndex = TreeIndexes.BinarySearch((byte)Location);
if (treeIndex < 0)
return false; return false;
var permissions = Trees[treeIndex];
var pivot = trainerID % 10; var pivot = trainerID % 10;
var type = Area.Type; var type = Area.Type;

View file

@ -50,7 +50,7 @@ public sealed record EncounterSlot7GO : EncounterSlotGO
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level) protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{ {
Span<ushort> moves = stackalloc ushort[4]; Span<ushort> moves = stackalloc ushort[4];
var source = GameData.GetLearnSource(GameVersion.GG); ILearnSource source = LearnSource7GG.Instance;
source.SetEncounterMoves(Species, Form, level, moves); source.SetEncounterMoves(Species, Form, level, moves);
pk.SetMoves(moves); pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves); pk.SetMaximumPPCurrent(moves);

View file

@ -103,7 +103,7 @@ public sealed record EncounterSlot8GO : EncounterSlotGO, IFixedOTFriendship, IEn
s.HeightScalar = PokeSizeUtil.GetRandomScalar(); s.HeightScalar = PokeSizeUtil.GetRandomScalar();
s.WeightScalar = PokeSizeUtil.GetRandomScalar(); s.WeightScalar = PokeSizeUtil.GetRandomScalar();
if (pk is IScaledSize3 s3) if (pk is IScaledSize3 s3)
s3.Scale = s.HeightScalar = PokeSizeUtil.GetRandomScalar(); s3.Scale = s.HeightScalar;
} }
if (pk is PA8 pa8) if (pk is PA8 pa8)

View file

@ -18,7 +18,7 @@ public struct EvolutionNode
/// </summary> /// </summary>
/// <param name="link">Link to register</param> /// <param name="link">Link to register</param>
/// <exception cref="InvalidOperationException"> is thrown if the node is full.</exception> /// <exception cref="InvalidOperationException"> is thrown if the node is full.</exception>
public void Add(EvolutionLink link) public void Add(in EvolutionLink link)
{ {
if (First.IsEmpty) if (First.IsEmpty)
First = link; First = link;

View file

@ -84,7 +84,7 @@ public static class EvolutionReversal
return false; return false;
} }
private static EvoCriteria Create(EvolutionLink link, byte currentMaxLevel) => new() private static EvoCriteria Create(in EvolutionLink link, byte currentMaxLevel) => new()
{ {
Species = link.Species, Species = link.Species,
Form = link.Form, Form = link.Form,

View file

@ -18,13 +18,13 @@ public sealed class EvolutionReverseLookup : IEvolutionLookup
MaxSpecies = maxSpecies; MaxSpecies = maxSpecies;
} }
private void Register(EvolutionLink link, int index) private void Register(in EvolutionLink link, int index)
{ {
ref var node = ref Nodes[index]; ref var node = ref Nodes[index];
node.Add(link); node.Add(link);
} }
public void Register(EvolutionLink link, ushort species, byte form) public void Register(in EvolutionLink link, ushort species, byte form)
{ {
if (form == 0) if (form == 0)
{ {

View file

@ -459,10 +459,14 @@ public sealed class BallVerifier : Verifier
if (species is >= (int)Species.Sprigatito and <= (int)Species.Quaquaval) // G9 Starters if (species is >= (int)Species.Sprigatito and <= (int)Species.Quaquaval) // G9 Starters
return VerifyBallEquals(data, (int)Poke); return VerifyBallEquals(data, (int)Poke);
// PLA Voltorb: Only via PLA (transfer only, not wild) // PLA Voltorb: Only via PLA (transfer only, not wild) and GO
if (enc is { Species: (ushort)Species.Voltorb, Form: 1 }) if (enc is { Species: (ushort)Species.Voltorb, Form: 1 })
return VerifyBallEquals(data, BallUseLegality.WildPokeballs8g); return VerifyBallEquals(data, BallUseLegality.WildPokeballs8g);
// S/V Tauros forms > 1: Only local Wild Balls for Blaze/Aqua breeds -- can't inherit balls from Kantonian/Combat.
if (enc is { Species: (ushort)Species.Tauros, Form: > 1 })
return VerifyBallEquals(data, BallUseLegality.WildPokeballs9);
var pk = data.Entity; var pk = data.Entity;
if (IsPaldeaCatchAndBreed(species) && IsBallPermitted(BallUseLegality.WildPokeballs9, pk.Ball)) if (IsPaldeaCatchAndBreed(species) && IsBallPermitted(BallUseLegality.WildPokeballs9, pk.Ball))
return GetValid(LBallSpeciesPass); return GetValid(LBallSpeciesPass);

View file

@ -81,8 +81,6 @@ public sealed class HistoryVerifier : Verifier
data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid)); data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid));
if ((Info.Generation != pk.Format || neverOT) && pk.CurrentHandler != 1) if ((Info.Generation != pk.Format || neverOT) && pk.CurrentHandler != 1)
data.AddLine(GetInvalid(LTransferHTFlagRequired)); data.AddLine(GetInvalid(LTransferHTFlagRequired));
if (pk is IHomeTrack { HasTracker: true } && pk.IsUntraded) // HOME sets HT name on moving in and out.
data.AddLine(GetInvalid(LTransferHTMismatchName));
} }
private static bool IsUntradeableEncounter(IEncounterTemplate enc) => enc switch private static bool IsUntradeableEncounter(IEncounterTemplate enc) => enc switch

View file

@ -25,8 +25,8 @@ public static class RibbonStrings
var index = line.IndexOf('\t'); var index = line.IndexOf('\t');
if (index < 0) if (index < 0)
continue; continue;
var name = line[..index];
var text = line[(index + 1)..]; var text = line[(index + 1)..];
var name = line[..index];
RibbonNames[name] = text; RibbonNames[name] = text;
} }
} }

View file

@ -1,76 +0,0 @@
using System;
namespace PKHeX.Core;
public static class EntityCharacteristic
{
public static int GetCharacteristic(uint ec, uint iv32)
{
int index = (int)(ec % 6);
int maxStatIndex = index;
var maxStatValue = 0u;
do
{
var value = iv32 >> (index * 5) & 0x1F;
if (value > maxStatValue)
{
maxStatIndex = index;
maxStatValue = value;
}
if (index != 5)
index++;
else
index = 0;
} while (maxStatIndex != index);
return (maxStatIndex * 5) + ((int)maxStatValue % 5);
}
public static int GetCharacteristic(uint ec, Span<int> ivs)
{
int index = (int)(ec % 6);
int maxStatIndex = index;
var maxStatValue = 0;
do
{
var value = ivs[index];
if (value > maxStatValue)
{
maxStatIndex = index;
maxStatValue = value;
}
if (index != 5)
index++;
else
index = 0;
} while (maxStatIndex != index);
return (maxStatIndex * 5) + (maxStatValue % 5);
}
public static int GetCharacteristicInvertFields(uint ec, uint iv32)
{
int index = (int)(ec % 6);
int maxStatIndex = index;
var maxStatValue = 0u;
do
{
// IVs are stored in reverse order, get the bits from the end of the IV value
var value = iv32 >> (27 - (index * 5)) & 0x1F;
if (value > maxStatValue)
{
maxStatIndex = index;
maxStatValue = value;
}
if (index != 5)
index++;
else
index = 0;
} while (maxStatIndex != index);
return (maxStatIndex * 5) + ((int)maxStatValue % 5);
}
}

View file

@ -6,7 +6,7 @@ namespace PKHeX.Core;
/// <summary> /// <summary>
/// Side game data for <see cref="PA8"/> data transferred into HOME. /// Side game data for <see cref="PA8"/> data transferred into HOME.
/// </summary> /// </summary>
public sealed class GameDataPA8 : HomeOptional1, IGameDataSide<PA8>, IScaledSizeAbsolute, IScaledSize3, IGameDataSplitAbility public sealed class GameDataPA8 : HomeOptional1, IGameDataSide<PA8>, IScaledSizeAbsolute, IScaledSize3, IGameDataSplitAbility, IPokerusStatus
{ {
private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PA8; private const HomeGameDataFormat ExpectFormat = HomeGameDataFormat.PA8;
private const int SIZE = HomeCrypto.SIZE_2GAME_PA8; private const int SIZE = HomeCrypto.SIZE_2GAME_PA8;

View file

@ -1,7 +1,17 @@
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Holds the ability and ability number for a <see cref="PKM"/> that has side-game specific data.
/// </summary>
public interface IGameDataSplitAbility public interface IGameDataSplitAbility
{ {
/// <summary>
/// Ability ID for the <see cref="PKM"/>.
/// </summary>
ushort Ability { get; set; } ushort Ability { get; set; }
/// <summary>
/// Ability number for the <see cref="PKM"/>.
/// </summary>
byte AbilityNumber { get; set; } byte AbilityNumber { get; set; }
} }

View file

@ -1,11 +1,29 @@
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Interface for a HOME storage system.
/// </summary>
public interface IHomeStorage public interface IHomeStorage
{ {
/// <summary>
/// Checks if the given tracker exists in the storage system.
/// </summary>
/// <param name="tracker">Tracker to check</param>
/// <returns>True if the tracker exists, false otherwise.</returns>
bool Exists(ulong tracker); bool Exists(ulong tracker);
/// <summary>
/// Gets the HOME entity for the given <see cref="PKM"/>.
/// </summary>
/// <typeparam name="T">Input PKM type</typeparam>
/// <param name="pk">PKM to get the entity for</param>
/// <returns>HOME entity for the given <see cref="PKM"/>.</returns>
PKH GetEntity<T>(T pk) where T : PKM; PKH GetEntity<T>(T pk) where T : PKM;
} }
/// <summary>
/// Facade for a HOME storage system.
/// </summary>
public sealed class HomeStorageFacade : IHomeStorage public sealed class HomeStorageFacade : IHomeStorage
{ {
public bool Exists(ulong tracker) => false; public bool Exists(ulong tracker) => false;

View file

@ -1,6 +1,12 @@
namespace PKHeX.Core; namespace PKHeX.Core;
/// <summary>
/// Holds the Pokerus status of a <see cref="PKM"/>.
/// </summary>
public interface IPokerusStatus public interface IPokerusStatus
{ {
/// <summary>
/// Pokerus Strain and Duration
/// </summary>
byte PKRS { get; set; } byte PKRS { get; set; }
} }

View file

@ -16,7 +16,7 @@ public static class ObedienceExtensions
{ {
if (entity.Species is (int)Species.Koraidon or (int)Species.Miraidon && entity is PK9 { FormArgument: not 0 }) if (entity.Species is (int)Species.Koraidon or (int)Species.Miraidon && entity is PK9 { FormArgument: not 0 })
return 0; // Box Legend ride-able is default 0. Everything else is met level! return 0; // Box Legend ride-able is default 0. Everything else is met level!
if (entity.Version is not (int)GameVersion.SL or (int)GameVersion.VL) if (entity.Version is not ((int)GameVersion.SL or (int)GameVersion.VL))
return (byte)entity.CurrentLevel; // foreign, play it safe. return (byte)entity.CurrentLevel; // foreign, play it safe.
// Can just assume min-level // Can just assume min-level
return (byte)originalMet; return (byte)originalMet;

View file

@ -0,0 +1,114 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Logic for calculating the characteristic behavior of Pokémon.
/// </summary>
public static class EntityCharacteristic
{
/// <summary>
/// Gets the characteristic index from the input parameters.
/// </summary>
/// <param name="maxStatIndex">Index of the highest IV.</param>
/// <param name="maxStatValue">Value of the highest IV.</param>
/// <returns>Characteristic index.</returns>
public static int GetCharacteristic(int maxStatIndex, int maxStatValue) => (maxStatIndex * 5) + (maxStatValue % 5);
/// <summary>
/// Gets the characteristic index of the given IVs in Little Endian format.
/// </summary>
/// <param name="ec">Encryption Constant.</param>
/// <param name="iv32">Lumped IVs in Little Endian format.</param>
/// <returns>Characteristic index.</returns>
public static int GetCharacteristic(uint ec, uint iv32)
{
int index = (int)(ec % 6);
// Get individual IVs from the lumped value.
// The IVs are stored in the following order: HP, Atk, Def, Spe, SpA, SpD
// Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen.
int maxStatIndex = index;
var maxStatValue = 0u;
do
{
var value = iv32 >> (index * 5) & 0x1F;
if (value > maxStatValue)
{
maxStatIndex = index;
maxStatValue = value;
}
if (index != 5)
index++;
else
index = 0;
} while (maxStatIndex != index);
return GetCharacteristic(maxStatIndex, (int)maxStatValue);
}
/// <summary>
/// Gets the characteristic index of the given unpacked IVs.
/// </summary>
/// <param name="ec">Encryption Constant.</param>
/// <param name="ivs">Unpacked IVs.</param>
/// <returns>Characteristic index.</returns>
public static int GetCharacteristic(uint ec, Span<int> ivs)
{
int index = (int)(ec % 6);
// Get individual IVs from the lumped value.
// The IVs are stored in the following order: HP, Atk, Def, Spe, SpA, SpD
// Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen.
int maxStatIndex = index;
var maxStatValue = 0;
do
{
var value = ivs[index];
if (value > maxStatValue)
{
maxStatIndex = index;
maxStatValue = value;
}
if (index != 5)
index++;
else
index = 0;
} while (maxStatIndex != index);
return GetCharacteristic(maxStatIndex, maxStatValue);
}
/// <summary>
/// Gets the characteristic index of the given IVs in Big Endian format.
/// </summary>
/// <param name="ec">Encryption Constant.</param>
/// <param name="iv32">Lumped IVs in Big Endian format.</param>
/// <returns>Characteristic index.</returns>
public static int GetCharacteristicInvertFields(uint ec, uint iv32)
{
int index = (int)(ec % 6);
// Get individual IVs from the lumped value.
// The IVs are stored in the following order: SpD, SpA, Spe, Def, Atk, HP
// Check all IVs, get the highest IV and its index. If there are multiple highest IVs, the first index checked is chosen.
int maxStatIndex = index;
var maxStatValue = 0u;
do
{
// IVs are stored in reverse order, get the bits from the end of the IV value
var value = iv32 >> (27 - (index * 5)) & 0x1F;
if (value > maxStatValue)
{
maxStatIndex = index;
maxStatValue = value;
}
if (index != 5)
index++;
else
index = 0;
} while (maxStatIndex != index);
return GetCharacteristic(maxStatIndex, (int)maxStatValue);
}
}

View file

@ -15,7 +15,7 @@ internal sealed class SAV2Offsets
LoadOffsetsKorean(); LoadOffsetsKorean();
else else
LoadOffsetsInternational(sav.Version); LoadOffsetsInternational(sav.Version);
Daycare = PokedexSeen + 0x1F + 28 + 1; // right after first unown seen Daycare = PokedexSeen + 0x1F + 28 + 1; // right after first Unown seen
EventWork = EventFlag - 0x100; EventWork = EventFlag - 0x100;
} }
@ -40,6 +40,7 @@ internal sealed class SAV2Offsets
public int Gender { get; private set; } = -1; public int Gender { get; private set; } = -1;
public int AccumulatedChecksumEnd { get; private set; } = -1; public int AccumulatedChecksumEnd { get; private set; } = -1;
public int OverallChecksumPosition { get; private set; } = -1; public int OverallChecksumPosition { get; private set; } = -1;
public int OverallChecksumPosition2 { get; private set; }
public int EventFlag { get; private set; } = -1; public int EventFlag { get; private set; } = -1;
public int EventWork { get; } public int EventWork { get; }
public int Daycare { get; } public int Daycare { get; }
@ -112,7 +113,7 @@ internal sealed class SAV2Offsets
break; break;
default: default:
throw new ArgumentException(nameof(version)); throw new ArgumentOutOfRangeException(nameof(version), version, null);
} }
} }
@ -176,12 +177,10 @@ internal sealed class SAV2Offsets
break; break;
default: default:
throw new ArgumentException(nameof(version)); throw new ArgumentOutOfRangeException(nameof(version), version, null);
} }
} }
public int OverallChecksumPosition2 { get; set; }
private void LoadOffsetsKorean() private void LoadOffsetsKorean()
{ {
RTCFlags = 0x1060; RTCFlags = 0x1060;

View file

@ -262,7 +262,7 @@ public abstract class InventoryPouch
{ {
// Cap at absolute maximum // Cap at absolute maximum
<= 2 when count > byte.MaxValue => byte.MaxValue, <= 2 when count > byte.MaxValue => byte.MaxValue,
>= 3 when count > ushort.MaxValue => ushort.MaxValue, _ when count > ushort.MaxValue => ushort.MaxValue,
_ => count, _ => count,
}; };
return true; return true;

View file

@ -119,15 +119,24 @@ public static partial class Util
return result; return result;
} }
public static byte[] GetBytesFromHexString(ReadOnlySpan<char> seed) /// <summary>
/// Parses a variable length hex string (non-spaced, bytes in reverse order).
/// </summary>
public static byte[] GetBytesFromHexString(ReadOnlySpan<char> input)
{
byte[] result = new byte[input.Length / 2];
GetBytesFromHexString(input, result);
return result;
}
/// <inheritdoc cref="GetBytesFromHexString(ReadOnlySpan{char})"/>
public static void GetBytesFromHexString(ReadOnlySpan<char> input, Span<byte> result)
{ {
byte[] result = new byte[seed.Length / 2];
for (int i = 0; i < result.Length; i++) for (int i = 0; i < result.Length; i++)
{ {
var slice = seed.Slice(i * 2, 2); var slice = input.Slice(i * 2, 2);
result[^(i+1)] = (byte)GetHexValue(slice); result[^(i + 1)] = (byte)GetHexValue(slice);
} }
return result;
} }
private const string HexChars = "0123456789ABCDEF"; private const string HexChars = "0123456789ABCDEF";

View file

@ -108,7 +108,7 @@ public partial class SizeCP : UserControl
var label = L_SizeH; var label = L_SizeH;
var value = ss.HeightScalar; var value = ss.HeightScalar;
label.Text = SizeClass[(int)PokeSizeUtil.GetSizeRating(value)]; label.Text = SizeClass[(int)PokeSizeUtil.GetSizeRating(value)];
SetLabelColorHeightWeight(label, value); SetLabelColorHeightWeight(label);
} }
if (!CHK_Auto.Checked || Loading || sv == null) if (!CHK_Auto.Checked || Loading || sv == null)
@ -128,7 +128,7 @@ public partial class SizeCP : UserControl
var label = L_SizeW; var label = L_SizeW;
var value = ss.WeightScalar; var value = ss.WeightScalar;
label.Text = SizeClass[(int)PokeSizeUtil.GetSizeRating(value)]; label.Text = SizeClass[(int)PokeSizeUtil.GetSizeRating(value)];
SetLabelColorHeightWeight(label, value); SetLabelColorHeightWeight(label);
} }
if (!CHK_Auto.Checked || Loading || sv == null) if (!CHK_Auto.Checked || Loading || sv == null)
@ -158,12 +158,10 @@ public partial class SizeCP : UserControl
} }
} }
private void SetLabelColorHeightWeight(Control label, byte value) private void SetLabelColorHeightWeight(Control label)
{ {
if (scale is not null) if (scale is not null)
label.ForeColor = Color.Gray; // not indicative of actual size label.ForeColor = Color.Gray; // not indicative of actual size
else if (value is 255 && scale is PA8)
label.ForeColor = Color.Red; // Alpha or (unlikely, user error?)
else else
label.ResetForeColor(); label.ResetForeColor();
} }

View file

@ -1,7 +1,27 @@
PKHeX - By Kaphotics PKHeX - By Kaphotics
http://projectpokemon.org/pkhex/ http://projectpokemon.org/pkhex/
23/06/03 - New Update: 23/07/09 - New Update:
- Legality: Updated evolution checking algorithms to better identify game visitation. Thanks @Lusamine & @sora10pls !
- - Added: HOME 3.0.0 move sharing logic for learning moves via HOME instead of in-game.
- - Added: HOME 3.0.0 ball inheritance logic for breeding encounters for/from Scarlet/Violet.
- - Added: GO Master Ball legality checks now allows Master Ball when encounter date & type permits.
- - Added: GO encounters outside the availability window now display a detailed illegal message.
- Added: Gen4/5 Geonet/Unity Tower editor. Thanks @abcboy101 !
- Changed: Gen9 Tera Type icons now use higher quality sprites from HOME. Thanks @sora10pls !
- Changed: Gen8a Move Shop editor GUI has been redesigned for a more visual experience.
- Changed: Gen7b+ Height/Weight now show as gray whenever Scale takes precedence in sizing determinations.
- Fixed: Batch Editor filters now work correctly for Box/Slot when using operators other than ! or =
- Fixed: Gen7b Randomize IV/AV buttons now center correctly within the form.
- Fixed: Gen7 Report grid now shows Resort (Poké Pelago) slots correctly.
- Fixed: Gen6/7 Memory Editor now shows Country/Region visitation correctly.
- Fixed: Gen6 Hall of Fame editor now keeps leading zeroes on the TID/SID entry fields.
- Fixed: Gen5 unused second Roamer block editor editing now reads from the correct offsets. Thanks SaltedNeos !
- Fixed: Gen4 Ranch checksums are now applied correctly again. Thanks @Zazsona !
- Fixed: Gen3 adding Ferry Tickets no longer causes the program to error when your Key Items bag is full.
- Fixed: Gen2 International Crystal saves now set the backup checksum to the correct offset. Thanks @Zazsona !
23/06/03 - New Update: (178908) [6981641]
- Added: HOME 3.0.0 initial support. Legality analysis is still under research. Thanks @Lusamine & @sora10pls ! - Added: HOME 3.0.0 initial support. Legality analysis is still under research. Thanks @Lusamine & @sora10pls !
- - Lacks full support for cross-game transfers involving evolutions and moves. Please be patient, there's a lot of things to handle! - - Lacks full support for cross-game transfers involving evolutions and moves. Please be patient, there's a lot of things to handle!
- Added: Tech Record editor GUI has been redesigned for a more visual experience. Can now sort alphabetically, owned, or move type. - Added: Tech Record editor GUI has been redesigned for a more visual experience. Can now sort alphabetically, owned, or move type.
@ -10,7 +30,7 @@ http://projectpokemon.org/pkhex/
- Changed: Gen7 Totem Form sprites now display with an Orange-colored glow, similar to Gen3 C/XD shadow purple. - Changed: Gen7 Totem Form sprites now display with an Orange-colored glow, similar to Gen3 C/XD shadow purple.
- Changed: DPI scaling adjusted for better appearance on high resolution monitors. - Changed: DPI scaling adjusted for better appearance on high resolution monitors.
23/05/11 - New Update: 23/05/11 - New Update: (80094) [6802733]
- Legality: - Legality:
- - Changed: Creating a new PKM from template will set current moves same as the games. - - Changed: Creating a new PKM from template will set current moves same as the games.
- - Changed: Pokérus strains 0 & 8 now permitted due to official implementation errors in Gen2 & Gen3. - - Changed: Pokérus strains 0 & 8 now permitted due to official implementation errors in Gen2 & Gen3.
@ -29,7 +49,7 @@ http://projectpokemon.org/pkhex/
- Fixed: Gen3 XD un-purified shadow Pokémon no longer flag w/o Ribbon if they have a Shadow Gauge of 0 (ready to purify). - Fixed: Gen3 XD un-purified shadow Pokémon no longer flag w/o Ribbon if they have a Shadow Gauge of 0 (ready to purify).
- Changed: Sub-form scaling mode changed to "Inherit" -- added a Display setting to revert back to Form scaling (from Dpi). - Changed: Sub-form scaling mode changed to "Inherit" -- added a Display setting to revert back to Form scaling (from Dpi).
23/04/06 - New Update: 23/04/06 - New Update: (166321) [6722639]
- Legality: - Legality:
- - Changed: Vivillon 3DS Region handling reworked to handle all regions. Thanks @abcboy101! - - Changed: Vivillon 3DS Region handling reworked to handle all regions. Thanks @abcboy101!
- - Fixed: BDSP footprint ribbon now checks correctly. - - Fixed: BDSP footprint ribbon now checks correctly.