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>
<PropertyGroup>
<Version>23.06.03</Version>
<Version>23.07.09</Version>
<LangVersion>11</LangVersion>
<Nullable>enable</Nullable>
<NeutralLanguage>en</NeutralLanguage>

View file

@ -298,7 +298,8 @@ public sealed class ShowdownSet : IBattleTemplate
var val = Util.ToInt32(value);
if ((uint)val > 10)
return false;
return (DynamaxLevel = (byte)val) is (>= 0 and <= 10);
DynamaxLevel = (byte)val;
return true;
}
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(131, 25, FRLG) { Gift = true, Location = 134 }, // Lapras @ Silph Co.
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
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);
#endregion
#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( 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 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);

View file

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System;
namespace PKHeX.Core;
@ -44,33 +44,40 @@ public sealed record EncounterSlot2 : EncounterSlot, INumberedSlot
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
{ 04, 0x0FF_3FF }, // Route 30
{ 05, 0x3FE_3FF }, // Route 31
{ 08, 0x3EE_3FF }, // Route 32
{ 11, 0x240_3FF }, // Route 33
{ 12, 0x37F_3FF }, // Azalea Town
{ 14, 0x3FF_3FF }, // Ilex Forest
{ 15, 0x001_3FE }, // Route 34
{ 18, 0x261_3FF }, // Route 35
{ 20, 0x3FF_3FF }, // Route 36
{ 21, 0x2B9_3FF }, // Route 37
{ 25, 0x3FF_3FF }, // Route 38
{ 26, 0x184_3FF }, // Route 39
{ 34, 0x3FF_3FF }, // Route 42
{ 37, 0x3FF_3FF }, // Route 43
{ 38, 0x3FF_3FF }, // Lake of Rage
{ 39, 0x2FF_3FF }, // Route 44
{ 91, 0x200_1FF }, // Route 26
{ 92, 0x2BB_3FF }, // Route 27
02, 04, 05, 08, 11, 12, 14, 15, 18, 20, 21, 25, 26, 34, 37, 38, 39, 91, 92,
};
private static ReadOnlySpan<int> Trees => new[]
{
0x3FF_3FF, // Route 29
0x0FF_3FF, // Route 30
0x3FE_3FF, // Route 31
0x3EE_3FF, // Route 32
0x240_3FF, // Route 33
0x37F_3FF, // Azalea Town
0x3FF_3FF, // Ilex Forest
0x001_3FE, // Route 34
0x261_3FF, // Route 35
0x3FF_3FF, // Route 36
0x2B9_3FF, // Route 37
0x3FF_3FF, // Route 38
0x184_3FF, // Route 39
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)
{
if (!Trees.TryGetValue(Location, out var permissions))
var treeIndex = TreeIndexes.BinarySearch((byte)Location);
if (treeIndex < 0)
return false;
var permissions = Trees[treeIndex];
var pivot = trainerID % 10;
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)
{
Span<ushort> moves = stackalloc ushort[4];
var source = GameData.GetLearnSource(GameVersion.GG);
ILearnSource source = LearnSource7GG.Instance;
source.SetEncounterMoves(Species, Form, level, moves);
pk.SetMoves(moves);
pk.SetMaximumPPCurrent(moves);

View file

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

View file

@ -18,7 +18,7 @@ public struct EvolutionNode
/// </summary>
/// <param name="link">Link to register</param>
/// <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)
First = link;

View file

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

View file

@ -18,13 +18,13 @@ public sealed class EvolutionReverseLookup : IEvolutionLookup
MaxSpecies = maxSpecies;
}
private void Register(EvolutionLink link, int index)
private void Register(in EvolutionLink link, int index)
{
ref var node = ref Nodes[index];
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)
{

View file

@ -459,10 +459,14 @@ public sealed class BallVerifier : Verifier
if (species is >= (int)Species.Sprigatito and <= (int)Species.Quaquaval) // G9 Starters
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 })
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;
if (IsPaldeaCatchAndBreed(species) && IsBallPermitted(BallUseLegality.WildPokeballs9, pk.Ball))
return GetValid(LBallSpeciesPass);

View file

@ -81,8 +81,6 @@ public sealed class HistoryVerifier : Verifier
data.AddLine(GetInvalid(LTransferCurrentHandlerInvalid));
if ((Info.Generation != pk.Format || neverOT) && pk.CurrentHandler != 1)
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

View file

@ -25,8 +25,8 @@ public static class RibbonStrings
var index = line.IndexOf('\t');
if (index < 0)
continue;
var name = line[..index];
var text = line[(index + 1)..];
var name = line[..index];
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>
/// Side game data for <see cref="PA8"/> data transferred into HOME.
/// </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 int SIZE = HomeCrypto.SIZE_2GAME_PA8;

View file

@ -1,7 +1,17 @@
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
{
/// <summary>
/// Ability ID for the <see cref="PKM"/>.
/// </summary>
ushort Ability { get; set; }
/// <summary>
/// Ability number for the <see cref="PKM"/>.
/// </summary>
byte AbilityNumber { get; set; }
}

View file

@ -1,11 +1,29 @@
namespace PKHeX.Core;
/// <summary>
/// Interface for a HOME storage system.
/// </summary>
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);
/// <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;
}
/// <summary>
/// Facade for a HOME storage system.
/// </summary>
public sealed class HomeStorageFacade : IHomeStorage
{
public bool Exists(ulong tracker) => false;

View file

@ -1,6 +1,12 @@
namespace PKHeX.Core;
/// <summary>
/// Holds the Pokerus status of a <see cref="PKM"/>.
/// </summary>
public interface IPokerusStatus
{
/// <summary>
/// Pokerus Strain and Duration
/// </summary>
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 })
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.
// Can just assume min-level
return (byte)originalMet;

View file

@ -316,7 +316,7 @@ public sealed class PA8 : PKM, ISanityChecksum,
public bool RIB47_5 { get => FlagUtil.GetFlag(Data, 0x47, 5); set => FlagUtil.SetFlag(Data, 0x47, 5, value); }
public bool RIB47_6 { get => FlagUtil.GetFlag(Data, 0x47, 6); set => FlagUtil.SetFlag(Data, 0x47, 6, value); }
public bool RIB47_7 { get => FlagUtil.GetFlag(Data, 0x47, 7); set => FlagUtil.SetFlag(Data, 0x47, 7, value); }
public int RibbonCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b00000000_00011111__11111111_11111111__11111111_11111111__11111111_11111111)
+ BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x40)) & 0b00000000_00000000__00000100_00011100__00000000_00000000__00000000_00000000);
public int MarkCount => BitOperations.PopCount(ReadUInt64LittleEndian(Data.AsSpan(0x34)) & 0b11111111_11100000__00000000_00000000__00000000_00000000__00000000_00000000)

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();
else
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;
}
@ -40,6 +40,7 @@ internal sealed class SAV2Offsets
public int Gender { get; private set; } = -1;
public int AccumulatedChecksumEnd { 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 EventWork { get; }
public int Daycare { get; }
@ -112,7 +113,7 @@ internal sealed class SAV2Offsets
break;
default:
throw new ArgumentException(nameof(version));
throw new ArgumentOutOfRangeException(nameof(version), version, null);
}
}
@ -176,12 +177,10 @@ internal sealed class SAV2Offsets
break;
default:
throw new ArgumentException(nameof(version));
throw new ArgumentOutOfRangeException(nameof(version), version, null);
}
}
public int OverallChecksumPosition2 { get; set; }
private void LoadOffsetsKorean()
{
RTCFlags = 0x1060;

View file

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

View file

@ -119,15 +119,24 @@ public static partial class Util
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++)
{
var slice = seed.Slice(i * 2, 2);
result[^(i+1)] = (byte)GetHexValue(slice);
var slice = input.Slice(i * 2, 2);
result[^(i + 1)] = (byte)GetHexValue(slice);
}
return result;
}
private const string HexChars = "0123456789ABCDEF";

View file

@ -108,7 +108,7 @@ public partial class SizeCP : UserControl
var label = L_SizeH;
var value = ss.HeightScalar;
label.Text = SizeClass[(int)PokeSizeUtil.GetSizeRating(value)];
SetLabelColorHeightWeight(label, value);
SetLabelColorHeightWeight(label);
}
if (!CHK_Auto.Checked || Loading || sv == null)
@ -128,7 +128,7 @@ public partial class SizeCP : UserControl
var label = L_SizeW;
var value = ss.WeightScalar;
label.Text = SizeClass[(int)PokeSizeUtil.GetSizeRating(value)];
SetLabelColorHeightWeight(label, value);
SetLabelColorHeightWeight(label);
}
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)
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
label.ResetForeColor();
}

View file

@ -1,7 +1,27 @@
PKHeX - By Kaphotics
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 !
- - 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.
@ -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: DPI scaling adjusted for better appearance on high resolution monitors.
23/05/11 - New Update:
23/05/11 - New Update: (80094) [6802733]
- Legality:
- - 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.
@ -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).
- 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:
- - Changed: Vivillon 3DS Region handling reworked to handle all regions. Thanks @abcboy101!
- - Fixed: BDSP footprint ribbon now checks correctly.