Merge pull request #3382 from kwsch/pla

Update 22.02.04
Individual commits from this PR are not cherry-pickable in a vacuum; these were manually re-committed from a staging repo in order to group together changes for general public viewing. There were over 250 commits on the private development repo for this update.
This commit is contained in:
Kurt
2022-02-04 18:28:17 -08:00
committed by GitHub
426 changed files with 19650 additions and 2668 deletions

View File

@@ -24,13 +24,13 @@ PKHeX erwartet Spielstände, die mit konsolenspezifischen Schlüsseln entschlüs
## Screenshots
![Main Window](https://i.imgur.com/ApbTkM0.png)
![Main Window](https://i.imgur.com/5421hUR.png)
## Erstellen
PKHeX ist eine Windows Forms Anwendung, die das [.NET Framework v4.6](https://www.microsoft.com/en-us/download/details.aspx?id=48137) benötigt, mit experimenteller Unterstützung für [.NET 5.0](https://dotnet.microsoft.com/download/dotnet/5.0).
PKHeX ist eine Windows Forms Anwendung, die das [.NET Framework v4.6](https://www.microsoft.com/en-us/download/details.aspx?id=48137) benötigt, mit experimenteller Unterstützung für [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0).
Die Anwendung kann mit jedem Kompiler erstellt werde, der C# 8 unterstützt.
Die Anwendung kann mit jedem Kompiler erstellt werde, der C# 10 unterstützt.
### Erstell Konfiguration

View File

@@ -24,13 +24,13 @@ PKHeX espera archivos de guardado que no estén cifrados con las claves específ
## Capturas de Pantalla
![Pantalla principal](https://i.imgur.com/tw9zZap.png)
![Pantalla principal](https://i.imgur.com/umit9S2.png)
## Building
PKHeX es una aplicación de Windows Forms que requiere [.NET Framework v4.6](https://www.microsoft.com/en-us/download/details.aspx?id=48137), con soporte experimental para [.NET 5.0](https://dotnet.microsoft.com/download/dotnet/5.0).
PKHeX es una aplicación de Windows Forms que requiere [.NET Framework v4.6](https://www.microsoft.com/en-us/download/details.aspx?id=48137), con soporte experimental para [.NET 6.0](https://dotnet.microsoft.com/download/dotnet/6.0).
El archivo ejecutable puede ser construido con cualquier compilador que soporte C# 8.
El archivo ejecutable puede ser construido con cualquier compilador que soporte C# 10.
### Configuraciones del Build

View File

@@ -23,13 +23,13 @@ PKHeX attend des fichiers de sauvegarde qui ne sont pas chiffrés avec des clés
## Captures d'écran
![Main Window](https://i.imgur.com/OqOD05v.png)
![Main Window](https://i.imgur.com/EhtQ14x.png)
## Construction
PKHeX est une application Windows Forms qui nécessite [.NET Framework v4.6](https://www.microsoft.com/en-us/download/details.aspx?id=48137), avec une prise en charge expérimentale de [.NET 5.0.](https://dotnet.microsoft.com/download/dotnet/5.0)
PKHeX est une application Windows Forms qui nécessite [.NET Framework v4.6](https://www.microsoft.com/en-us/download/details.aspx?id=48137), avec une prise en charge expérimentale de [.NET 6.0.](https://dotnet.microsoft.com/download/dotnet/6.0)
L'exécutable peut être construit avec n'importe quel compilateur prenant en charge C# 8.
L'exécutable peut être construit avec n'importe quel compilateur prenant en charge C# 10.
### Construire les configurations

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core;
/// <summary>
/// Logic for modifying the Move Shop Record flags of a <see cref="PA8"/>.
/// </summary>
public static class MoveShopRecordApplicator
{
public static void ClearMoveShopFlags(this IMoveShop8 shop)
{
var bits = shop.MoveShopPermitFlags;
for (int i = 0; i < bits.Length; i++)
shop.SetPurchasedRecordFlag(i, false);
if (shop is IMoveShop8Mastery m)
m.ClearMoveShopFlagsMastered();
}
public static void ClearMoveShopFlagsMastered(this IMoveShop8Mastery shop)
{
var bits = shop.MoveShopPermitFlags;
for (int i = 0; i < bits.Length; i++)
shop.SetMasteredRecordFlag(i, false);
}
public static void SetMoveShopFlags(this IMoveShop8 shop, bool value, int max = 100)
{
var bits = shop.MoveShopPermitFlags;
max = Math.Min(bits.Length, max);
for (int i = 0; i < max; i++)
shop.SetPurchasedRecordFlag(i, value);
}
public static void SetMoveShopFlagsMastered(this IMoveShop8Mastery shop)
{
var bits = shop.MoveShopPermitFlags;
for (int i = 0; i < bits.Length; i++)
shop.SetMasteredRecordFlag(i, shop.GetPurchasedRecordFlag(i));
}
public static void SetMoveShopFlags(this IMoveShop8 shop)
{
var permit = shop.MoveShopPermitFlags;
for (int index = 0; index < permit.Length; index++)
{
if (permit[index])
shop.SetPurchasedRecordFlag(index, true);
}
}
/// <summary>
/// Sets the Shop Record flags for the <see cref="shop"/> based on the current moves.
/// </summary>
/// <param name="shop">Pokémon to modify.</param>
/// <param name="moves">Moves to set flags for. If a move is not a Technical Record, it is skipped.</param>
public static void SetMoveShopFlags(this IMoveShop8 shop, IEnumerable<int> moves)
{
var permit = shop.MoveShopPermitFlags;
var moveIDs = shop.MoveShopPermitIndexes;
foreach (var m in moves)
{
var index = moveIDs.IndexOf(m);
if (index == -1)
continue;
if (permit[index])
shop.SetPurchasedRecordFlag(index, true);
}
}
}

View File

@@ -14,38 +14,37 @@ public static class TechnicalRecordApplicator
/// <param name="pk">Pokémon to modify.</param>
/// <param name="value">Value to set for the record.</param>
/// <param name="max">Max record to set.</param>
public static void SetRecordFlags(this PKM pk, bool value, int max = 100)
public static void SetRecordFlags(this ITechRecord8 pk, bool value, int max = 100)
{
if (pk is not PK8 pk8)
return;
for (int i = 0; i < max; i++)
pk8.SetMoveRecordFlag(i, value);
pk.SetMoveRecordFlag(i, value);
}
/// <summary>
/// Clears the Technical Record flags for the <see cref="pk"/>.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void ClearRecordFlags(this PKM pk) => pk.SetRecordFlags(false, 112);
public static void ClearRecordFlags(this ITechRecord8 pk) => pk.SetRecordFlags(false, 112);
/// <summary>
/// Sets the Technical Record flags for the <see cref="pk"/> based on the current moves.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
/// <param name="moves">Moves to set flags for. If a move is not a Technical Record, it is skipped.</param>
public static void SetRecordFlags(this PKM pk, IEnumerable<int> moves)
public static void SetRecordFlags(this ITechRecord8 pk, IEnumerable<int> moves)
{
if (pk is not PK8 pk8)
if (pk is PA8)
return;
var permit = pk8.PersonalInfo.TMHM.AsSpan(PersonalInfoSWSH.CountTM);
var moveIDs = Legal.TMHM_SWSH.AsSpan(PersonalInfoSWSH.CountTM);
var permit = pk.TechRecordPermitFlags;
var moveIDs = pk.TechRecordPermitIndexes;
foreach (var m in moves)
{
var index = moveIDs.IndexOf(m);
if (index == -1)
continue;
if (permit[index])
pk8.SetMoveRecordFlag(index, true);
pk.SetMoveRecordFlag(index, true);
}
}
@@ -53,15 +52,13 @@ public static void SetRecordFlags(this PKM pk, IEnumerable<int> moves)
/// Sets all the Technical Record flags for the <see cref="pk"/> if they are permitted to be learned in-game.
/// </summary>
/// <param name="pk">Pokémon to modify.</param>
public static void SetRecordFlags(this PKM pk)
public static void SetRecordFlags(this ITechRecord8 pk)
{
if (pk is not PK8 pk8)
return;
var permit = pk8.PersonalInfo.TMHM.AsSpan(PersonalInfoSWSH.CountTM); // tm[100], tr[100]
var permit = pk.TechRecordPermitFlags;
for (int i = 0; i < permit.Length; i++)
{
if (permit[i])
pk8.SetMoveRecordFlag(i, true);
pk.SetMoveRecordFlag(i, true);
}
}
}

View File

@@ -18,7 +18,7 @@ public static class BatchEditing
{
public static readonly Type[] Types =
{
typeof (PK8), typeof (PB8),
typeof (PK8), typeof (PA8), typeof (PB8),
typeof (PB7),
typeof (PK7), typeof (PK6), typeof (PK5), typeof (PK4), typeof(BK4),
typeof (PK3), typeof (XK3), typeof (CK3),

View File

@@ -14,13 +14,13 @@ internal static class BatchModifications
public static ModifyResult SetSuggestedRelearnData(BatchInfo info, string propValue)
{
var pk = info.Entity;
if (pk.Format >= 8)
if (pk is ITechRecord8 t)
{
pk.ClearRecordFlags();
t.ClearRecordFlags();
if (IsAll(propValue))
pk.SetRecordFlags(); // all
t.SetRecordFlags(); // all
else if (!IsNone(propValue))
pk.SetRecordFlags(pk.Moves); // whatever fit the current moves
t.SetRecordFlags(pk.Moves); // whatever fit the current moves
}
pk.SetRelearnMoves(info.SuggestedRelearn);

View File

@@ -248,14 +248,19 @@ public static void ApplySetDetails(this PKM pk, IBattleTemplate Set)
b.ResetCalculatedValues();
}
}
if (pk is IGanbaru g)
g.SetSuggestedGanbaruValues(pk);
if (pk is IGigantamax c)
c.CanGigantamax = Set.CanGigantamax;
if (pk is IDynamaxLevel d)
d.DynamaxLevel = d.CanHaveDynamaxLevel(pk) ? (byte)10 : (byte)0;
pk.ClearRecordFlags();
pk.SetRecordFlags(Set.Moves);
if (pk is ITechRecord8 t)
{
t.ClearRecordFlags();
t.SetRecordFlags(Set.Moves);
}
if (ShowdownSetBehaviorNature && pk.Format >= 8)
pk.Nature = pk.StatNature;

View File

@@ -54,23 +54,25 @@ protected BoxManipBase(BoxManipType type, Func<SaveFile, bool> usable)
public static readonly IReadOnlyList<BoxManipBase> ClearCommon = new List<BoxManipBase>
{
new BoxManipClear(BoxManipType.DeleteAll, _ => true),
new BoxManipClear(BoxManipType.DeleteEggs, pk => pk.IsEgg, s => s.Generation >= 2),
new BoxManipClear(BoxManipType.DeleteEggs, pk => pk.IsEgg, s => s.Generation >= 2 & s is not SAV8LA),
new BoxManipClearComplex(BoxManipType.DeletePastGen, (pk, sav) => pk.Generation != sav.Generation, s => s.Generation >= 4),
new BoxManipClearComplex(BoxManipType.DeleteForeign, (pk, sav) => !sav.IsOriginalHandler(pk, pk.Format > 2)),
new BoxManipClear(BoxManipType.DeleteUntrained, pk => pk.EVTotal == 0),
new BoxManipClear(BoxManipType.DeleteItemless, pk => pk.HeldItem == 0),
new BoxManipClear(BoxManipType.DeleteUntrained, pk => pk.EVTotal == 0, s => s is not SAV8LA),
new BoxManipClear(BoxManipType.DeleteUntrained, pk => !((PA8)pk).IsGanbaruValuesMax(pk), s => s is SAV8LA),
new BoxManipClear(BoxManipType.DeleteItemless, pk => pk.HeldItem == 0, s => s is not SAV8LA),
new BoxManipClear(BoxManipType.DeleteIllegal, pk => !new LegalityAnalysis(pk).Valid),
new BoxManipClearDuplicate<string>(BoxManipType.DeleteClones, pk => SearchUtil.GetCloneDetectMethod(CloneDetectionMethod.HashDetails)(pk)),
};
public static readonly IReadOnlyList<BoxManipModify> ModifyCommon = new List<BoxManipModify>
{
new(BoxManipType.ModifyHatchEggs, pk => pk.ForceHatchPKM(), s => s.Generation >= 2),
new(BoxManipType.ModifyHatchEggs, pk => pk.ForceHatchPKM(), s => s.Generation >= 2 & s is not SAV8LA),
new(BoxManipType.ModifyMaxFriendship, pk => pk.MaximizeFriendship()),
new(BoxManipType.ModifyMaxLevel, pk => pk.MaximizeLevel()),
new(BoxManipType.ModifyResetMoves, pk => pk.SetMoves(pk.GetMoveSet()), s => s.Generation >= 3),
new(BoxManipType.ModifyRandomMoves, pk => pk.SetMoves(pk.GetMoveSet(true))),
new(BoxManipType.ModifyHyperTrain,pk => pk.SetSuggestedHyperTrainingData(), s => s.Generation >= 7),
new(BoxManipType.ModifyHyperTrain,pk => pk.SetSuggestedHyperTrainingData(), s => s.Generation >= 7 && s is not SAV8LA),
new(BoxManipType.ModifyGanbaru,pk => ((IGanbaru)pk).SetSuggestedGanbaruValues(pk), s => s is SAV8LA),
new(BoxManipType.ModifyRemoveNicknames, pk => pk.SetDefaultNickname()),
new(BoxManipType.ModifyRemoveItem, pk => pk.HeldItem = 0, s => s.Generation >= 2),
new(BoxManipType.ModifyHeal, pk => pk.Heal(), s => s.Generation >= 8 || s is SAV7b),

View File

@@ -42,6 +42,7 @@ public enum BoxManipType
ModifyResetMoves,
ModifyRandomMoves,
ModifyHyperTrain,
ModifyGanbaru,
ModifyRemoveNicknames,
ModifyRemoveItem,
ModifyHeal,

View File

@@ -55,6 +55,7 @@ public static List<SlotInfoMisc> GetExtraSlots(this SaveFile sav, bool all = fal
SAV7b lgpe => GetExtraSlots7b(lgpe),
SAV8SWSH ss => GetExtraSlots8(ss),
SAV8BS bs => GetExtraSlots8b(bs),
SAV8LA la => GetExtraSlots8a(la),
_ => None,
};
@@ -218,5 +219,10 @@ private static List<SlotInfoMisc> GetExtraSlots8b(SAV8BS sav)
new(sav.Data, 8, sav.UgSaveData.GetSlotOffset(8), true) { Type = StorageSlotType.Misc },
};
}
private static List<SlotInfoMisc> GetExtraSlots8a(SAV8LA sav)
{
return new List<SlotInfoMisc>();
}
}
}

View File

@@ -38,6 +38,19 @@ public enum Ball : byte
Sport = 24,
Dream = 25,
Beast = 26,
// Legends: Arceus
Strange = 27,
LAPoke = 28,
LAGreat = 29,
LAUltra = 30,
LAFeather = 31,
LAWing = 32,
LAJet = 33,
LAHeavy = 34,
LALeaden = 35,
LAGigaton = 36,
LAOrigin = 37,
}
public static class BallExtensions

View File

@@ -204,7 +204,7 @@ public enum GameVersion
SH = 45,
// HOME = 46,
// PLA = 47,
PLA = 47,
/// <summary>
/// Pokémon Brilliant Diamond (NX)
@@ -459,6 +459,7 @@ public enum GameVersion
/// </summary>
/// <see cref="SWSH"/>
/// <see cref="BDSP"/>
/// <see cref="PLA"/>
Gen8,
/// <summary>

View File

@@ -832,6 +832,30 @@ public enum Move
GlacialLance,
AstralBarrage,
EerieSpell,
DireClaw,
PsyshieldBash,
PowerShift,
StoneAxe,
SpringtideStorm,
MysticalPower,
RagingFury,
WaveCrash,
Chloroblast,
MountainGale,
VictoryDance,
HeadlongRush,
BarbBarrage,
EsperWing,
BitterMalice,
Shelter,
TripleArrows,
InfernalParade,
CeaselessEdge,
BleakwindStorm,
WildboltStorm,
SandsearStorm,
LunarBlessing,
TakeHeart,
MAX_COUNT,
}
}

View File

@@ -904,6 +904,13 @@ public enum Species : ushort
Glastrier,
Spectrier,
Calyrex,
Wyrdeer,
Kleavor,
Ursaluna,
Basculegion,
Sneasler,
Overqwil,
Enamorus,
MAX_COUNT,
}
}

View File

@@ -74,8 +74,8 @@ public GameDataSource(GameStrings s)
private static IReadOnlyList<ComboItem> GetBalls(string[] itemList)
{
// ignores Poke/Great/Ultra
ReadOnlySpan<ushort> ball_nums = stackalloc ushort[] { 007, 576, 013, 492, 497, 014, 495, 493, 496, 494, 011, 498, 008, 006, 012, 015, 009, 005, 499, 010, 001, 016, 851 };
ReadOnlySpan<byte> ball_vals = stackalloc byte[] { 007, 025, 013, 017, 022, 014, 020, 018, 021, 019, 011, 023, 008, 006, 012, 015, 009, 005, 024, 010, 001, 016, 026 };
ReadOnlySpan<ushort> ball_nums = stackalloc ushort[] { 007, 576, 013, 492, 497, 014, 495, 493, 496, 494, 011, 498, 008, 006, 012, 015, 009, 005, 499, 010, 001, 016, 851, 1785, 1710, 1711, 1712, 1713, 1746, 1747, 1748, 1749, 1750, 1771 };
ReadOnlySpan<byte> ball_vals = stackalloc byte[] { 007, 025, 013, 017, 022, 014, 020, 018, 021, 019, 011, 023, 008, 006, 012, 015, 009, 005, 024, 010, 001, 016, 026, 0027, 0028, 0029, 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037 };
return Util.GetVariedCBListBall(itemList, ball_nums, ball_vals);
}
@@ -84,6 +84,7 @@ private static IReadOnlyList<ComboItem> GetVersionList(GameStrings s)
var list = s.gamelist;
ReadOnlySpan<byte> games = stackalloc byte[]
{
47, // 8 legends arceus
48, 49, // 8 bdsp
44, 45, // 8 swsh
42, 43, // 7 gg

View File

@@ -25,6 +25,7 @@ public sealed class GameStrings : IBasicStrings
public readonly string[] metGG_00000, metGG_30000, metGG_40000, metGG_60000;
public readonly string[] metSWSH_00000, metSWSH_30000, metSWSH_40000, metSWSH_60000;
public readonly string[] metBDSP_00000, metBDSP_30000, metBDSP_40000, metBDSP_60000;
public readonly string[] metLA_00000, metLA_30000, metLA_40000, metLA_60000;
// Misc
public readonly string[] wallpapernames, puffs, walkercourses;
@@ -48,9 +49,11 @@ public sealed class GameStrings : IBasicStrings
/// </summary>
private static readonly ushort[] Items_Ball =
{
000, 001, 002, 003, 004, 005, 006, 007, 008, 009, 010, 011, 012,
013, 014, 015, 016, 492, 493, 494, 495, 496, 497, 498, 499, 576,
851,
0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007, 0008, 0009,
0010, 0011, 0012, 0013, 0014, 0015, 0016, 0492, 0493, 0494,
0495, 0496, 0497, 0498, 0499, 0576, 0851,
1785, 1710, 1711,
1712, 1713, 1746, 1747, 1748, 1749, 1750, 1771,
};
public GameStrings(string l)
@@ -138,6 +141,11 @@ public GameStrings(string l)
metSWSH_40000 = Get("swsh_40000");
metSWSH_60000 = Get("swsh_60000");
metLA_00000 = Get("la_00000");
metLA_30000 = Get("la_30000");
metLA_40000 = Get("la_40000");
metLA_60000 = Get("la_60000");
metBDSP_00000 = Get("bdsp_00000");
metBDSP_30000 = Get("bdsp_30000");
metBDSP_40000 = Get("bdsp_40000");
@@ -250,6 +258,86 @@ private void SanitizeItemNames()
g3coloitems[500 + i] += $" ({i - 11:00})";
// differentiate G3 Card Key from Colo
g3coloitems[500 + 10] += " (COLO)";
SanitizeItemsLA(itemlist);
if (lang is "fr")
{
itemlist[1681] += " (LA)"; // Galet Noir dup with 617 (Dark Stone | Black Tumblestone)
}
else if (lang is "ja")
{
itemlist[1693] += " (LA)"; // むしよけスプレー dup with 79 (Repel)
itemlist[1716] += " (LA)"; // ビビリだま dup with 847 (Adrenaline Orb | Scatter Bang)
itemlist[1717] += " (LA)"; // けむりだま dup with 228 (Smoke Ball | Smoke Bomb)
}
itemlist[464] += " (G4)"; // Secret Medicine
itemlist[1763] += " (LA)"; // Secret Medicine
}
private static void SanitizeItemsLA(string[] items)
{
// Recipes
items[1784] += " (~)"; // Gigaton Ball
items[1783] += " (~)"; // Leaden Ball
items[1753] += " (~)"; // Heavy Ball
items[1752] += " (~)"; // Jet Ball
items[1751] += " (~)"; // Wing Ball
items[1731] += " (~)"; // Twice-Spiced Radish
items[1730] += " (~)"; // Choice Dumpling
items[1729] += " (~)"; // Swap Snack
items[1677] += " (~)"; // Aux Powerguard
items[1676] += " (~)"; // Aux Evasion
items[1675] += " (~)"; // Dire Hit
items[1674] += " (~)"; // Aux Guard
items[1673] += " (~)"; // Aux Power
items[1671] += " (~)"; // Stealth Spray
items[1670] += " (~)"; // Max Elixir
items[1669] += " (~)"; // Max Ether
items[1668] += " (~)"; // Max Revive
items[1667] += " (~)"; // Revive
items[1666] += " (~)"; // Full Heal
items[1665] += " (~)"; // Jubilife Muffin
items[1664] += " (~)"; // Old Gateau
items[1663] += " (~)"; // Superb Remedy
items[1662] += " (~)"; // Fine Remedy
items[1661] += " (~)"; // Remedy
items[1660] += " (~)"; // Full Restore
items[1659] += " (~)"; // Max Potion
items[1658] += " (~)"; // Hyper Potion
items[1657] += " (~)"; // Super Potion
items[1656] += " (~)"; // Potion
items[1655] += " (~)"; // Salt Cake
items[1654] += " (~)"; // Bean Cake
items[1653] += " (~)"; // Grain Cake
items[1652] += " (~)"; // Honey Cake
items[1650] += " (~)"; // Mushroom Cake
items[1649] += " (~)"; // Star Piece
items[1648] += " (~)"; // Sticky Glob
items[1647] += " (~)"; // Scatter Bang
items[1646] += " (~)"; // Smoke Bomb
items[1644] += " (~)"; // Pokéshi Doll
items[1643] += " (~)"; // Feather Ball
items[1642] += " (~)"; // Ultra Ball
items[1641] += " (~)"; // Great Ball
items[1640] += " (~)"; // Poké Ball
// Items
items[1616] += " (LA)"; // Dire Hit
items[1689] += " (LA)"; // Snowball
items[1710] += " (LA)"; // Poké Ball
items[1711] += " (LA)"; // Great Ball
items[1712] += " (LA)"; // Ultra Ball
items[1748] += " (LA)"; // Heavy Ball
// Key Items
items[1622] += " (-)"; // Poké Ball
items[1765] += " (1)"; // Lost Satchel
items[1766] += " (2)"; // Lost Satchel
items[1767] += " (3)"; // Lost Satchel
items[1768] += " (4)"; // Lost Satchel
items[1769] += " (5)"; // Lost Satchel
}
private void SanitizeMetLocations()
@@ -260,7 +348,9 @@ private void SanitizeMetLocations()
SanitizeMetG6XY();
SanitizeMetG7SM();
SanitizeMetG8SWSH();
SanitizeMetG8LA();
SanitizeMetG8BDSP();
SanitizeMetG8PLA();
if (lang is "es" or "it")
{
@@ -394,6 +484,12 @@ private void SanitizeMetG8SWSH()
// metSWSH_30000[18] += " (-)"; // Pokémon HOME -- duplicate with 40000's entry
}
private void SanitizeMetG8LA()
{
metBDSP_30000[1] += $" ({NPC})"; // Anything from an NPC
metBDSP_30000[2] += $" ({EggName})"; // Egg From Link Trade
}
private void SanitizeMetG8BDSP()
{
metBDSP_30000[1] += $" ({NPC})"; // Anything from an NPC
@@ -405,6 +501,48 @@ private void SanitizeMetG8BDSP()
Deduplicate(metBDSP_60000, 60000);
}
private void SanitizeMetG8PLA()
{
metLA_00000[31] += " (2)"; // in Floaro Gardens
metLA_30000[1] += $" ({NPC})"; // Anything from an NPC
metLA_30000[2] += $" ({EggName})"; // Egg From Link Trade
for (int i = 3; i <= 6; i++) // distinguish first set of regions (unused) from second (used)
metLA_30000[i] += " (-)";
metLA_30000[19] += " (?)"; // Kanto for the third time
metLA_40000[30] += " (-)"; // a Video game Event (in spanish etc) -- duplicate with line 39
metLA_40000[53] += " (-)"; // a Pokémon event -- duplicate with line 37
metLA_40000[81] += " (-)"; // Pokémon GO -- duplicate with 30000's entry
metLA_40000[86] += " (-)"; // Pokémon HOME -- duplicate with 30000's entry
// metLA_30000[12] += " (-)"; // Pokémon GO -- duplicate with 40000's entry
// metLA_30000[18] += " (-)"; // Pokémon HOME -- duplicate with 40000's entry
for (int i = 55; i <= 60; i++) // distinguish second set of YYYY Event from the first
metLA_40000[i] += " (-)";
if (lang is "es")
{
// en un lugar misterioso
metLA_00000[2] += " (2)"; // in a mystery zone
metLA_00000[4] += " (4)"; // in a faraway place
}
else if (lang is "ja")
{
// ひょうざんのいくさば
metLA_00000[099] += " (099)"; // along the Arenas Approach
metLA_00000[142] += " (142)"; // at Icepeak Arena
}
else if (lang is "fr" or "it")
{
// Final four locations are not nouns, rather the same location reference (at the...) as prior entries.
metLA_00000[152] += " (152)"; // Galaxy Hall
metLA_00000[153] += " (153)"; // Front Gate
metLA_00000[154] += " (154)"; // Farm
metLA_00000[155] += " (155)"; // Training Grounds
}
}
private static void Deduplicate(string[] arr, int group)
{
var counts = new Dictionary<string, int>();
@@ -557,7 +695,11 @@ public string GetLocationName(bool isEggLocation, int location, int format, int
5 => GetLocationNames5(bankID),
6 => GetLocationNames6(bankID),
7 => GameVersion.Gen7b.Contains(version) ? GetLocationNames7GG(bankID) : GetLocationNames7(bankID),
8 => GameVersion.BDSP.Contains(version) ? GetLocationNames8b(bankID) : GetLocationNames8(bankID),
8 when version is GameVersion.PLA => GetLocationNames8a(bankID),
8 when GameVersion.BDSP.Contains(version) => GetLocationNames8b(bankID),
8 => GetLocationNames8(bankID),
_ => Array.Empty<string>(),
};
@@ -614,6 +756,15 @@ public string GetLocationName(bool isEggLocation, int location, int format, int
_ => Array.Empty<string>(),
};
public IReadOnlyList<string> GetLocationNames8a(int bankID) => bankID switch
{
0 => metLA_00000,
3 => metLA_30000,
4 => metLA_40000,
6 => metLA_60000,
_ => Array.Empty<string>(),
};
public IReadOnlyList<string> GetLocationNames8b(int bankID) => bankID switch
{
0 => metBDSP_00000,

View File

@@ -18,6 +18,7 @@ public sealed class MetDataSource
private readonly List<ComboItem> MetGen7;
private readonly List<ComboItem> MetGen7GG;
private readonly List<ComboItem> MetGen8;
private readonly List<ComboItem> MetGen8a;
private readonly List<ComboItem> MetGen8b;
private IReadOnlyList<ComboItem>? MetGen4Transfer;
@@ -34,6 +35,7 @@ public MetDataSource(GameStrings s)
MetGen7 = CreateGen7(s);
MetGen7GG = CreateGen7GG(s);
MetGen8 = CreateGen8(s);
MetGen8a = CreateGen8a(s);
MetGen8b = CreateGen8b(s);
}
@@ -152,6 +154,17 @@ private static List<ComboItem> CreateGen8(GameStrings s)
return locations;
}
private static List<ComboItem> CreateGen8a(GameStrings s)
{
var locations = Util.GetCBList(s.metLA_00000, 0);
Util.AddCBWithOffset(locations, s.metLA_30000, 30000, Locations.LinkTrade6);
Util.AddCBWithOffset(locations, s.metLA_00000, 00000, Legal.Met_LA_0);
Util.AddCBWithOffset(locations, s.metLA_30000, 30000, Legal.Met_LA_3);
Util.AddCBWithOffset(locations, s.metLA_40000, 40000, Legal.Met_LA_4);
Util.AddCBWithOffset(locations, s.metLA_60000, 60000, Legal.Met_LA_6);
return locations;
}
private static List<ComboItem> CreateGen8b(GameStrings s)
{
// Manually add invalid (-1) location from SWSH as ID 65535
@@ -201,6 +214,7 @@ public IReadOnlyList<ComboItem> GetLocationList(GameVersion version, int current
GP or GE or GO => Partition2(MetGen7GG, z => z <= 54), // Pokémon League
SW or SH => Partition2(MetGen8, z => z < 400),
BD or SP => Partition2(MetGen8b, z => z < 628),
PLA => Partition2(MetGen8a, z => z < 512),
_ => GetLocationListModified(version, currentGen),
};

View File

@@ -76,6 +76,7 @@ private static GameVersion[] GetValidGameVersions()
// Gen8
SW or SH => SWSH,
BD or SP => BDSP,
PLA => PLA,
_ => Invalid,
};
@@ -138,6 +139,7 @@ public static int GetMaxSpeciesID(this GameVersion game)
return Legal.MaxSpeciesID_7_USUM;
return Legal.MaxSpeciesID_7_USUM;
}
if (PLA == game) return Legal.MaxSpeciesID_8a;
if (BDSP.Contains(game)) return Legal.MaxSpeciesID_8b;
if (Gen8.Contains(game)) return Legal.MaxSpeciesID_8;
return -1;
@@ -200,7 +202,7 @@ public static bool Contains(this GameVersion g1, GameVersion g2)
SWSH => g2 is SW or SH,
BDSP => g2 is BD or SP,
Gen8 => SWSH.Contains(g2) || BDSP.Contains(g2),
Gen8 => SWSH.Contains(g2) || BDSP.Contains(g2) || PLA == g2,
_ => false,
};
}

View File

@@ -14,10 +14,10 @@ public sealed record EncounterArea1 : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea1[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea1[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea1[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea1(input[i], game);
return result;
}

View File

@@ -20,10 +20,10 @@ public sealed record EncounterArea2 : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea2[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea2[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea2[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea2(input[i], game);
return result;
}

View File

@@ -15,18 +15,18 @@ public sealed record EncounterArea3 : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea3[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea3[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea3[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea3(input[i], game);
return result;
}
public static EncounterArea3[] GetAreasSwarm(byte[][] input, GameVersion game)
public static EncounterArea3[] GetAreasSwarm(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea3[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea3(input[i], game, SlotType.Swarm | SlotType.Grass);
return result;
}

View File

@@ -16,10 +16,10 @@ public sealed record EncounterArea4 : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea4[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea4[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea4[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea4(input[i], game);
return result;
}

View File

@@ -14,10 +14,10 @@ public sealed record EncounterArea5 : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea5[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea5[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea5[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea5(input[i], game);
return result;
}

View File

@@ -14,10 +14,10 @@ public sealed record EncounterArea6AO : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea6AO[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea6AO[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea6AO[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea6AO(input[i], game);
return result;
}

View File

@@ -14,10 +14,11 @@ public sealed record EncounterArea6XY : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea6XY[] GetAreas(byte[][] input, GameVersion game, EncounterArea6XY safari)
public static EncounterArea6XY[] GetAreas(BinLinkerAccessor input, GameVersion game, EncounterArea6XY safari)
{
var result = new EncounterArea6XY[input.Length + 1];
for (int i = 0; i < input.Length; i++)
int count = input.Length;
var result = new EncounterArea6XY[count + 1];
for (int i = 0; i < count; i++)
result[i] = new EncounterArea6XY(input[i], game);
result[^1] = safari;
return result;

View File

@@ -14,10 +14,10 @@ public sealed record EncounterArea7 : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea7[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea7[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea7[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea7(input[i], game);
return result;
}

View File

@@ -13,10 +13,10 @@ public sealed record EncounterArea7b : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea7b[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea7b[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea7b[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea7b(input[i], game);
return result;
}

View File

@@ -28,7 +28,7 @@ private EncounterArea7g(int species, int form, EncounterSlot7GO[] slots) : base(
Slots = slots;
}
internal static EncounterArea7g[] GetArea(byte[][] data)
internal static EncounterArea7g[] GetArea(BinLinkerAccessor data)
{
var areas = new EncounterArea7g[data.Length];
for (int i = 0; i < areas.Length; i++)

View File

@@ -355,15 +355,15 @@ private static bool CanCrossoverTo(int fromLocation, int toLocation, AreaSlotTyp
_ => false,
};
public static EncounterArea8[] GetAreas(byte[][] input, GameVersion game, bool symbol = false)
public static EncounterArea8[] GetAreas(BinLinkerAccessor input, GameVersion game, bool symbol = false)
{
var result = new EncounterArea8[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea8(input[i], symbol, game);
return result;
}
private EncounterArea8(byte[] areaData, bool symbol, GameVersion game) : base(game)
private EncounterArea8(ReadOnlySpan<byte> areaData, bool symbol, GameVersion game) : base(game)
{
PermitCrossover = symbol;
Location = areaData[0];

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <inheritdoc cref="EncounterArea" />
/// <summary>
/// <see cref="GameVersion.SWSH"/> encounter area
/// </summary>
public sealed record EncounterArea8a : EncounterArea
{
public readonly EncounterSlot8a[] Slots;
public readonly int ParentLocation;
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public override bool IsMatchLocation(int location)
{
if (base.IsMatchLocation(location))
return true;
return CanCrossoverTo(location);
}
private bool CanCrossoverTo(int location)
{
return location == ParentLocation;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain) => GetMatches(chain, pkm.Met_Level);
private IEnumerable<EncounterSlot8a> GetMatches(IReadOnlyList<EvoCriteria> chain, int metLevel)
{
foreach (var slot in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (!slot.IsLevelWithinRange(metLevel))
break;
if (slot.Form != evo.Form && slot.Species is not ((int)Species.Rotom or (int)Species.Burmy or (int)Species.Wormadam))
break;
yield return slot;
break;
}
}
}
public static EncounterArea8a[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea8a[input.Length];
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea8a(input[i], game);
return result;
}
private EncounterArea8a(ReadOnlySpan<byte> areaData, GameVersion game) : base(game)
{
// Area Metadata
Location = areaData[0];
ParentLocation = areaData[1];
Type = areaData[2] + SlotType.Overworld;
var count = areaData[3];
var slots = areaData[4..];
Slots = ReadSlots(slots, count);
}
private EncounterSlot8a[] ReadSlots(ReadOnlySpan<byte> areaData, byte slotCount)
{
var slots = new EncounterSlot8a[slotCount];
const int bpe = 8;
for (int i = 0; i < slotCount; i++)
{
var ofs = i * bpe;
var entry = areaData.Slice(ofs, bpe);
byte flawless = entry[7];
var gender = (Gender)entry[6];
int max = entry[5];
int min = entry[4];
var alpha = entry[3];
var form = entry[2];
var species = ReadUInt16LittleEndian(entry);
slots[i] = new EncounterSlot8a(this, species, form, min, max, alpha, flawless, gender);
}
return slots;
}
}

View File

@@ -14,10 +14,10 @@ public sealed record EncounterArea8b : EncounterArea
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea8b[] GetAreas(byte[][] input, GameVersion game)
public static EncounterArea8b[] GetAreas(BinLinkerAccessor input, GameVersion game)
{
var result = new EncounterArea8b[input.Length];
for (int i = 0; i < input.Length; i++)
for (int i = 0; i < result.Length; i++)
result[i] = new EncounterArea8b(input[i], game);
return result;
}
@@ -123,27 +123,27 @@ private static bool IsInaccessibleHoneySlotLocation(EncounterSlot8b slot, PKM pk
private static readonly ushort[] LocationID_HoneyTree =
{
359, // 00 Route 205 Floaroma
361, // 01 Route 205 Eterna
362, // 02 Route 206
364, // 03 Route 207
365, // 04 Route 208
367, // 05 Route 209
373, // 06 Route 210 Solaceon
375, // 07 Route 210 Celestic
378, // 08 Route 211
379, // 09 Route 212 Hearthome
383, // 10 Route 212 Pastoria
385, // 11 Route 213
392, // 12 Route 214
394, // 13 Route 215
400, // 14 Route 218
404, // 15 Route 221
407, // 16 Route 222
197, // 17 Valley Windworks
199, // 18 Eterna Forest
201, // 19 Fuego Ironworks
253, // 20 Floaroma Meadow
359, // 00 Route 205 Floaroma
361, // 01 Route 205 Eterna
362, // 02 Route 206
364, // 03 Route 207
365, // 04 Route 208
367, // 05 Route 209
373, // 06 Route 210 Solaceon
375, // 07 Route 210 Celestic
378, // 08 Route 211
379, // 09 Route 212 Hearthome
383, // 10 Route 212 Pastoria
385, // 11 Route 213
392, // 12 Route 214
394, // 13 Route 215
400, // 14 Route 218
404, // 15 Route 221
407, // 16 Route 222
197, // 17 Valley Windworks
199, // 18 Eterna Forest
201, // 19 Fuego Ironworks
253, // 20 Floaroma Meadow
};
}
}

View File

@@ -28,7 +28,7 @@ private EncounterArea8g(int species, int form, EncounterSlot8GO[] slots) : base(
Slots = slots;
}
internal static EncounterArea8g[] GetArea(byte[][] data)
internal static EncounterArea8g[] GetArea(BinLinkerAccessor data)
{
var areas = new EncounterArea8g[data.Length];
for (int i = 0; i < areas.Length; i++)

View File

@@ -1,35 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace PKHeX.Core
{
public static class BinLinker
{
/// <summary>
/// Unpacks a BinLinkerAccessor generated file container into individual arrays.
/// </summary>
/// <param name="fileData">Packed data</param>
/// <param name="identifier">Signature expected in the first two bytes (ASCII)</param>
/// <returns>Unpacked array containing all files that were packed.</returns>
public static byte[][] Unpack(ReadOnlySpan<byte> fileData, string identifier)
{
#if DEBUG
System.Diagnostics.Debug.Assert(fileData.Length > 4);
System.Diagnostics.Debug.Assert(identifier[0] == fileData[0] && identifier[1] == fileData[1]);
#endif
MemoryMarshal.TryRead(fileData[4..], out int start);
MemoryMarshal.TryRead(fileData[2..], out ushort count);
var offsetBytes = fileData[8..(8 + (count * sizeof(int)))];
var offsets = MemoryMarshal.Cast<byte, int>(offsetBytes);
byte[][] returnData = new byte[count][];
for (int i = 0; i < offsets.Length; i++)
{
int end = offsets[i];
returnData[i] = fileData[start..end].ToArray();
start = end;
}
return returnData;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core;
/// <summary>
/// Unpacks a BinLinkerAccessor generated file container into individual arrays.
/// </summary>
public readonly ref struct BinLinkerAccessor
{
/// <summary> Backing data object </summary>
private readonly ReadOnlySpan<byte> Data;
/// <summary> Total count of files available for accessing. </summary>
public int Length => ReadUInt16LittleEndian(Data[2..]);
/// <summary> Magic identifier for the file. </summary>
public string Identifier => new(new[] {(char)Data[0], (char)Data[1]});
/// <summary>
/// Retrieves a view of the entry at the requested <see cref="index"/>.
/// </summary>
/// <param name="index">Entry to retrieve.</param>
public ReadOnlySpan<byte> this[int index] => GetEntry(index);
private BinLinkerAccessor(ReadOnlySpan<byte> data) => Data = data;
private ReadOnlySpan<byte> GetEntry(int index)
{
int offset = 4 + (index * sizeof(int));
int end = ReadInt32LittleEndian(Data[(offset + 4)..]);
int start = ReadInt32LittleEndian(Data[offset..]);
return Data[start..end];
}
/// <summary>
/// Sanity checks the input <see cref="data"/> only in DEBUG builds, and returns a new wrapper.
/// </summary>
/// <param name="data">Data reference</param>
/// <param name="identifier">Expected identifier (debug verification only)</param>
public static BinLinkerAccessor Get(ReadOnlySpan<byte> data, string identifier)
{
var result = new BinLinkerAccessor(data);
#if DEBUG
System.Diagnostics.Debug.Assert(data.Length > 4);
System.Diagnostics.Debug.Assert(identifier[0] == data[0] && identifier[1] == data[1]);
#endif
return result;
}
}

View File

@@ -111,6 +111,9 @@ public static bool CanHatchAsEgg(int species, int form, int generation)
if (FormInfo.IsTotemForm(species, form, generation))
return false;
if (FormInfo.IsLordForm(species, form, generation))
return false;
return IsBreedableForm(species, form);
}
@@ -199,6 +202,7 @@ public static bool CanHatchAsEgg(int species, int form, GameVersion game)
(int)Kubfu, (int)Urshifu, (int)Zarude,
(int)Regieleki, (int)Regidrago,
(int)Glastrier, (int)Spectrier, (int)Calyrex,
(int)Enamorus,
};
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.BinLinkerAccessor;
namespace PKHeX.Core
{
@@ -16,50 +17,50 @@ public static partial class Legal
internal static readonly Learnset[] LevelUpC = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_c.pkl"), MaxSpeciesID_2);
// Gen 3
internal static readonly Learnset[] LevelUpE = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_e.pkl"), "em"));
internal static readonly Learnset[] LevelUpRS = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_rs.pkl"), "rs"));
internal static readonly Learnset[] LevelUpFR = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_fr.pkl"), "fr"));
internal static readonly Learnset[] LevelUpLG = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_lg.pkl"), "lg"));
internal static readonly EggMoves6[] EggMovesRS = EggMoves6.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_rs.pkl"), "rs"));
internal static readonly Learnset[] LevelUpE = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_e.pkl"), "em"));
internal static readonly Learnset[] LevelUpRS = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_rs.pkl"), "rs"));
internal static readonly Learnset[] LevelUpFR = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_fr.pkl"), "fr"));
internal static readonly Learnset[] LevelUpLG = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_lg.pkl"), "lg"));
internal static readonly EggMoves6[] EggMovesRS = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_rs.pkl"), "rs"));
// Gen 4
internal static readonly Learnset[] LevelUpDP = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_dp.pkl"), "dp"));
internal static readonly Learnset[] LevelUpPt = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_pt.pkl"), "pt"));
internal static readonly Learnset[] LevelUpHGSS = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_hgss.pkl"), "hs"));
internal static readonly EggMoves6[] EggMovesDPPt = EggMoves6.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_dppt.pkl"), "dp"));
internal static readonly EggMoves6[] EggMovesHGSS = EggMoves6.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_hgss.pkl"), "hs"));
internal static readonly Learnset[] LevelUpDP = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_dp.pkl"), "dp"));
internal static readonly Learnset[] LevelUpPt = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_pt.pkl"), "pt"));
internal static readonly Learnset[] LevelUpHGSS = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_hgss.pkl"), "hs"));
internal static readonly EggMoves6[] EggMovesDPPt = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_dppt.pkl"), "dp"));
internal static readonly EggMoves6[] EggMovesHGSS = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_hgss.pkl"), "hs"));
// Gen 5
internal static readonly Learnset[] LevelUpBW = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_bw.pkl"), "51"));
internal static readonly Learnset[] LevelUpB2W2 = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_b2w2.pkl"), "52"));
internal static readonly EggMoves6[] EggMovesBW = EggMoves6.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_bw.pkl"), "bw"));
internal static readonly Learnset[] LevelUpBW = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_bw.pkl"), "51"));
internal static readonly Learnset[] LevelUpB2W2 = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_b2w2.pkl"), "52"));
internal static readonly EggMoves6[] EggMovesBW = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_bw.pkl"), "bw"));
// Gen 6
internal static readonly EggMoves6[] EggMovesXY = EggMoves6.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_xy.pkl"), "xy"));
internal static readonly Learnset[] LevelUpXY = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_xy.pkl"), "xy"));
internal static readonly EggMoves6[] EggMovesAO = EggMoves6.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_ao.pkl"), "ao"));
internal static readonly Learnset[] LevelUpAO = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_ao.pkl"), "ao"));
internal static readonly EggMoves6[] EggMovesXY = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_xy.pkl"), "xy"));
internal static readonly Learnset[] LevelUpXY = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_xy.pkl"), "xy"));
internal static readonly EggMoves6[] EggMovesAO = EggMoves6.GetArray(Get(Util.GetBinaryResource("eggmove_ao.pkl"), "ao"));
internal static readonly Learnset[] LevelUpAO = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_ao.pkl"), "ao"));
// Gen 7
internal static readonly EggMoves7[] EggMovesSM = EggMoves7.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
internal static readonly Learnset[] LevelUpSM = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
internal static readonly EggMoves7[] EggMovesUSUM = EggMoves7.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpUSUM = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpGG = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_gg.pkl"), "gg"));
internal static readonly EggMoves7[] EggMovesSM = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
internal static readonly Learnset[] LevelUpSM = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
internal static readonly EggMoves7[] EggMovesUSUM = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpUSUM = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpGG = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_gg.pkl"), "gg"));
// Gen 8
internal static readonly EggMoves7[] EggMovesSWSH = EggMoves7.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_swsh.pkl"), "ss"));
internal static readonly Learnset[] LevelUpSWSH = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_swsh.pkl"), "ss"));
internal static readonly EggMoves7[] EggMovesBDSP = EggMoves7.GetArray(BinLinker.Unpack(Util.GetBinaryResource("eggmove_bdsp.pkl"), "bs"));
internal static readonly Learnset[] LevelUpBDSP = LearnsetReader.GetArray(BinLinker.Unpack(Util.GetBinaryResource("lvlmove_bdsp.pkl"), "bs"));
internal static readonly EggMoves7[] EggMovesSWSH = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_swsh.pkl"), "ss"));
internal static readonly Learnset[] LevelUpSWSH = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_swsh.pkl"), "ss"));
internal static readonly EggMoves7[] EggMovesBDSP = EggMoves7.GetArray(Get(Util.GetBinaryResource("eggmove_bdsp.pkl"), "bs"));
internal static readonly Learnset[] LevelUpBDSP = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_bdsp.pkl"), "bs"));
internal static readonly Learnset[] LevelUpLA = LearnsetReader.GetArray(Get(Util.GetBinaryResource("lvlmove_la.pkl"), "la"));
public static IReadOnlyList<byte> GetPPTable(PKM pkm, int format)
public static IReadOnlyList<byte> GetPPTable(PKM pkm, int format) => format switch
{
if (format != 7)
return GetPPTable(format);
var lgpe = pkm.Version is (int) GameVersion.GO or (int) GameVersion.GP or (int) GameVersion.GE;
return lgpe ? MovePP_GG : MovePP_SM;
}
7 when pkm is PB7 => MovePP_GG,
8 when pkm is PA8 => MovePP_LA,
_ => GetPPTable(format),
};
public static IReadOnlyList<byte> GetPPTable(int format) => format switch
{
@@ -78,6 +79,7 @@ public static IReadOnlyList<byte> GetPPTable(PKM pkm, int format)
{
PK8 => DummiedMoves_SWSH,
PB8 => DummiedMoves_BDSP,
PA8 => DummiedMoves_LA,
_ => Array.Empty<int>(),
};
@@ -99,7 +101,7 @@ internal static int GetMaxSpeciesOrigin(PKM pkm)
5 => MaxSpeciesID_5,
6 => MaxSpeciesID_6,
7 => MaxSpeciesID_7b,
8 => MaxSpeciesID_8,
8 => MaxSpeciesID_8a,
_ => -1,
};
@@ -112,7 +114,7 @@ internal static int GetMaxSpeciesOrigin(PKM pkm)
<= MaxSpeciesID_5 => 5,
<= MaxSpeciesID_6 => 6,
<= MaxSpeciesID_7b => 7,
<= MaxSpeciesID_8 => 8,
<= MaxSpeciesID_8a => 8,
_ => -1,
};
@@ -138,7 +140,7 @@ internal static int GetMaxSpeciesOrigin(PKM pkm)
5 => MaxMoveID_5,
6 => MaxMoveID_6_AO,
7 => MaxMoveID_7b,
8 => MaxMoveID_8,
8 => MaxMoveID_8a,
_ => -1,
};
@@ -161,6 +163,18 @@ internal static bool HasVisitedBDSP(this PKM pkm, int species)
return pi.IsPresentInGame;
}
internal static bool HasVisitedLA(this PKM pkm, int species)
{
if (!pkm.InhabitedGeneration(8, species))
return false;
if (pkm.LA)
return true;
if (pkm.IsUntraded)
return false;
var pi = (PersonalInfoLA)PersonalTable.LA[species];
return pi.IsPresentInGame;
}
/// <summary>
/// Indicates if the moveset is restricted to only the original version.
/// </summary>
@@ -172,6 +186,8 @@ internal static bool IsMovesetRestricted(this PKM pkm)
return true;
if (pkm.BDSP)
return true;
if (pkm.LA)
return true;
return false;
}

View File

@@ -30,6 +30,9 @@ public static class EncounterEvent
/// <summary>Event Database for Generation 8</summary>
public static IReadOnlyList<WC8> MGDB_G8 { get; private set; } = Array.Empty<WC8>();
/// <summary>Event Database for Generation 8 <see cref="GameVersion.BDSP"/></summary>
public static IReadOnlyList<WA8> MGDB_G8A { get; private set; } = Array.Empty<WA8>();
/// <summary>Event Database for Generation 8 <see cref="GameVersion.BDSP"/></summary>
public static IReadOnlyList<WB8> MGDB_G8B { get; private set; } = Array.Empty<WB8>();
@@ -45,6 +48,7 @@ public static class EncounterEvent
private static WB7[] GetWB7DB(ReadOnlySpan<byte> bin) => Get(bin, WB7.SizeFull, d => new WB7(d));
private static WC8[] GetWC8DB(ReadOnlySpan<byte> bin) => Get(bin, WC8.Size, d => new WC8(d));
private static WB8[] GetWB8DB(ReadOnlySpan<byte> bin) => Get(bin, WB8.Size, d => new WB8(d));
private static WA8[] GetWA8DB(ReadOnlySpan<byte> bin) => Get(bin, WA8.Size, d => new WA8(d));
private static T[] Get<T>(ReadOnlySpan<byte> bin, int size, Func<byte[], T> ctor)
{
@@ -68,6 +72,7 @@ public static void RefreshMGDB(params string[] paths)
ICollection<WB7> b7 = GetWB7DB(Util.GetBinaryResource("wb7full.pkl"));
ICollection<WC8> g8 = GetWC8DB(Util.GetBinaryResource("wc8.pkl"));
ICollection<WB8> b8 = GetWB8DB(Util.GetBinaryResource("wb8.pkl"));
ICollection<WA8> a8 = GetWA8DB(Util.GetBinaryResource("wa8.pkl"));
foreach (var gift in paths.Where(Directory.Exists).SelectMany(MysteryUtil.GetGiftsFromFolder))
{
@@ -87,6 +92,7 @@ static void AddOrExpand<T>(ref ICollection<T> arr, T obj)
case WB7 wb7: AddOrExpand(ref b7, wb7); continue;
case WC8 wc8: AddOrExpand(ref g8, wc8); continue;
case WB8 wb8: AddOrExpand(ref b8, wb8); continue;
case WA8 wa8: AddOrExpand(ref a8, wa8); continue;
}
}
@@ -108,6 +114,7 @@ static T[] SetArray<T>(ICollection<T> arr)
MGDB_G7 = SetArray(g7);
MGDB_G7GG = SetArray(b7);
MGDB_G8 = SetArray(g8);
MGDB_G8A = SetArray(a8);
MGDB_G8B = SetArray(b8);
}
@@ -121,6 +128,7 @@ public static IEnumerable<MysteryGift> GetAllEvents(bool sorted = true)
MGDB_G7,
MGDB_G7GG,
MGDB_G8,
MGDB_G8A,
MGDB_G8B,
}.SelectMany(z => z);
regular = regular.Where(mg => !mg.IsItem && mg.IsPokémon && mg.Species > 0);

View File

@@ -8,6 +8,8 @@ namespace PKHeX.Core
/// </summary>
internal static class EncounterUtil
{
internal static BinLinkerAccessor Get(string resource, string ident) => BinLinkerAccessor.Get(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
/// <summary>
/// Gets the relevant <see cref="EncounterStatic"/> objects that appear in the relevant game.
/// </summary>

View File

@@ -1,5 +1,6 @@
using static PKHeX.Core.GameVersion;
using static PKHeX.Core.EncounterGBLanguage;
using static PKHeX.Core.EncounterUtil;
namespace PKHeX.Core
{
@@ -8,17 +9,14 @@ namespace PKHeX.Core
/// </summary>
internal static class Encounters1
{
internal static readonly EncounterArea1[] SlotsRD = Get("red", "g1", RD);
internal static readonly EncounterArea1[] SlotsGN = Get("blue", "g1", GN);
internal static readonly EncounterArea1[] SlotsYW = Get("yellow", "g1", YW);
internal static readonly EncounterArea1[] SlotsBU = Get("blue_jp", "g1", BU);
internal static readonly EncounterArea1[] SlotsRD = EncounterArea1.GetAreas(Get("red", "g1"), RD);
internal static readonly EncounterArea1[] SlotsGN = EncounterArea1.GetAreas(Get("blue", "g1"), GN);
internal static readonly EncounterArea1[] SlotsYW = EncounterArea1.GetAreas(Get("yellow", "g1"), YW);
internal static readonly EncounterArea1[] SlotsBU = EncounterArea1.GetAreas(Get("blue_jp", "g1"), BU);
internal static readonly EncounterArea1[] SlotsRBY = ArrayUtil.ConcatAll(SlotsRD, SlotsGN, SlotsYW);
internal static readonly EncounterArea1[] SlotsRGBY = ArrayUtil.ConcatAll(SlotsRBY, SlotsBU);
private static EncounterArea1[] Get(string name, string ident, GameVersion game) =>
EncounterArea1.GetAreas(BinLinker.Unpack(Util.GetBinaryResource($"encounter_{name}.pkl"), ident), game);
static Encounters1() => EncounterUtil.MarkEncounterTradeNicknames(TradeGift_RBY, TradeGift_RBY_OTs);
static Encounters1() => MarkEncounterTradeNicknames(TradeGift_RBY, TradeGift_RBY_OTs);
internal static readonly EncounterStatic1[] StaticRBY =
{

View File

@@ -10,14 +10,12 @@ namespace PKHeX.Core
/// </summary>
internal static class Encounters2
{
internal static readonly EncounterArea2[] SlotsGD = Get("gold", "g2", GD);
internal static readonly EncounterArea2[] SlotsSV = Get("silver", "g2", SV);
internal static readonly EncounterArea2[] SlotsC = Get("crystal", "g2", C);
internal static readonly EncounterArea2[] SlotsGD = EncounterArea2.GetAreas(Get("gold", "g2"), GD);
internal static readonly EncounterArea2[] SlotsSV = EncounterArea2.GetAreas(Get("silver", "g2"), SV);
internal static readonly EncounterArea2[] SlotsC = EncounterArea2.GetAreas(Get("crystal", "g2"), C);
internal static readonly EncounterArea2[] SlotsGS = ArrayUtil.ConcatAll(SlotsGD, SlotsSV);
internal static readonly EncounterArea2[] SlotsGSC = ArrayUtil.ConcatAll(SlotsGS, SlotsC);
private static EncounterArea2[] Get(string name, string ident, GameVersion game) =>
EncounterArea2.GetAreas(BinLinker.Unpack(Util.GetBinaryResource($"encounter_{name}.pkl"), ident), game);
static Encounters2() => MarkEncounterTradeStrings(TradeGift_GSC, TradeGift_GSC_OTs);

View File

@@ -10,15 +10,14 @@ namespace PKHeX.Core
internal static class Encounters3
{
private static readonly EncounterArea3[] SlotsSwarmRSE = GetSwarm("rse_swarm", "rs", RSE);
internal static readonly EncounterArea3[] SlotsR = ArrayUtil.ConcatAll(Get("r", "ru", R), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsS = ArrayUtil.ConcatAll(Get("s", "sa", S), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsE = ArrayUtil.ConcatAll(Get("e", "em", E), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsFR = Get("fr", "fr", FR);
internal static readonly EncounterArea3[] SlotsLG = Get("lg", "lg", LG);
internal static readonly EncounterArea3[] SlotsR = ArrayUtil.ConcatAll(GetRegular("r", "ru", R), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsS = ArrayUtil.ConcatAll(GetRegular("s", "sa", S), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsE = ArrayUtil.ConcatAll(GetRegular("e", "em", E), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsFR = GetRegular("fr", "fr", FR);
internal static readonly EncounterArea3[] SlotsLG = GetRegular("lg", "lg", LG);
private static byte[][] ReadUnpack(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
private static EncounterArea3[] Get(string resource, string ident, GameVersion game) => EncounterArea3.GetAreas(ReadUnpack(resource, ident), game);
private static EncounterArea3[] GetSwarm(string resource, string ident, GameVersion game) => EncounterArea3.GetAreasSwarm(ReadUnpack(resource, ident), game);
private static EncounterArea3[] GetRegular(string resource, string ident, GameVersion game) => EncounterArea3.GetAreas(Get(resource, ident), game);
private static EncounterArea3[] GetSwarm(string resource, string ident, GameVersion game) => EncounterArea3.GetAreasSwarm(Get(resource, ident), game);
static Encounters3()
{

View File

@@ -15,7 +15,6 @@ internal static class Encounters4
internal static readonly EncounterArea4[] SlotsPt = EncounterArea4.GetAreas(Get("pt", "pt"), Pt);
internal static readonly EncounterArea4[] SlotsHG = EncounterArea4.GetAreas(Get("hg", "hg"), HG);
internal static readonly EncounterArea4[] SlotsSS = EncounterArea4.GetAreas(Get("ss", "ss"), SS);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
static Encounters4()
{

View File

@@ -13,7 +13,6 @@ public static class Encounters5
internal static readonly EncounterArea5[] SlotsW = EncounterArea5.GetAreas(Get("w", "51"), W);
internal static readonly EncounterArea5[] SlotsB2 = EncounterArea5.GetAreas(Get("b2", "52"), B2);
internal static readonly EncounterArea5[] SlotsW2 = EncounterArea5.GetAreas(Get("w2", "52"), W2);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
static Encounters5()
{

View File

@@ -14,7 +14,6 @@ internal static class Encounters6
internal static readonly EncounterArea6XY[] SlotsY = EncounterArea6XY.GetAreas(Get("y", "xy"), Y, FriendSafari);
internal static readonly EncounterArea6AO[] SlotsA = EncounterArea6AO.GetAreas(Get("as", "ao"), AS);
internal static readonly EncounterArea6AO[] SlotsO = EncounterArea6AO.GetAreas(Get("or", "ao"), OR);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
static Encounters6()
{

View File

@@ -13,7 +13,6 @@ internal static class Encounters7
internal static readonly EncounterArea7[] SlotsMN = EncounterArea7.GetAreas(Get("mn", "sm"), MN);
internal static readonly EncounterArea7[] SlotsUS = EncounterArea7.GetAreas(Get("us", "uu"), US);
internal static readonly EncounterArea7[] SlotsUM = EncounterArea7.GetAreas(Get("um", "uu"), UM);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
static Encounters7()
{

View File

@@ -7,7 +7,6 @@ internal static class Encounters7b
{
internal static readonly EncounterArea7b[] SlotsGP = EncounterArea7b.GetAreas(Get("gp", "gg"), GP);
internal static readonly EncounterArea7b[] SlotsGE = EncounterArea7b.GetAreas(Get("ge", "gg"), GE);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
private static readonly EncounterStatic7b[] Encounter_GG =
{

View File

@@ -17,7 +17,6 @@ internal static class Encounters8
private static readonly EncounterArea8[] SlotsSH_Symbol = EncounterArea8.GetAreas(Get("sh_symbol", "sh"), SH, true);
private static readonly EncounterArea8[] SlotsSW_Hidden = EncounterArea8.GetAreas(Get("sw_hidden", "sw"), SW);
private static readonly EncounterArea8[] SlotsSH_Hidden = EncounterArea8.GetAreas(Get("sh_hidden", "sh"), SH);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
internal static readonly EncounterArea8[] SlotsSW = ArrayUtil.ConcatAll(SlotsSW_Symbol, SlotsSW_Hidden);
internal static readonly EncounterArea8[] SlotsSH = ArrayUtil.ConcatAll(SlotsSH_Symbol, SlotsSH_Hidden);

View File

@@ -0,0 +1,128 @@
using static PKHeX.Core.EncounterUtil;
using static PKHeX.Core.Shiny;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core;
internal static class Encounters8a
{
internal static readonly EncounterArea8a[] SlotsLA = EncounterArea8a.GetAreas(Get("la", "la"), PLA);
private const byte M = 127; // Middle Height/Weight
private const byte A = 255; // Max Height/Weight for Alphas
private const byte U = 128; // Middle Height - Unown
internal static readonly EncounterStatic8a[] StaticLA =
{
// Gifts
new(722,000,05,M,M) { Location = 006, Gift = true, Ball = (int)Ball.LAPoke }, // Rowlet
new(155,000,05,M,M) { Location = 006, Gift = true, Ball = (int)Ball.LAPoke }, // Cyndaquil
new(501,000,05,M,M) { Location = 006, Gift = true, Ball = (int)Ball.LAPoke }, // Oshawott
new(037,001,40,M,M) { Location = 088, Gift = true, Ball = (int)Ball.LAPoke }, // Vulpix-1
new(483,000,65,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Ball = (int)Ball.LAOrigin }, // Dialga
new(484,000,65,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Ball = (int)Ball.LAOrigin }, // Palkia
new(493,000,75,M,M) { Location = 109, FlawlessIVCount = 3, Gift = true, Ball = (int)Ball.LAPoke, Fateful = true }, // Arceus
// Static Encounters - Scripted Table Slots
new(480,000,70,M,M) { Location = 111, FlawlessIVCount = 3 }, // Uxie
new(481,000,70,M,M) { Location = 104, FlawlessIVCount = 3 }, // Mesprit
new(482,000,70,M,M) { Location = 105, FlawlessIVCount = 3 }, // Azelf
new(485,000,70,M,M) { Location = 068, FlawlessIVCount = 3 }, // Heatran
new(488,000,70,M,M) { Location = 082, FlawlessIVCount = 3 }, // Cresselia
new(641,000,70,M,M) { Location = 090, FlawlessIVCount = 3 }, // Tornadus
new(642,000,70,M,M) { Location = 009, FlawlessIVCount = 3 }, // Thundurus
new(645,000,70,M,M) { Location = 027, FlawlessIVCount = 3 }, // Landorus
new(905,000,70,M,M) { Location = 038, FlawlessIVCount = 3 }, // Enamorus
new(077,000,15 ) { Location = 014, Shiny = Always}, // Ponyta*
new(442,000,60,M,M) { Location = 043, FlawlessIVCount = 3 }, // Spiritomb
new(489,000,33 ) { Location = 064, Fateful = true }, // Phione
new(489,000,34 ) { Location = 064, Fateful = true }, // Phione
new(489,000,35 ) { Location = 064, Fateful = true }, // Phione
new(489,000,36 ) { Location = 064, Fateful = true }, // Phione
new(490,000,50,M,M) { Location = 064, FlawlessIVCount = 3, Fateful = true }, // Manaphy
new(491,000,70,M,M) { Location = 010, FlawlessIVCount = 3, Fateful = true }, // Darkrai
new(492,000,70,M,M) { Location = 026, FlawlessIVCount = 3, Fateful = true }, // Shaymin
// Unown Notes
new(201,000,25,U) { Location = 040 }, // Unown A
new(201,001,25,U) { Location = 056 }, // Unown B
new(201,002,25,U) { Location = 081 }, // Unown C
new(201,003,25,U) { Location = 008 }, // Unown D
new(201,004,25,U) { Location = 022 }, // Unown E
new(201,005,25,U) { Location = 010 }, // Unown F
new(201,006,25,U) { Location = 017 }, // Unown G
new(201,007,25,U) { Location = 006 }, // Unown H
new(201,008,25,U) { Location = 023 }, // Unown I
new(201,009,25,U) { Location = 072 }, // Unown J
new(201,010,25,U) { Location = 043 }, // Unown K
new(201,011,25,U) { Location = 086 }, // Unown L
new(201,012,25,U) { Location = 037 }, // Unown M
new(201,013,25,U) { Location = 009 }, // Unown N
new(201,014,25,U) { Location = 102 }, // Unown O
new(201,015,25,U) { Location = 075 }, // Unown P
new(201,016,25,U) { Location = 058 }, // Unown Q
new(201,017,25,U) { Location = 059 }, // Unown R
new(201,018,25,U) { Location = 025 }, // Unown S
new(201,019,25,U) { Location = 092 }, // Unown T
new(201,020,25,U) { Location = 011 }, // Unown U
new(201,021,25,U) { Location = 038 }, // Unown V
new(201,022,25,U) { Location = 006 }, // Unown W
new(201,023,25,U) { Location = 021 }, // Unown X
new(201,024,25,U) { Location = 097 }, // Unown Y
new(201,025,25,U) { Location = 051 }, // Unown Z
new(201,026,25,U) { Location = 142 }, // Unown ! at Snowfall Hot Spring
new(201,026,25,U) { Location = 099 }, // Unown ! along the Arenas Approach (crossover)
new(201,027,25,U) { Location = 006 }, // Unown ?
// Static Encounters
new(046,000,50,M,M) { Location = 019 }, // paras01: Paras
new(390,000,12,M,M) { Location = 007 }, // hikozaru_01: Chimchar
new(434,000,20,M,M) { Location = 008 }, // skunpuu01: Stunky
new(441,000,34,M,M) { Location = 129 }, // perap01: Chatot
new(450,000,34,M,M) { Location = 036, Gender = 0 }, // kabaldon01: Hippowdon
new(459,000,50,M,M) { Location = 101, Gender = 1 }, // yukikaburi01: Snover
new(483,000,65,M,M) { Location = 109, FlawlessIVCount = 3 }, // dialga01: Dialga
new(484,000,65,M,M) { Location = 109, FlawlessIVCount = 3 }, // palkia01: Palkia
new(486,000,70,M,M) { Location = 095, FlawlessIVCount = 3 }, // regigigas01: Regigigas
new(487,001,70,M,M) { Location = 067, FlawlessIVCount = 3 }, // giratina02: Giratina-1
new(362,000,64,A,A) { Location = 011, IsAlpha = true, Moves = new[] {442,059,556,242}, Mastery = new[] {true,true,true, true } }, // onigohri01: Glalie
new(402,000,12,A,A) { Location = 007, IsAlpha = true, Gender = 0, Moves = new[] {206,071,033,332}, Mastery = new[] {true,true,false,false} }, // mev002: Kricketune
new(416,000,60,A,A) { Location = 022, IsAlpha = true, Gender = 1, FlawlessIVCount = 3, Moves = new[] {188,403,408,405}, Mastery = new[] {true,true,true ,true } }, // beequen01: Vespiquen
new(571,001,58,M,M) { Location = 111, IsAlpha = true, Moves = new[] {555,421,841,417}, Mastery = new[] {true,true,true ,true } }, // zoroark01: Zoroark-1
new(706,001,58,M,M) { Location = 104, IsAlpha = true, Moves = new[] {231,406,842,056}, Mastery = new[] {true,true,true ,true } }, // numelgon01: Goodra-1
new(904,000,58,M,M) { Location = 105, IsAlpha = true, Moves = new[] {301,398,401,038}, Mastery = new[] {true,true,true ,false} }, // harysen01: Overqwil
// Uncatchable
// new(901,000,26,M,M) { Location = -01, Gender = 0, Nature = Adamant, FlawlessIVCount = 3, GVs = new[]{2,3,0,2,2,0} }, // ringuma01: Ursaluna
// new(190,000,30,M,M) { Location = -01, Gender = 0 }, // aipom01: Aipom
// new(190,000,30,M,M) { Location = -01, Gender = 1 }, // aipom01: Aipom
// new(185,000,26,S,S) { Location = -01, Gender = 0 }, // usokkie01: Sudowoodo
// new(460,000,55,M,M) { Location = -01, Gender = 0 }, // yukinooh01: Abomasnow
// new(478,000,55,M,M) { Location = -01, Gender = 1 }, // yukimenoko01: Froslass
// new(448,000,62,M,M) { Location = -01, Gender = 0, FlawlessIVCount = 3 }, // lucario01: Lucario
// new(628,001,54,M,M) { Location = -01, Gender = 0, Nature = Adamant }, // warrgle01: Braviary-1
// new(217,000,30,M,M) { Location = -01, Gender = 0, Nature = Modest }, // ringuma02: Ursaring
// new(483,001,65,M,M) { Location = -01, FlawlessIVCount = 3 }, // nsi_ex_07: Dialga-1
// new(484,001,65,M,M) { Location = -01, FlawlessIVCount = 3 }, // nsi_ex_08: Palkia-1
// new(900,001,18,M,M) { Location = -01, Gender = 0, Nature = Adamant }, // ns001: Kleavor-1
// new(900,001,18,M,M) { Location = -01, Gender = 0, Nature = Adamant, FlawlessIVCount = 3 }, // nsi_ex_01: Kleavor-1
// new(549,002,30,M,M) { Location = -01, Gender = 1, Nature = Adamant, FlawlessIVCount = 3, Moves = new[] {078,077,412,249}, Mastery = new[] {true,true,true,true } }, // dredear01: Lilligant-2
// new(059,002,36,M,M) { Location = -01, Gender = 0, Nature = Adamant, FlawlessIVCount = 3 }, // nsi_ex_06: Arcanine-2
// new(101,002,46,M,M) { Location = -01, Gender = 0, Nature = Modest, FlawlessIVCount = 3, Moves = new[] {086,412,085,087}, Mastery = new[] {true,true,true,false} }, // nsi_ex_04: Electrode-2
// new(713,002,56,M,M) { Location = -01, Gender = 0, Nature = Relaxed, FlawlessIVCount = 3 }, // nsi_ex_03: Avalugg-2
// new(493,000,75,M,M) { Location = -01, FlawlessIVCount = 3, Moves = new[] {347,326,449,063}, Mastery = new[] {true,true,true,true } }, // nsi_ex_09: Arceus
// new(483,001,85,M,M) { Location = -01, GVs = new[]{10,10,10,10,10,10} }, // nsi_ex_07_r: Dialga-1
// new(484,001,85,M,M) { Location = -01, GVs = new[]{10,10,10,10,10,10} }, // nsi_ex_08_r: Palkia-1
// new(900,001,70,M,M) { Location = -01, Gender = 0, Nature = Adamant, GVs = new[]{10,10,10,10,10,10}, Moves = new[] {830,403,404,370}, Mastery = new[] {true,true,true,true} }, // nsi_ex_01_r: Kleavor-1
// new(549,002,70,M,M) { Location = -01, Gender = 1, Nature = Adamant, GVs = new[]{10,10,10,10,10,10}, Moves = new[] {837,080,409,416}, Mastery = new[] {true,true,true,true} }, // dredear01_r: Lilligant-2
// new(059,002,70,M,M) { Location = -01, Gender = 0, Nature = Adamant, GVs = new[]{10,10,10,10,10,10}, Moves = new[] {833,444,242,528}, Mastery = new[] {true,true,true,true} }, // nsi_ex_06_r: Arcanine-2
// new(101,002,70,M,M) { Location = -01, Gender = 0, Nature = Modest, GVs = new[]{10,10,10,10,10,10}, Moves = new[] {835,087,063,086}, Mastery = new[] {true,true,true,true} }, // nsi_ex_04_r: Electrode-2
// new(713,002,70,M,M) { Location = -01, Gender = 0, Nature = Relaxed, GVs = new[]{10,10,10,10,10,10}, Moves = new[] {836,667,444,442}, Mastery = new[] {true,true,true,true} }, // nsi_ex_03_r: Avalugg-2
// new(493,000,100,M,M){ Location = -01, GVs = new[]{10,10,10,10,10,10}, Moves = new[] {347,326,449,063}, Mastery = new[] {true,true,true,true} }, // nsi_ex_09_r: Arceus
};
}

View File

@@ -7,16 +7,14 @@ namespace PKHeX.Core
{
internal static class Encounters8b
{
private static readonly EncounterArea8b[] SlotsBD_OW = EncounterArea8b.GetAreas(Get("encounter_bd", "bs"), BD);
private static readonly EncounterArea8b[] SlotsSP_OW = EncounterArea8b.GetAreas(Get("encounter_sp", "bs"), SP);
private static readonly EncounterArea8b[] SlotsBD_UG = EncounterArea8b.GetAreas(Get("underground_bd", "bs"), BD);
private static readonly EncounterArea8b[] SlotsSP_UG = EncounterArea8b.GetAreas(Get("underground_sp", "bs"), SP);
private static readonly EncounterArea8b[] SlotsBD_OW = EncounterArea8b.GetAreas(Get("bd", "bs"), BD);
private static readonly EncounterArea8b[] SlotsSP_OW = EncounterArea8b.GetAreas(Get("sp", "bs"), SP);
private static readonly EncounterArea8b[] SlotsBD_UG = EncounterArea8b.GetAreas(Get("bd_underground", "bs"), BD);
private static readonly EncounterArea8b[] SlotsSP_UG = EncounterArea8b.GetAreas(Get("sp_underground", "bs"), SP);
internal static readonly EncounterArea8b[] SlotsBD = ArrayUtil.ConcatAll(SlotsBD_OW, SlotsBD_UG);
internal static readonly EncounterArea8b[] SlotsSP = ArrayUtil.ConcatAll(SlotsSP_OW, SlotsSP_UG);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"{resource}.pkl"), ident);
static Encounters8b() => MarkEncounterTradeStrings(TradeGift_BDSP, TradeBDSP);
private static readonly EncounterStatic8b[] Encounter_BDSP =

View File

@@ -8,15 +8,8 @@ internal static class EncountersGO
{
internal const int MAX_LEVEL = 50;
internal static readonly EncounterArea7g[] SlotsGO_GG = EncounterArea7g.GetArea(Get("go_lgpe", "go"));
internal static readonly EncounterArea8g[] SlotsGO = EncounterArea8g.GetArea(Get("go_home", "go"));
private static byte[][] Get(string resource, string ident)
{
var name = $"encounter_{resource}.pkl";
var data = Util.GetBinaryResource(name);
return BinLinker.Unpack(data, ident);
}
internal static readonly EncounterArea7g[] SlotsGO_GG = EncounterArea7g.GetArea(EncounterUtil.Get("go_lgpe", "go"));
internal static readonly EncounterArea8g[] SlotsGO = EncounterArea8g.GetArea(EncounterUtil.Get("go_home", "go"));
}
#else
public static class EncountersGO
@@ -32,11 +25,11 @@ public static void Reload()
SlotsGO = EncounterArea8g.GetArea(Get("go_home", "go"));
}
private static byte[][] Get(string resource, string ident)
private static BinLinkerAccessor Get(string resource, string ident)
{
var name = $"encounter_{resource}.pkl";
var data = System.IO.File.Exists(name) ? System.IO.File.ReadAllBytes(name) : Util.GetBinaryResource(name);
return BinLinker.Unpack(data, ident);
return BinLinkerAccessor.Get(data, ident);
}
}
#endif

View File

@@ -91,8 +91,7 @@ protected virtual void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria
pk.Version = (int)version;
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation);
var ball = FixedBall;
pk.Ball = (int)(ball == Ball.None ? Ball.Poke : ball);
ApplyDetailsBall(pk);
pk.Language = lang;
pk.Form = GetWildForm(pk, Form, sav);
pk.OT_Friendship = pk.PersonalInfo.BaseFriendship;
@@ -114,6 +113,12 @@ protected virtual void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria
}
}
protected virtual void ApplyDetailsBall(PKM pk)
{
var ball = FixedBall;
pk.Ball = (int)(ball == Ball.None ? Ball.Poke : ball);
}
protected virtual void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var moves = MoveLevelUp.GetEncounterMoves(pk, level, version);

View File

@@ -17,5 +17,15 @@ protected override void SetPINGA(PKM pk, EncounterCriteria criteria)
base.SetPINGA(pk, criteria);
pk.SetRandomEC();
}
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
{
base.ApplyDetails(sav, criteria, pk);
if (pk is IScaledSizeValue v)
{
v.ResetHeight();
v.ResetWeight();
}
}
}
}

View File

@@ -0,0 +1,101 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Encounter Slot found in <see cref="GameVersion.SWSH"/>.
/// </summary>
/// <inheritdoc cref="EncounterSlot"/>
public sealed record EncounterSlot8a : EncounterSlot, IAlpha
{
public override int Generation => 8;
public bool IsAlpha { get => AlphaType is not 0; set => throw new InvalidOperationException("Do not mutate this field."); }
public byte FlawlessIVCount { get; }
public Gender Gender { get; }
public byte AlphaType { get; } // 0=Never, 1=Random, 2=Guaranteed
public EncounterSlot8a(EncounterArea8a area, int species, int form, int min, int max, byte alphaType, byte flawlessIVs, Gender gender) : base(area, species, form, min, max)
{
AlphaType = alphaType;
FlawlessIVCount = flawlessIVs;
Gender = gender;
}
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
{
base.ApplyDetails(sav, criteria, pk);
pk.SetRandomEC();
if (Gender != Gender.Random)
pk.Gender = (int)Gender;
if (IsAlpha)
{
if (pk is IAlpha a)
a.IsAlpha = true;
if (pk is IScaledSize s)
s.HeightScalar = s.WeightScalar = byte.MaxValue;
if (pk is PA8 pa)
pa.SetMasteryFlagMove(pa.AlphaMove = pa.GetRandomAlphaMove());
}
if (pk is IScaledSizeValue v)
{
v.ResetHeight();
v.ResetWeight();
}
if (pk is PA8 pa8)
{
pa8.HeightScalarCopy = pa8.HeightScalar;
pa8.SetMasteryFlags();
}
if (FlawlessIVCount > 0)
pk.SetRandomIVs(flawless: FlawlessIVCount);
}
protected override void ApplyDetailsBall(PKM pk) => pk.Ball = (int)Ball.LAPoke;
public override EncounterMatchRating GetMatchRating(PKM pkm)
{
if (pkm is IAlpha a && a.IsAlpha != IsAlpha)
return EncounterMatchRating.DeferredErrors;
if (Gender is not Gender.Random && pkm.Gender != (int)Gender)
return EncounterMatchRating.DeferredErrors;
if (FlawlessIVCount is not 0 && pkm.FlawlessIVCount != FlawlessIVCount)
return EncounterMatchRating.DeferredErrors;
var result = GetAlphaMoveCompatibility(pkm);
var orig = base.GetMatchRating(pkm);
return result > orig ? result : orig;
}
private EncounterMatchRating GetAlphaMoveCompatibility(PKM pkm)
{
// Check for Alpha move compatibility.
if (pkm is not PA8 pa)
return EncounterMatchRating.Match;
var alphaMove = pa.AlphaMove;
if (!pa.IsAlpha)
return alphaMove == 0 ? EncounterMatchRating.Match : EncounterMatchRating.DeferredErrors;
var pi = PersonalTable.LA.GetFormEntry(Species, Form);
var tutors = pi.SpecialTutors[0];
if (alphaMove is 0)
{
bool hasAnyTutor = Array.IndexOf(tutors, true) >= 0;
if (hasAnyTutor)
return EncounterMatchRating.Deferred;
}
else
{
var idx = pa.MoveShopPermitIndexes;
var index = idx.IndexOf(idx);
if (index == -1)
return EncounterMatchRating.Deferred;
if (!tutors[index])
return EncounterMatchRating.Deferred;
}
return EncounterMatchRating.Match;
}
}

View File

@@ -77,7 +77,7 @@ protected virtual void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation);
pk.CurrentLevel = level;
pk.Ball = Ball;
ApplyDetailsBall(pk);
pk.HeldItem = HeldItem;
pk.OT_Friendship = pk.PersonalInfo.BaseFriendship;
@@ -111,6 +111,8 @@ protected virtual void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria
pd.DynamaxLevel = d.DynamaxLevel;
}
protected virtual void ApplyDetailsBall(PKM pk) => pk.Ball = Ball;
protected virtual int GetMinimalLevel() => LevelMin;
protected virtual void SetPINGA(PKM pk, EncounterCriteria criteria)

View File

@@ -11,6 +11,11 @@ public sealed record EncounterStatic7b(GameVersion Version) : EncounterStatic(Ve
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
{
base.ApplyDetails(sav, criteria, pk);
if (pk is IScaledSizeValue v)
{
v.ResetHeight();
v.ResetWeight();
}
pk.SetRandomEC();
}
}

View File

@@ -0,0 +1,131 @@
using System;
namespace PKHeX.Core;
/// <summary>
/// Generation 8 Static Encounter
/// </summary>
/// <inheritdoc cref="EncounterStatic"/>
public sealed record EncounterStatic8a(GameVersion Version) : EncounterStatic(Version), IAlpha
{
public bool[]? Mastery;
public override int Generation => 8;
public byte HeightScalar { get; }
public byte WeightScalar { get; }
public bool IsAlpha { get; set; }
public bool HasFixedHeight => HeightScalar != NoScalar;
public bool HasFixedWeight => WeightScalar != NoScalar;
private const byte NoScalar = 0;
public EncounterStatic8a(ushort species, ushort form, byte level, byte h = NoScalar, byte w = NoScalar) : this(GameVersion.PLA)
{
Species = species;
Form = form;
Level = level;
HeightScalar = h;
WeightScalar = w;
Shiny = Shiny.Never;
}
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
{
base.ApplyDetails(sav, criteria, pk);
if (pk is IScaledSize s)
{
if (HasFixedHeight)
s.HeightScalar = HeightScalar;
if (HasFixedWeight)
s.WeightScalar = WeightScalar;
}
if (pk is IScaledSizeValue v)
{
v.ResetHeight();
v.ResetWeight();
}
if (IsAlpha && pk is IAlpha a)
a.IsAlpha = true;
if (pk is PA8 pa)
{
pa.SetMasteryFlags();
pa.HeightScalarCopy = pa.HeightScalar;
if (IsAlpha)
pa.SetMasteryFlagMove(pa.AlphaMove = pa.GetRandomAlphaMove());
}
pk.SetRandomEC();
}
protected override void ApplyDetailsBall(PKM pk) => pk.Ball = Gift ? Ball : (int)Core.Ball.LAPoke;
public override bool IsMatchExact(PKM pkm, DexLevel evo)
{
if (!base.IsMatchExact(pkm, evo))
return false;
if (pkm is IScaledSize s)
{
if (HasFixedHeight && s.HeightScalar != HeightScalar)
return false;
if (HasFixedWeight && s.WeightScalar != WeightScalar)
return false;
}
if (pkm is IAlpha a && a.IsAlpha != IsAlpha)
return false;
return true;
}
public override EncounterMatchRating GetMatchRating(PKM pkm)
{
if (Shiny != Shiny.Random && !Shiny.IsValid(pkm))
return EncounterMatchRating.DeferredErrors;
if (Gift && pkm.Ball != Ball)
return EncounterMatchRating.DeferredErrors;
if (!IsForcedMasteryCorrect(pkm))
return EncounterMatchRating.PartialMatch;
var orig = base.GetMatchRating(pkm);
if (orig is not EncounterMatchRating.Match)
return orig;
if (IsAlpha && pkm is PA8 { AlphaMove: 0 })
return EncounterMatchRating.Deferred;
return EncounterMatchRating.Match;
}
private bool IsForcedMasteryCorrect(PKM pkm)
{
if (Mastery is not { } m)
return true;
if (Species == (int)Core.Species.Kricketune && Level == 12)
{
if (pkm is PA8 { AlphaMove: not (int)Move.FalseSwipe })
return false;
}
if (pkm is not IMoveShop8Mastery p)
return true;
for (int i = 0; i < m.Length; i++)
{
if (!m[i])
continue;
var move = Moves[i];
var index = p.MoveShopPermitIndexes.IndexOf(move);
if (index == -1)
continue; // manually mastered for encounter, not a tutor
if (!p.GetMasteredRecordFlag(index))
return false;
}
return true;
}
}

View File

@@ -14,5 +14,15 @@ public EncounterTrade7b(GameVersion game) : base(game)
Shiny = Shiny.Random;
IsNicknamed = false;
}
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
{
base.ApplyDetails(sav, criteria, pk);
if (pk is IScaledSizeValue v)
{
v.ResetHeight();
v.ResetWeight();
}
}
}
}

View File

@@ -17,6 +17,7 @@ public static IEnumerable<IEncounterable> GetEncounters(PKM pkm)
return pkm.Version switch
{
(int)GameVersion.GO => EncounterGenerator7.GetEncountersGO(pkm, chain),
(int)GameVersion.PLA => EncounterGenerator8a.GetEncounters(pkm, chain),
(int)GameVersion.BD or (int)GameVersion.SP => EncounterGenerator8b.GetEncounters(pkm, chain),
_ => GetEncountersMainline(pkm, chain),
};

View File

@@ -0,0 +1,61 @@
using System.Collections.Generic;
using static PKHeX.Core.MysteryGiftGenerator;
using static PKHeX.Core.EncounterSlotGenerator;
using static PKHeX.Core.EncounterStaticGenerator;
using static PKHeX.Core.EncounterMatchRating;
namespace PKHeX.Core;
internal static class EncounterGenerator8a
{
public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
if (pkm.IsEgg)
yield break;
int ctr = 0;
if (pkm.FatefulEncounter)
{
foreach (var z in GetValidGifts(pkm, chain))
{ yield return z; ++ctr; }
if (ctr != 0) yield break;
}
IEncounterable? cache = null;
EncounterMatchRating rating = None;
// Static Encounters can collide with wild encounters (close match); don't break if a Static Encounter is yielded.
var encs = GetValidStaticEncounter(pkm, chain);
foreach (var z in encs)
{
var match = z.GetMatchRating(pkm);
if (match == Match)
{
yield return z;
}
else if (match < rating)
{
cache = z;
rating = match;
}
}
foreach (var z in GetValidWildEncounters(pkm, chain))
{
var match = z.GetMatchRating(pkm);
if (match == Match)
{
yield return z;
}
else if (match < rating)
{
cache = z;
rating = match;
}
}
if (cache != null)
yield return cache;
}
}

View File

@@ -202,7 +202,7 @@ private static IEnumerable<int> GetMovesForGeneration(PKM pk, IReadOnlyList<EvoC
moves = moves.Concat(MoveEgg.GetSharedEggMoves(pk, generation));
// TR moves -- default logic checks the TR flags, so we need to add all possible ones here.
if (!pk.BDSP)
if (!(pk.BDSP || pk.LA))
moves = moves.Concat(MoveTechnicalMachine.GetAllPossibleRecords(pk.Species, pk.Form));
}
if (pk.Species == (int)Species.Shedinja)

View File

@@ -13,6 +13,7 @@
using static PKHeX.Core.Encounters7;
using static PKHeX.Core.Encounters7b;
using static PKHeX.Core.Encounters8;
using static PKHeX.Core.Encounters8a;
using static PKHeX.Core.Encounters8b;
using static PKHeX.Core.EncountersGO;
@@ -153,6 +154,7 @@ private static IEnumerable<EncounterArea> GetEncounterAreas(PKM pkm, GameVersion
SH => SlotsSH,
BD => SlotsBD,
SP => SlotsSP,
PLA => SlotsLA,
_ => Array.Empty<EncounterArea>(),
};

View File

@@ -13,6 +13,7 @@
using static PKHeX.Core.Encounters7;
using static PKHeX.Core.Encounters7b;
using static PKHeX.Core.Encounters8;
using static PKHeX.Core.Encounters8a;
using static PKHeX.Core.Encounters8b;
using static PKHeX.Core.GameVersion;
@@ -175,6 +176,7 @@ internal static EncounterStatic7 GetVCStaticTransferEncounter(PKM pkm, IEncounte
SH => StaticSH,
BD => StaticBD,
SP => StaticSP,
PLA => StaticLA,
_ => Array.Empty<EncounterStatic>(),
};

View File

@@ -39,7 +39,7 @@ public static IEnumerable<MysteryGift> GetValidGifts(PKM pkm, IReadOnlyList<DexL
5 => MGDB_G5,
6 => MGDB_G6,
7 => pkm.LGPE ? MGDB_G7GG : MGDB_G7,
8 => pkm.BDSP ? MGDB_G8B : MGDB_G8,
8 => pkm.BDSP ? MGDB_G8B : pkm.LA ? MGDB_G8A : MGDB_G8,
_ => Array.Empty<MysteryGift>(),
};

View File

@@ -140,7 +140,8 @@ private static CheckMoveResult[] ParseMovesGenGB(PKM pkm, IReadOnlyList<int> cur
{
var res = new CheckMoveResult[4];
var enc = info.EncounterMatch;
var level = info.EvoChainsAllGens[enc.Generation][^1].MinLevel;
var evos = info.EvoChainsAllGens[enc.Generation];
var level = evos.Count > 0 ? evos[^1].MinLevel : enc.LevelMin;
var InitialMoves = Array.Empty<int>();
var SpecialMoves = GetSpecialMoves(enc);
var games = enc.Generation == 1 ? GBRestrictions.GetGen1Versions(enc) : GBRestrictions.GetGen2Versions(enc, pkm.Korean);

View File

@@ -85,6 +85,10 @@ public enum SlotType : byte
/// </summary>
SOS = 15,
Overworld = 16,
Distortion = 17,
Landmark = 18,
// Modifiers
/// <summary>

View File

@@ -74,13 +74,14 @@ public bool Valid(PKM pkm, int lvl, bool skipChecks)
RequiresLevelUp = false;
switch ((EvolutionType)Method)
{
case UseItem or UseItemWormhole:
case UseItem or UseItemWormhole or UseItemFullMoon:
case CriticalHitsInBattle or HitPointsLostInBattle or Spin:
case UseAgileStyleMoves or UseStrongStyleMoves:
case TowerOfDarkness or TowerOfWaters:
return true;
case UseItemMale:
case UseItemMale or RecoilDamageMale:
return pkm.Gender == 0;
case UseItemFemale:
case UseItemFemale or RecoilDamageFemale:
return pkm.Gender == 1;
case Trade or TradeHeldItem or TradeShelmetKarrablast:
@@ -104,6 +105,9 @@ public bool Valid(PKM pkm, int lvl, bool skipChecks)
// Level Up (any); the above Level Up (with condition) cases will reach here if they were valid
default:
if (IsThresholdCheckMode(pkm))
return lvl >= Level;
if (Level == 0 && lvl < 2)
return false;
if (lvl < Level)
@@ -118,6 +122,13 @@ public bool Valid(PKM pkm, int lvl, bool skipChecks)
}
}
private static bool IsThresholdCheckMode(PKM pkm)
{
// Starting in Legends: Arceus, level-up evolutions can be triggered if the current level is >= criteria.
// This allows for evolving over-leveled captures immediately without leveling up from capture level.
return pkm is PA8;
}
private bool HasMetLevelIncreased(PKM pkm, int lvl)
{
int origin = pkm.Generation;

View File

@@ -35,9 +35,9 @@ private static EvolutionMethod GetMethod(ReadOnlySpan<byte> entry)
return new EvolutionMethod(method, species, argument: arg, level: lvl);
}
public static IReadOnlyList<EvolutionMethod[]> GetArray(IReadOnlyList<byte[]> data)
public static IReadOnlyList<EvolutionMethod[]> GetArray(BinLinkerAccessor data)
{
var evos = new EvolutionMethod[data.Count][];
var evos = new EvolutionMethod[data.Length][];
for (int i = 0; i < evos.Length; i++)
evos[i] = GetMethods(data[i]);
return evos;

View File

@@ -32,9 +32,9 @@ private static EvolutionMethod ReadEvolution(ReadOnlySpan<byte> entry)
return new EvolutionMethod(method, species, argument: arg, level: level, form: form);
}
public static IReadOnlyList<EvolutionMethod[]> GetArray(IReadOnlyList<byte[]> data)
public static IReadOnlyList<EvolutionMethod[]> GetArray(BinLinkerAccessor data)
{
var evos = new EvolutionMethod[data.Count][];
var evos = new EvolutionMethod[data.Length][];
for (int i = 0; i < evos.Length; i++)
evos[i] = GetMethods(data[i]);
return evos;

View File

@@ -14,25 +14,27 @@ namespace PKHeX.Core
/// </remarks>
public sealed class EvolutionTree
{
private static readonly EvolutionTree Evolves1 = new(new[] { Get("rby") }, Gen1, PersonalTable.Y, MaxSpeciesID_1);
private static readonly EvolutionTree Evolves2 = new(new[] { Get("gsc") }, Gen2, PersonalTable.C, MaxSpeciesID_2);
private static readonly EvolutionTree Evolves3 = new(new[] { Get("g3") }, Gen3, PersonalTable.RS, MaxSpeciesID_3);
private static readonly EvolutionTree Evolves4 = new(new[] { Get("g4") }, Gen4, PersonalTable.DP, MaxSpeciesID_4);
private static readonly EvolutionTree Evolves5 = new(new[] { Get("g5") }, Gen5, PersonalTable.BW, MaxSpeciesID_5);
private static readonly EvolutionTree Evolves6 = new(Unpack("ao"), Gen6, PersonalTable.AO, MaxSpeciesID_6);
private static readonly EvolutionTree Evolves7 = new(Unpack("uu"), Gen7, PersonalTable.USUM, MaxSpeciesID_7_USUM);
private static readonly EvolutionTree Evolves7b = new(Unpack("gg"), Gen7, PersonalTable.GG, MaxSpeciesID_7b);
private static readonly EvolutionTree Evolves8 = new(Unpack("ss"), Gen8, PersonalTable.SWSH, MaxSpeciesID_8);
private static readonly EvolutionTree Evolves8b = new(Unpack("bs"), Gen8, PersonalTable.BDSP, MaxSpeciesID_8b);
private static readonly EvolutionTree Evolves1 = new(GetResource("rby"), Gen1, PersonalTable.Y, MaxSpeciesID_1);
private static readonly EvolutionTree Evolves2 = new(GetResource("gsc"), Gen2, PersonalTable.C, MaxSpeciesID_2);
private static readonly EvolutionTree Evolves3 = new(GetResource("g3"), Gen3, PersonalTable.RS, MaxSpeciesID_3);
private static readonly EvolutionTree Evolves4 = new(GetResource("g4"), Gen4, PersonalTable.DP, MaxSpeciesID_4);
private static readonly EvolutionTree Evolves5 = new(GetResource("g5"), Gen5, PersonalTable.BW, MaxSpeciesID_5);
private static readonly EvolutionTree Evolves6 = new(GetReader("ao"), Gen6, PersonalTable.AO, MaxSpeciesID_6);
private static readonly EvolutionTree Evolves7 = new(GetReader("uu"), Gen7, PersonalTable.USUM, MaxSpeciesID_7_USUM);
private static readonly EvolutionTree Evolves7b = new(GetReader("gg"), Gen7, PersonalTable.GG, MaxSpeciesID_7b);
private static readonly EvolutionTree Evolves8 = new(GetReader("ss"), Gen8, PersonalTable.SWSH, MaxSpeciesID_8);
private static readonly EvolutionTree Evolves8a = new(GetReader("la"), Gen8, PersonalTable.LA, MaxSpeciesID_8a);
private static readonly EvolutionTree Evolves8b = new(GetReader("bs"), Gen8, PersonalTable.BDSP, MaxSpeciesID_8b);
private static byte[] Get(string resource) => Util.GetBinaryResource($"evos_{resource}.pkl");
private static byte[][] Unpack(string resource) => BinLinker.Unpack(Get(resource), resource);
private static ReadOnlySpan<byte> GetResource(string resource) => Util.GetBinaryResource($"evos_{resource}.pkl");
private static BinLinkerAccessor GetReader(string resource) => BinLinkerAccessor.Get(GetResource(resource), resource);
static EvolutionTree()
{
// Add in banned evolution data!
Evolves7.FixEvoTreeSM();
Evolves8.FixEvoTreeSS();
Evolves8a.FixEvoTreeLA();
Evolves8b.FixEvoTreeBS();
}
@@ -57,7 +59,12 @@ static EvolutionTree()
5 => Evolves5,
6 => Evolves6,
7 => pkm.Version is (int)GO or (int)GP or (int)GE ? Evolves7b : Evolves7,
_ => pkm.Version is (int)BD or (int)SP ? Evolves8b : Evolves8,
_ => pkm.Version switch
{
(int)PLA => Evolves8a,
(int)BD or (int)SP => Evolves8b,
_ => Evolves8,
},
};
private readonly IReadOnlyList<EvolutionMethod[]> Entries;
@@ -69,7 +76,22 @@ static EvolutionTree()
#region Constructor
private EvolutionTree(IReadOnlyList<byte[]> data, GameVersion game, PersonalTable personal, int maxSpeciesTree)
private EvolutionTree(ReadOnlySpan<byte> data, GameVersion game, PersonalTable personal, int maxSpeciesTree)
{
Game = game;
Personal = personal;
MaxSpeciesTree = maxSpeciesTree;
Entries = GetEntries(data, game);
// Starting in Generation 7, forms have separate evolution data.
int format = Game - Gen1 + 1;
var oldStyle = format < 7;
var connections = oldStyle ? CreateTreeOld() : CreateTree();
Lineage = connections.ToLookup(obj => obj.Key, obj => obj.Value);
}
private EvolutionTree(BinLinkerAccessor data, GameVersion game, PersonalTable personal, int maxSpeciesTree)
{
Game = game;
Personal = personal;
@@ -134,13 +156,18 @@ private EvolutionTree(IReadOnlyList<byte[]> data, GameVersion game, PersonalTabl
}
}
private IReadOnlyList<EvolutionMethod[]> GetEntries(IReadOnlyList<byte[]> data, GameVersion game) => game switch
private IReadOnlyList<EvolutionMethod[]> GetEntries(ReadOnlySpan<byte> data, GameVersion game) => game switch
{
Gen1 => EvolutionSet1.GetArray(data, MaxSpeciesTree),
Gen2 => EvolutionSet1.GetArray(data, MaxSpeciesTree),
Gen3 => EvolutionSet3.GetArray(data),
Gen4 => EvolutionSet4.GetArray(data),
Gen5 => EvolutionSet5.GetArray(data),
_ => throw new ArgumentOutOfRangeException(),
};
private IReadOnlyList<EvolutionMethod[]> GetEntries(BinLinkerAccessor data, GameVersion game) => game switch
{
Gen1 => EvolutionSet1.GetArray(data[0], MaxSpeciesTree),
Gen2 => EvolutionSet1.GetArray(data[0], MaxSpeciesTree),
Gen3 => EvolutionSet3.GetArray(data[0]),
Gen4 => EvolutionSet4.GetArray(data[0]),
Gen5 => EvolutionSet5.GetArray(data[0]),
Gen6 => EvolutionSet6.GetArray(data),
Gen7 => EvolutionSet7.GetArray(data),
Gen8 => EvolutionSet7.GetArray(data),
@@ -174,6 +201,10 @@ private void FixEvoTreeSS()
BanEvo(s, 0, pkm => pkm is IGigantamax {CanGigantamax: true});
}
private void FixEvoTreeLA()
{
}
private void FixEvoTreeBS()
{
BanEvo((int)Species.Glaceon, 0, pkm => pkm.CurrentLevel == pkm.Met_Level); // Ice Stone is unreleased, requires Route 217 Ice Rock Level Up instead

View File

@@ -54,6 +54,11 @@ public enum EvolutionType : byte
LevelUpNatureLowKey = 47, // Toxtricity
TowerOfDarkness = 48, // Urshifu
TowerOfWaters = 49, // Urshifu
UseItemFullMoon = 50, // Ursaluna
UseAgileStyleMoves = 51, // Wyrdeer
UseStrongStyleMoves = 52, // Overqwil
RecoilDamageMale = 53, // Basculegion-0
RecoilDamageFemale = 54, // Basculegion-1
}
public static class EvolutionTypeExtensions

View File

@@ -269,6 +269,7 @@ public static class LegalityCheckStrings
public static string LHyperBelow100 { get; set; } = "Can't Hyper Train a Pokémon that isn't level 100.";
public static string LHyperPerfectAll { get; set; } = "Can't Hyper Train a Pokémon with perfect IVs.";
public static string LHyperPerfectOne { get; set; } = "Can't Hyper Train a perfect IV.";
public static string LHyperPerfectUnavailable { get; set; } = "Can't Hyper Train any IV(s).";
public static string LItemEgg { get; set; } = "Eggs cannot hold items.";
public static string LItemUnreleased { get; set; } = "Held item is unreleased.";
@@ -353,6 +354,7 @@ public static class LegalityCheckStrings
public static string LMoveNincadaEvo { get; set; } = "Learned by evolving Nincada into Ninjask.";
public static string LMoveNincadaEvoF_0 { get; set; } = "Learned by evolving Nincada into Ninjask in Generation {0}.";
public static string LMovePPTooHigh_0 { get; set; } = "Move {0} PP is above the amount allowed.";
public static string LMovePPUpsTooHigh_0 { get; set; } = "Move {0} PP Ups is above the amount allowed.";
public static string LMoveSourceShared { get; set; } = "Shared Non-Relearn Move.";
public static string LMoveSourceSharedF { get; set; } = "Shared Non-Relearn Move in Generation {0}.";
@@ -365,6 +367,13 @@ public static class LegalityCheckStrings
public static string LMoveRelearnInvalid { get; set; } = "Not an expected Relearnable move.";
public static string LMoveRelearnNone { get; set; } = "Expected no Relearn Move in slot.";
public static string LMoveShopAlphaMoveShouldBeMastered { get; set; } = "Alpha Move should be marked as mastered.";
public static string LMoveShopAlphaMoveShouldBeOther { get; set; } = "Alpha encounter cannot be found with this Alpha Move.";
public static string LMoveShopAlphaMoveShouldBeZero { get; set; } = "Only Alphas may have an Alpha Move set.";
public static string LMoveShopMasterInvalid_0 { get; set; } = "Cannot manually master {0}: not permitted to master.";
public static string LMoveShopMasterNotLearned_0 { get; set; } = "Cannot manually master {0}: not in possible learned level up moves.";
public static string LMoveShopPurchaseInvalid_0 { get; set; } = "Cannot purchase {0} from the move shop.";
public static string LMoveSourceDefault { get; set; } = "Default move.";
public static string LMoveSourceDuplicate { get; set; } = "Duplicate Move.";
public static string LMoveSourceEgg { get; set; } = "Egg Move.";
@@ -414,6 +423,9 @@ public static class LegalityCheckStrings
public static string LPIDTypeMismatch { get; set; } = "Encounter Type PID mismatch.";
public static string LPIDZero { get; set; } = "PID is not set.";
public static string LPokerusDaysTooHigh_0 { get; set; } = "Pokérus Days Remaining value is too high; expected <= {0}.";
public static string LPokerusStrainUnobtainable_0 { get; set; } = "Pokérus Strain {0} cannot be obtained.";
public static string LRibbonAllValid { get; set; } = "All ribbons accounted for.";
public static string LRibbonEgg { get; set; } = "Can't receive Ribbon(s) as an Egg.";
public static string LRibbonFInvalid_0 { get; set; } = "Invalid Ribbons: {0}";
@@ -423,13 +435,18 @@ public static class LegalityCheckStrings
public static string LStatDynamaxInvalid { get; set; } = "Dynamax Level is not within the expected range.";
public static string LStatIncorrectHeight { get; set; } = "Calculated Height does not match stored value.";
public static string LStatIncorrectHeightCopy { get; set; } = "Copy Height does not match the original value.";
public static string LStatIncorrectHeightValue { get; set; } = "Height does not match the expected value.";
public static string LStatIncorrectWeight { get; set; } = "Calculated Weight does not match stored value.";
public static string LStatIncorrectWeightValue { get; set; } = "Weight does not match the expected value.";
public static string LStatInvalidHeightWeight { get; set; } = "Height / Weight values are statistically improbable.";
public static string LStatIncorrectCP { get; set; } = "Calculated CP does not match stored value.";
public static string LStatGigantamaxInvalid { get; set; } = "Gigantamax Flag mismatch.";
public static string LStatGigantamaxValid { get; set; } = "Gigantamax Flag was changed via Max Soup.";
public static string LStatNatureInvalid { get; set; } = "Stat Nature is not within the expected range.";
public static string LStatBattleVersionInvalid { get; set; } = "Battle Version is not within the expected range.";
public static string LStatNobleInvalid { get; set; } = "Noble Flag mismatch.";
public static string LStatAlphaInvalid { get; set; } = "Alpha Flag mismatch.";
public static string LSuperComplete { get; set; } = "Super Training complete flag mismatch.";
public static string LSuperDistro { get; set; } = "Distribution Super Training missions are not released.";

View File

@@ -94,7 +94,8 @@ public int[] GetEncounterMoves(int level)
{
const int count = 4;
var moves = new int[count];
return GetEncounterMoves(level, moves);
SetEncounterMoves(level, moves);
return moves;
}
/// <summary>Returns the moves a Pokémon would have if it were encountered at the specified level.</summary>
@@ -103,7 +104,7 @@ public int[] GetEncounterMoves(int level)
/// <param name="moves">Move array to write to</param>
/// <param name="ctr">Starting index to begin overwriting at</param>
/// <returns>Array of Move IDs</returns>
public int[] GetEncounterMoves(int level, int[] moves, int ctr = 0)
public void SetEncounterMoves(int level, Span<int> moves, int ctr = 0)
{
for (int i = 0; i < Moves.Length; i++)
{
@@ -111,14 +112,50 @@ public int[] GetEncounterMoves(int level, int[] moves, int ctr = 0)
break;
int move = Moves[i];
bool alreadyHasMove = Array.IndexOf(moves, move) >= 0;
bool alreadyHasMove = moves.IndexOf(move) >= 0;
if (alreadyHasMove)
continue;
moves[ctr++] = move;
ctr &= 3;
}
}
/// <summary>Adds the learned moves by level up to the specified level.</summary>
public void SetLevelUpMoves(int startLevel, int endLevel, Span<int> moves, int ctr = 0)
{
int startIndex = Array.FindIndex(Levels, z => z >= startLevel);
int endIndex = Array.FindIndex(Levels, z => z > endLevel);
for (int i = startIndex; i < endIndex; i++)
{
int move = Moves[i];
bool alreadyHasMove = moves.IndexOf(move) >= 0;
if (alreadyHasMove)
continue;
moves[ctr++] = move;
ctr &= 3;
}
}
/// <summary>Adds the moves that are gained upon evolving.</summary>
/// <param name="moves">Move array to write to</param>
/// <param name="ctr">Starting index to begin overwriting at</param>
public void SetEvolutionMoves(Span<int> moves, int ctr = 0)
{
for (int i = 0; i < Moves.Length; i++)
{
if (Levels[i] != 0)
break;
int move = Moves[i];
bool alreadyHasMove = moves.IndexOf(move) >= 0;
if (alreadyHasMove)
continue;
moves[ctr++] = move;
ctr &= 3;
}
return moves;
}
public IList<int> GetUniqueMovesLearned(IEnumerable<int> seed, int maxLevel, int minLevel = 0)

View File

@@ -15,7 +15,7 @@ public static class LearnsetReader
/// </summary>
/// <param name="input">Raw ROM data containing the contiguous moves</param>
/// <param name="maxSpecies">Highest species ID for the input game.</param>
public static Learnset[] GetArray(byte[] input, int maxSpecies)
public static Learnset[] GetArray(ReadOnlySpan<byte> input, int maxSpecies)
{
var data = new Learnset[maxSpecies + 1];
@@ -30,7 +30,7 @@ public static Learnset[] GetArray(byte[] input, int maxSpecies)
/// Loads a learnset by reading 16-bit move,level pairs.
/// </summary>
/// <param name="entries">Entry data</param>
public static Learnset[] GetArray(byte[][] entries)
public static Learnset[] GetArray(BinLinkerAccessor entries)
{
Learnset[] data = new Learnset[entries.Length];
for (int i = 0; i < data.Length; i++)

View File

@@ -1,4 +1,4 @@
#define SUPPRESS
//#define SUPPRESS
using System;
using System.Collections.Generic;
@@ -311,6 +311,7 @@ private void UpdateChecks()
return;
Mark.Verify(this);
Arceus.Verify(this);
}
}
}

View File

@@ -32,5 +32,6 @@ internal static class LegalityAnalyzers
public static readonly MiscVerifier MiscValues = new();
public static readonly TransferVerifier Transfer = new();
public static readonly MarkVerifier Mark = new();
public static readonly LegendsArceusVerifier Arceus = new();
}
}

View File

@@ -142,10 +142,18 @@ internal static int[] GetBaseEggMoves(PKM pkm, int species, int form, GameVersio
}
break;
case PLA:
if (pkm.InhabitedGeneration(8))
{
int index = PersonalTable.LA.GetFormIndex(species, form);
return LevelUpLA[index].GetMoves(lvl);
}
break;
case BD or SP or BDSP:
if (pkm.InhabitedGeneration(8))
{
int index = PersonalTable.SWSH.GetFormIndex(species, form);
int index = PersonalTable.BDSP.GetFormIndex(species, form);
return LevelUpBDSP[index].GetMoves(lvl);
}
break;

View File

@@ -44,6 +44,7 @@ public static Learnset GetLearnset(GameVersion game, int species, int form)
SW or SH or SWSH => Legal.LevelUpSWSH,
BD or SP or BDSP => Legal.LevelUpBDSP,
PLA => Legal.LevelUpLA,
Gen1 => Legal.LevelUpY,
Gen2 => Legal.LevelUpC,
@@ -88,6 +89,7 @@ public static Learnset GetLearnset(GameVersion game, int species, int form)
SW or SH or SWSH => PersonalTable.SWSH,
BD or SP or BDSP => PersonalTable.BDSP,
PLA => PersonalTable.LA,
Gen1 => PersonalTable.Y,
Gen2 => PersonalTable.C,

View File

@@ -61,6 +61,7 @@ internal static int[] GetRelearnLVLMoves(PKM pkm, int species, int form, int lvl
US or UM => getMoves(LevelUpUSUM, PersonalTable.USUM),
SW or SH => getMoves(LevelUpSWSH, PersonalTable.SWSH),
BD or SP => getMoves(LevelUpBDSP, PersonalTable.BDSP),
PLA => getMoves(LevelUpLA, PersonalTable.LA),
_ => Array.Empty<int>(),
};
@@ -79,6 +80,7 @@ public static int[] GetSharedEggMoves(PKM pkm, int gen)
{
if (gen < 8 || pkm.IsEgg)
return Array.Empty<int>();
if (pkm.BDSP)
{
var table = PersonalTable.BDSP;

View File

@@ -9,6 +9,7 @@ namespace PKHeX.Core
public static class MoveLevelUp
{
private static readonly LearnLookup
LearnLA = new(PersonalTable.LA, LevelUpLA, PLA),
LearnBDSP = new(PersonalTable.BDSP, LevelUpBDSP, BDSP),
LearnSWSH = new(PersonalTable.SWSH, LevelUpSWSH, SWSH),
LearnSM = new(PersonalTable.SM, LevelUpSM, SM),
@@ -215,6 +216,11 @@ private static LearnVersion GetIsLevelUp8(int species, int form, int move, int m
return LearnNONE;
return LearnSWSH.GetIsLevelUp(species, form, move, maxLevel);
case PLA:
if (species > MaxSpeciesID_8a)
return LearnNONE;
return LearnLA.GetIsLevelUp(species, form, move, maxLevel);
case BD or SP or BDSP:
if (species > MaxSpeciesID_8b)
return LearnNONE;
@@ -478,6 +484,11 @@ private static List<int> AddMovesLevelUp8(List<int> moves, GameVersion ver, int
return moves;
return LearnSWSH.AddMoves(moves, species, form, maxLevel);
case PLA:
if (species > MaxSpeciesID_8a)
return moves;
return LearnLA.AddMoves(moves, species, form, maxLevel);
case BD or SP or BDSP:
if (species > MaxSpeciesID_8b)
return moves;
@@ -501,7 +512,8 @@ private static int[] GetEncounterMoves1(int species, int level, GameVersion vers
var lvl0 = (int[])((PersonalInfoG1) table[index]).Moves.Clone();
int start = Math.Max(0, Array.IndexOf(lvl0, 0));
return learn[index].GetEncounterMoves(level, lvl0, start);
learn[index].SetEncounterMoves(level, lvl0, start);
return lvl0;
}
private static int[] GetEncounterMoves2(int species, int level, GameVersion version)
@@ -512,7 +524,8 @@ private static int[] GetEncounterMoves2(int species, int level, GameVersion vers
var lvl0 = learn[species].GetEncounterMoves(1);
int start = Math.Max(0, Array.IndexOf(lvl0, 0));
return learn[index].GetEncounterMoves(level, lvl0, start);
learn[index].SetEncounterMoves(level, lvl0, start);
return lvl0;
}
public static int[] GetEncounterMoves(int species, int form, int level, GameVersion version)

View File

@@ -245,7 +245,7 @@ private static GameVersion GetIsMachine8(int species, int move, int form, GameVe
if (GameVersion.BDSP.Contains(ver))
{
for (int i = 0; i < PersonalInfoSWSH.CountTM; i++)
for (int i = 0; i < PersonalInfoBDSP.CountTM; i++)
{
if (Legal.TMHM_BDSP[i] != move)
continue;
@@ -272,7 +272,7 @@ private static GameVersion GetIsRecord8(PKM pkm, int species, int move, int form
break;
if (allowBit)
return GameVersion.SWSH;
if (((G8PKM)pkm).GetMoveRecordFlag(i))
if (((ITechRecord8)pkm).GetMoveRecordFlag(i))
return GameVersion.SWSH;
if (i == 12 && species == (int)Species.Calyrex && form == 0) // TR12
return GameVersion.SWSH; // Agility Calyrex without TR glitch.
@@ -421,7 +421,7 @@ private static void AddMachine8(List<int> r, int species, int form, GameVersion
case GameVersion.Any:
case GameVersion.SW or GameVersion.SH or GameVersion.SWSH:
AddMachineSWSH(r, species, form);
break;
return;
case GameVersion.BD or GameVersion.SP or GameVersion.BDSP:
AddMachineBDSP(r, species, form);
return;

View File

@@ -153,6 +153,17 @@ private static GameVersion GetIsTutor7(PKM pkm, int species, int form, bool spec
private static GameVersion GetIsTutor8(PKM pkm, int species, int form, bool specialTutors, int move)
{
if (pkm.LA)
{
var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(species, form);
if (!pi.IsPresentInGame)
return NONE;
var index = Array.IndexOf(MoveShop8_LA, move);
if (index != -1 && pi.SpecialTutors[0][index])
return GameVersion.PLA;
return NONE;
}
if (pkm.BDSP)
{
var pi = (PersonalInfoBDSP)PersonalTable.BDSP.GetFormEntry(species, form);
@@ -267,6 +278,13 @@ private static void AddMovesTutor7(List<int> moves, int species, int form, PKM p
private static void AddMovesTutor8(List<int> moves, int species, int form, PKM pkm, bool specialTutors)
{
if (pkm.LA)
{
var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(species, form);
if (!pi.IsPresentInGame)
return;
moves.AddRange(MoveShop8_LA.Where((_, i) => pi.SpecialTutors[0][i]));
}
if (pkm.BDSP)
{
var pi = (PersonalInfoBDSP)PersonalTable.BDSP.GetFormEntry(species, form);

View File

@@ -85,6 +85,20 @@ internal static class EvolutionRestrictions
new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 35 }, // Grapploct (Clobbopus with Taunt)
};
private static readonly byte[] MinLevelEvolutionWithMove_8LA =
{
00, // Sylveon (Eevee with Fairy Move)
25, // Mr. Mime (Mime Jr with Mimic)
29, // Sudowoodo (Bonsly with Mimic)
25, // Ambipom (Aipom with Double Hit)
34, // Lickilicky (Lickitung with Rollout)
34, // Tangrowth (Tangela with Ancient Power)
34, // Yanmega (Yanma with Ancient Power)
34, // Mamoswine (Piloswine with Ancient Power)
99, // Tsareena (Steenee with Stomp)
99, // Grapploct (Clobbopus with Taunt)
};
private static readonly bool[][] CanEggHatchWithEvolveMove =
{
new [] { false, false, true, true, true, true, true, true, true }, // Sylveon (Eevee with Fairy Move)
@@ -153,6 +167,9 @@ public static bool IsValidEvolutionWithMove(PKM pkm, LegalInfo info)
private static int GetMinLevelKnowRequiredMove(PKM pkm, int gen, int index)
{
if (gen == 8 && pkm.LA) // No Level Up required, and different levels than mainline SW/SH.
return MinLevelEvolutionWithMove_8LA[index];
var lvl = GetLevelLearnMove(pkm, gen, index);
// If has original met location the minimum evolution level is one level after met level

View File

@@ -45,6 +45,7 @@ public static bool IsHeldItemAllowed(int item, int generation, PKM pk)
5 => ReleasedHeldItems_5,
6 => ReleasedHeldItems_6,
7 => ReleasedHeldItems_7,
8 when pk is PA8 => Array.Empty<bool>(),
8 when pk is PB8 => ReleasedHeldItems_8b,
8 => ReleasedHeldItems_8,
_ => Array.Empty<bool>(),

View File

@@ -61,7 +61,7 @@ private static EggMoves6 Get(ReadOnlySpan<byte> data)
return new EggMoves6(moves);
}
public static EggMoves6[] GetArray(byte[][] entries)
public static EggMoves6[] GetArray(BinLinkerAccessor entries)
{
EggMoves6[] data = new EggMoves6[entries.Length];
for (int i = 0; i < data.Length; i++)
@@ -92,7 +92,7 @@ private static EggMoves7 Get(ReadOnlySpan<byte> data)
return new EggMoves7(moves, formIndex);
}
public static EggMoves7[] GetArray(byte[][] entries)
public static EggMoves7[] GetArray(BinLinkerAccessor entries)
{
EggMoves7[] data = new EggMoves7[entries.Length];
for (int i = 0; i < data.Length; i++)

View File

@@ -100,25 +100,28 @@ public static bool IsFormChangeable(int species, int oldForm, int newForm, int f
private static readonly HashSet<int> FormChange = new()
{
// Sometimes considered for wild encounters
412, // Burmy
479, // Rotom
676, // Furfrou
741, // Oricorio
(int)Burmy,
(int)Rotom,
(int)Furfrou,
(int)Oricorio,
386, // Deoxys
487, // Giratina
492, // Shaymin
493, // Arceus
641, // Tornadus
642, // Thundurus
645, // Landorus
646, // Kyurem
647, // Keldeo
649, // Genesect
720, // Hoopa
773, // Silvally
800, // Necrozma
898, // Calyrex
(int)Deoxys,
(int)Dialga,
(int)Palkia,
(int)Giratina,
(int)Shaymin,
(int)Arceus,
(int)Tornadus,
(int)Thundurus,
(int)Landorus,
(int)Kyurem,
(int)Keldeo,
(int)Genesect,
(int)Hoopa,
(int)Silvally,
(int)Necrozma,
(int)Calyrex,
(int)Enamorus,
};
/// <summary>
@@ -234,6 +237,24 @@ public static int GetTotemBaseForm(int species, int form)
return form - 1;
}
public static bool IsLordForm(int species, int form, int generation)
{
if (generation != 8)
return false;
return IsLordForm(species, form);
}
private static bool IsLordForm(int species, int form) => form != 0 && species switch
{
(int)Arcanine when form == 2 => true,
(int)Electrode when form == 2 => true,
(int)Lilligant when form == 2 => true,
(int)Avalugg when form == 2 => true,
(int)Kleavor when form == 1 => true,
_ => false,
};
/// <summary>
/// Checks if the <see cref="form"/> exists for the <see cref="species"/> without having an associated <see cref="PersonalInfo"/> index.
/// </summary>

View File

@@ -32,6 +32,7 @@ public static partial class Legal
{(int)Thundurus,(g, _) => g >= 6},
{(int)Landorus, (g, _) => g >= 6},
{(int)Urshifu, (g, _) => g >= 8},
{(int)Enamorus, (g, _) => g >= 8},
// Fused
{(int)Kyurem, (g, _) => g >= 6},
@@ -127,8 +128,14 @@ public static bool IsValidSketch(int move, int generation)
return false;
if (generation is 6 && move is ((int)ThousandArrows or (int)ThousandWaves))
return false;
if (generation is 8 && (SignatureSketch_BDSP.Contains(move) || DummiedMoves_BDSP.Contains(move))) // can't Sketch unusable moves in BDSP
return false;
if (generation is 8) // can't Sketch unusable moves in BDSP, no Sketch in PLA
{
if (SignatureSketch_BDSP.Contains(move) || DummiedMoves_BDSP.Contains(move))
return false;
if (move > MaxMoveID_8)
return false;
}
return move <= GetMaxMoveID(generation);
}
@@ -177,7 +184,7 @@ public static bool IsValidSketch(int move, int generation)
(int)TypeNull, (int)Silvally, (int)TapuKoko, (int)TapuLele, (int)TapuBulu, (int)TapuFini,
(int)Nihilego, (int)Buzzwole, (int)Pheromosa, (int)Xurkitree, (int)Celesteela, (int)Kartana, (int)Guzzlord,
(int)Poipole, (int)Naganadel, (int)Stakataka, (int)Blacephalon,
(int)Kubfu, (int)Urshifu, (int)Regieleki, (int)Regidrago, (int)Glastrier, (int)Spectrier,
(int)Kubfu, (int)Urshifu, (int)Regieleki, (int)Regidrago, (int)Glastrier, (int)Spectrier, (int)Enamorus,
};
/// <summary>

View File

@@ -0,0 +1,309 @@
using System.Collections.Generic;
namespace PKHeX.Core
{
public static partial class Legal
{
internal const int MaxSpeciesID_8a = (int)Species.Enamorus;
internal const int MaxMoveID_8a = (int)Move.TakeHeart;
internal const int MaxItemID_8a = 1828; // Legend Plate
internal const int MaxBallID_8a = (int)Ball.LAOrigin;
internal const int MaxGameID_8a = (int)GameVersion.SP;
internal const int MaxAbilityID_8a = MaxAbilityID_8_R2;
#region Met Locations
internal static readonly int[] Met_LA_0 =
{
000, 002, 004, 006, 007, 008, 009,
010, 011, 012, 013, 014, 015, 016, 017, 018, 019,
020, 021, 022, 023, 024, 025, 026, 027, 028, 029,
030, 031, 032, 033, 034, 035, 036, 037, 038, 039,
040, 041, 042, 043, 045, 046, 047, 048, 049,
050, 051, 052, 053, 054, 055, 056, 057, 058, 059,
060, 061, 063, 064, 065, 066, 067, 068, 069,
070, 071, 072, 073, 074, 075, 076, 077, 079,
080, 081, 082, 083, 084, 085, 086, 087, 088, 089,
090, 092, 093, 094, 095, 096, 097, 098, 099,
100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155,
};
internal static readonly int[] Met_LA_3 =
{
30001, 30002, 30003, 30004, 30005, 30006, 30007, 30008, 30009, 30010, 30011, 30012, 30013, 30014, 30015, 30016, 30017, 30018, 30019, 30020, 30021, 30022,
};
internal static readonly int[] Met_LA_4 =
{
40001, 40002, 40003, 40005, 40006, 40007, 40008, 40009,
40010, 40011, 40012, 40013, 40014, 40016, 40017, 40018, 40019,
40020, 40021, 40022, 40024, 40025, 40026, 40027, 40028, 40029,
40030, 40032, 40033, 40034, 40035, 40036, 40037, 40038, 40039,
40040, 40041, 40042, 40043, 40044, 40045, 40047, 40048, 40049,
40050, 40051, 40052, 40053, 40055, 40056, 40057, 40058, 40059,
40060, 40061, 40063, 40064, 40065, 40066, 40067, 40068, 40069,
40070, 40071, 40072, 40074, 40075, 40076, 40077, 40078, 40079,
40080, 40081, 40082, 40083, 40084, 40085, 40086,
};
internal static readonly int[] Met_LA_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004 };
#endregion
internal static readonly ushort[] Pouch_Items_LA =
{
017, 023, 024, 025, 026, 027, 028, 029, 039, 041,
050, 054, 072, 073, 075, 080, 081, 082, 083, 084,
085, 090, 091, 092, 107, 108, 109, 110, 149, 150,
151, 152, 153, 154, 155, 157, 158, 159, 160, 161,
162, 163, 164, 166, 168, 233, 252, 321, 322, 323,
324, 325, 326, 327, 583, 849,
1125, 1126, 1127, 1128, 1231, 1232, 1233, 1234, 1235, 1236,
1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246,
1247, 1248, 1249, 1250, 1251,
1611, 1613, 1614, 1615, 1616, 1617, 1618, 1619, 1620, 1621,
1628, 1630, 1631, 1632, 1633, 1634, 1635, 1636, 1637, 1638,
1651, 1679, 1681, 1682, 1684, 1686, 1687, 1688, 1689, 1690,
1691, 1692, 1693, 1694, 1695, 1696, 1699, 1700, 1701, 1702,
1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712,
1713, 1716, 1717, 1720, 1724, 1725, 1726, 1727, 1728, 1732,
1733, 1734, 1735, 1736, 1738, 1739, 1740, 1741, 1742, 1746,
1747, 1748, 1749, 1750, 1754, 1755, 1756, 1757, 1758, 1759,
1760, 1761, 1762, 1764, 1785,
};
internal static readonly ushort[] Pouch_Recipe_LA =
{
1640, 1641, 1642, 1643, 1644, 1646, 1647, 1648, 1649,
1650, 1652, 1653, 1654, 1655, 1656, 1657, 1658, 1659,
1660, 1661, 1662, 1663, 1664, 1665, 1666, 1667, 1668, 1669,
1670, 1671, 1673, 1674, 1675, 1676, 1677,
1729,
1730, 1731,
1751, 1752, 1753,
1783, 1784,
};
internal static readonly ushort[] Pouch_Key_LA =
{
111,
298, 299,
300, 301, 302, 303, 304, 305, 306, 307, 308, 309,
310, 311, 312, 313,
441, 455, 466,
632, 638, 644,
1608, 1609, 1610, 1612, 1622, 1624, 1625, 1626, 1627, 1629,
1639, 1678, 1721, 1722, 1723, 1737, 1743, 1744, 1745, 1763,
1765, 1766, 1767, 1768, 1769, 1771, 1776, 1777, 1778, 1779,
1780, 1782, 1786, 1787, 1788, 1789, 1790, 1792, 1793, 1794,
1795, 1796, 1797, 1798, 1799, 1800, 1801, 1802, 1803, 1804,
1805, 1806, 1807,
1828,
};
internal static readonly ushort[] HeldItems_LA = { 0 };
internal static readonly HashSet<int> HisuiOriginForms = new()
{
(int)Species.Sneasel,
(int)Species.Growlithe,
(int)Species.Arcanine,
(int)Species.Voltorb,
(int)Species.Electrode,
(int)Species.Qwilfish,
(int)Species.Sliggoo,
(int)Species.Goodra,
};
internal static readonly IReadOnlyDictionary<int, int> HisuiForm0Evolutions = new Dictionary<int, int>
{
{(int)Species.Sneasler, 1},
};
internal static readonly HashSet<int> HisuiVariantFormEvolutions = new()
{
(int)Species.Decidueye,
(int)Species.Typhlosion,
(int)Species.Samurott,
(int)Species.Lilligant,
(int)Species.Braviary,
(int)Species.Avalugg,
};
#region Moves
internal static readonly int[] MoveShop8_LA =
{
(int)Move.FalseSwipe,
(int)Move.FireFang,
(int)Move.ThunderFang,
(int)Move.IceFang,
(int)Move.IceBall,
(int)Move.RockSmash,
(int)Move.Spikes,
(int)Move.Bulldoze,
(int)Move.AerialAce,
(int)Move.StealthRock,
(int)Move.Swift,
(int)Move.TriAttack,
(int)Move.MagicalLeaf,
(int)Move.OminousWind,
(int)Move.PowerShift,
(int)Move.FocusEnergy,
(int)Move.BulkUp,
(int)Move.CalmMind,
(int)Move.Rest,
(int)Move.BabyDollEyes,
(int)Move.FirePunch,
(int)Move.ThunderPunch,
(int)Move.IcePunch,
(int)Move.DrainPunch,
(int)Move.PoisonJab,
(int)Move.PsychoCut,
(int)Move.ZenHeadbutt,
(int)Move.LeechLife,
(int)Move.XScissor,
(int)Move.RockSlide,
(int)Move.ShadowClaw,
(int)Move.IronHead,
(int)Move.IronTail,
(int)Move.MysticalFire,
(int)Move.WaterPulse,
(int)Move.ChargeBeam,
(int)Move.EnergyBall,
(int)Move.IcyWind,
(int)Move.SludgeBomb,
(int)Move.EarthPower,
(int)Move.ShadowBall,
(int)Move.Snarl,
(int)Move.FlashCannon,
(int)Move.DazzlingGleam,
(int)Move.GigaImpact,
(int)Move.AquaTail,
(int)Move.WildCharge,
(int)Move.HighHorsepower,
(int)Move.Megahorn,
(int)Move.StoneEdge,
(int)Move.Outrage,
(int)Move.PlayRough,
(int)Move.HyperBeam,
(int)Move.Flamethrower,
(int)Move.Thunderbolt,
(int)Move.IceBeam,
(int)Move.Psychic,
(int)Move.DarkPulse,
(int)Move.DracoMeteor,
(int)Move.SteelBeam,
(int)Move.VoltTackle,
};
internal static readonly byte[] MovePP_LA =
{
00,
35, 25, 10, 15, 20, 20, 10, 10, 10, 35, 30, 05, 10, 20, 30, 25, 35, 20, 15, 20, 20, 25, 20, 30, 05, 10, 15, 15, 15, 25, 20, 05, 30, 15, 20, 20, 10, 05, 30, 20, 20, 20, 30, 20, 40, 20, 15, 20, 20, 20,
30, 25, 10, 30, 25, 05, 15, 10, 05, 20, 20, 20, 05, 35, 20, 20, 20, 20, 20, 15, 20, 15, 10, 20, 25, 10, 20, 20, 20, 10, 40, 10, 15, 25, 10, 20, 05, 15, 10, 05, 10, 10, 20, 10, 20, 40, 30, 20, 20, 20,
15, 10, 40, 15, 10, 30, 10, 20, 10, 40, 40, 20, 30, 30, 20, 20, 10, 10, 20, 05, 10, 30, 20, 20, 20, 05, 15, 15, 20, 10, 15, 35, 20, 15, 10, 10, 30, 15, 20, 20, 10, 10, 05, 10, 25, 10, 10, 20, 15, 40,
20, 10, 05, 15, 10, 10, 10, 15, 30, 30, 10, 10, 15, 10, 01, 01, 10, 25, 10, 05, 15, 20, 15, 10, 15, 30, 05, 40, 15, 10, 25, 10, 20, 10, 20, 10, 10, 10, 20, 15, 20, 05, 40, 05, 05, 20, 05, 10, 05, 10,
10, 10, 10, 20, 20, 30, 15, 10, 20, 20, 25, 05, 15, 10, 05, 20, 15, 20, 25, 20, 05, 30, 05, 05, 20, 40, 05, 20, 40, 20, 05, 35, 10, 05, 05, 05, 15, 05, 25, 05, 05, 10, 20, 10, 05, 15, 10, 10, 20, 15,
10, 10, 10, 20, 10, 10, 10, 10, 15, 15, 15, 10, 20, 20, 10, 20, 20, 20, 20, 20, 10, 10, 10, 20, 20, 05, 15, 10, 10, 15, 10, 20, 05, 05, 10, 10, 20, 05, 10, 20, 10, 20, 20, 20, 05, 05, 15, 20, 10, 15,
20, 15, 10, 10, 15, 10, 05, 05, 10, 25, 10, 05, 20, 15, 05, 40, 15, 15, 40, 15, 20, 20, 05, 15, 20, 15, 15, 15, 05, 10, 30, 20, 30, 20, 05, 40, 10, 05, 10, 05, 15, 25, 25, 05, 20, 15, 10, 10, 20, 10,
20, 20, 05, 05, 10, 05, 40, 10, 10, 05, 10, 10, 15, 10, 20, 15, 30, 10, 20, 05, 10, 10, 15, 10, 10, 05, 15, 05, 10, 10, 30, 20, 20, 10, 10, 05, 05, 10, 05, 20, 10, 20, 10, 05, 10, 10, 20, 10, 10, 15,
10, 15, 10, 10, 10, 10, 10, 10, 10, 30, 05, 10, 05, 10, 10, 05, 20, 20, 10, 20, 15, 15, 15, 15, 20, 15, 15, 10, 10, 10, 20, 15, 05, 05, 15, 15, 05, 10, 05, 15, 05, 10, 20, 05, 20, 20, 20, 20, 05, 20,
15, 05, 20, 15, 10, 10, 05, 10, 05, 05, 10, 05, 05, 10, 05, 15, 05, 15, 10, 10, 10, 10, 10, 15, 15, 20, 15, 10, 15, 10, 15, 10, 20, 10, 10, 10, 20, 20, 20, 20, 20, 15, 15, 15, 15, 15, 15, 20, 15, 10,
15, 15, 15, 15, 10, 15, 10, 10, 10, 15, 15, 15, 15, 05, 05, 15, 05, 10, 10, 10, 20, 20, 20, 10, 10, 30, 15, 10, 10, 15, 25, 10, 15, 10, 10, 10, 20, 10, 10, 10, 10, 05, 15, 15, 05, 05, 10, 10, 10, 05,
05, 10, 05, 05, 15, 10, 05, 05, 05, 10, 10, 10, 10, 20, 25, 10, 20, 30, 25, 20, 20, 15, 20, 15, 20, 20, 15, 10, 10, 10, 10, 20, 10, 25, 10, 10, 10, 10, 20, 20, 05, 05, 05, 20, 10, 10, 20, 15, 20, 20,
10, 20, 30, 10, 10, 40, 40, 20, 20, 40, 20, 20, 10, 10, 10, 10, 05, 10, 10, 05, 05, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01, 01,
01, 01, 01, 01, 01, 01, 01, 01, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 25, 15, 20, 30, 20, 15, 15, 20, 10, 15, 15, 10, 05, 10, 10, 20, 15, 10, 15, 15, 15, 05, 15, 20, 20, 01, 01, 01, 01, 01, 01,
01, 01, 01, 05, 05, 10, 10, 10, 20, 10, 10, 10, 05, 05, 20, 10, 10, 10, 01, 05, 15, 05, 01, 01, 01, 01, 01, 01, 10, 15, 15, 20, 20, 20, 20, 15, 15, 10, 10, 05, 20, 05, 10, 05, 15, 10, 10, 05, 15, 20,
10, 10, 15, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 05, 10, 15, 10, 15, 05, 05, 05, 10, 15, 40, 10, 10, 10, 15, 10, 10, 10, 10, 05, 05, 05, 10, 05, 20, 10,
10, 05, 20, 20, 10, 10, 05, 05, 05, 40, 10, 20, 10, 10, 10, 10, 05, 05, 15, 05, 10, 10, 10, 05, 05, 35, 15, 10, 10, 15, 05, 10, 10, 10, 05, 05, 10, 05, 15, 10, 15, 10, 15, 15, 15, 05, 05, 05, 10, 10,
};
/// <summary>
/// Moves that are kill
/// </summary>
public static readonly HashSet<int> DummiedMoves_LA = new()
{
001, 002, 003, 004, 005, 006, 010, 011, 012, 013,
015, 017, 018, 019, 020, 021, 022, 023, 024, 025,
026, 027, 028, 029, 030, 031, 032, 034, 035, 036,
037, 039, 041, 043, 045, 046, 047, 048, 049, 050,
051, 054, 055, 057, 060, 061, 062, 064, 065, 066,
067, 068, 069, 070, 072, 073, 074, 075, 076, 081,
082, 083, 088, 089, 090, 091, 092, 096, 097, 099,
101, 103, 104, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 117, 118, 119, 121, 122, 123, 124,
125, 127, 128, 130, 131, 132, 133, 134, 136, 137,
138, 140, 142, 143, 144, 146, 148, 149, 152, 153,
154, 155, 158, 159, 160, 162, 164, 166, 167, 168,
169, 170, 171, 173, 174, 175, 176, 177, 178, 179,
180, 182, 184, 185, 186, 187, 192, 193, 194, 195,
197, 198, 199, 201, 202, 203, 204, 207, 208, 210,
211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
221, 222, 223, 225, 226, 227, 228, 229, 230, 232,
233, 234, 235, 236, 238, 240, 241, 243, 244, 245,
248, 250, 251, 252, 253, 254, 255, 256, 257, 258,
259, 260, 261, 262, 263, 264, 265, 266, 267, 268,
269, 270, 271, 272, 273, 274, 275, 276, 277, 278,
279, 280, 281, 282, 283, 284, 285, 286, 287, 288,
289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
299, 300, 302, 303, 304, 305, 306, 307, 308, 309,
311, 312, 313, 316, 317, 319, 320, 321, 322, 323,
324, 325, 327, 328, 329, 330, 331, 333, 335, 336,
338, 340, 341, 342, 343, 346, 349, 350, 351, 353,
354, 356, 357, 358, 359, 360, 361, 362, 363, 364,
365, 366, 367, 368, 369, 371, 372, 373, 374, 375,
376, 377, 378, 379, 380, 381, 382, 383, 384, 385,
386, 387, 388, 389, 390, 391, 392, 393, 395, 397,
402, 407, 410, 411, 415, 419, 429, 431, 432, 433,
435, 436, 438, 439, 441, 443, 445, 447, 448, 450,
454, 455, 456, 461, 468, 469, 470, 471, 472, 473,
475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
485, 486, 487, 488, 489, 490, 492, 493, 494, 495,
496, 497, 498, 499, 500, 501, 502, 503, 504, 505,
507, 508, 509, 510, 511, 512, 513, 514, 515, 516,
517, 518, 519, 520, 521, 524, 525, 526, 527, 529,
530, 531, 532, 533, 534, 535, 536, 537, 538, 539,
540, 541, 543, 544, 545, 546, 547, 548, 549, 550,
551, 552, 553, 554, 557, 558, 559, 560, 561, 562,
563, 564, 565, 566, 567, 568, 569, 570, 571, 572,
573, 574, 575, 576, 578, 579, 580, 581, 582, 586,
587, 588, 589, 590, 591, 592, 593, 594, 596, 597,
598, 599, 600, 601, 602, 603, 604, 606, 607, 609,
610, 611, 612, 613, 614, 615, 616, 617, 618, 619,
620, 621, 622, 623, 624, 625, 626, 627, 628, 629,
630, 631, 632, 633, 634, 635, 636, 637, 638, 639,
640, 641, 642, 643, 644, 645, 646, 647, 648, 649,
650, 651, 652, 653, 654, 655, 656, 657, 658, 659,
660, 661, 662, 663, 664, 665, 666, 668, 669, 671,
672, 673, 674, 675, 676, 677, 678, 679, 680, 681,
682, 683, 684, 685, 686, 687, 688, 689, 690, 691,
692, 693, 694, 695, 696, 697, 698, 699, 700, 701,
702, 703, 704, 705, 706, 707, 708, 709, 711, 712,
713, 714, 715, 716, 717, 718, 719, 720, 721, 722,
723, 724, 725, 726, 727, 728, 729, 730, 731, 732,
733, 734, 735, 736, 737, 738, 739, 740, 741, 742,
743, 744, 745, 746, 747, 748, 749, 750, 751, 752,
753, 754, 755, 756, 757, 758, 759, 760, 761, 762,
763, 764, 765, 766, 767, 768, 769, 770, 771, 772,
773, 774, 775, 776, 777, 778, 779, 780, 781, 782,
783, 784, 785, 786, 787, 788, 789, 790, 791, 792,
793, 794, 795, 797, 798, 799, 800, 801, 802, 803,
804, 805, 806, 807, 808, 809, 810, 811, 812, 813,
814, 815, 816, 817, 818, 819, 820, 821, 822, 823,
824, 825, 826,
};
#endregion
}
}

View File

@@ -68,7 +68,7 @@ private CheckResult VerifyAbility(LegalityAnalysis data)
if (format >= 8) // Ability Patch
{
if (pkm.AbilityNumber == 4)
if (pkm.AbilityNumber == 4 && !pkm.LA)
{
if (CanAbilityPatch(format, abilities, pkm.Species))
return GetValid(LAbilityPatchUsed);
@@ -452,6 +452,8 @@ private static bool IsAbilityCapsuleModified(PKM pkm, IReadOnlyList<int> abiliti
return false;
if (pkm.AbilityNumber == 4)
return false; // Cannot alter to hidden ability.
if (pkm.LA)
return false; // Not available.
if (encounterAbility == AbilityPermission.OnlyHidden)
return false; // Cannot alter from hidden ability.
return true;
@@ -481,6 +483,7 @@ public static bool CanAbilityPatch(int format, IReadOnlyList<int> abilities, int
(int)Species.Tornadus => true, // Form-0 is a/a/h
(int)Species.Thundurus => true, // Form-0 is a/a/h
(int)Species.Landorus => true, // Form-0 is a/a/h
(int)Species.Enamorus => true, // Form-0 is a/a/h
_ => false,
};
}

View File

@@ -16,6 +16,7 @@ internal static class BallUseLegality
6 => WildPokeballs6,
7 => GameVersion.Gen7b.Contains(game) ? WildPokeballs7b : WildPokeballs7,
8 when GameVersion.BDSP.Contains(game) => WildPokeBalls4_HGSS,
8 when GameVersion.PLA == game => WildPokeBalls8a,
8 => GameVersion.GO == game ? WildPokeballs8g : WildPokeballs8,
_ => Array.Empty<int>(),
};
@@ -66,5 +67,20 @@ internal static class BallUseLegality
(int)Sport,
// no cherish ball
};
private static readonly HashSet<int> WildPokeBalls8a = new()
{
(int)LAPoke,
(int)LAGreat,
(int)LAUltra,
(int)LAFeather,
(int)LAWing,
(int)LAJet,
(int)LAHeavy,
(int)LALeaden,
(int)LAGigaton,
};
}
}

View File

@@ -42,12 +42,6 @@ private CheckResult VerifyForm(LegalityAnalysis data)
if (!PersonalInfo.IsFormWithinRange(form) && !FormInfo.IsValidOutOfBoundsForm(species, form, Info.Generation))
return GetInvalid(string.Format(LFormInvalidRange, count - 1, form));
switch (enc)
{
case EncounterEgg e when FormInfo.IsTotemForm(species, form, e.Generation):
return GetInvalid(LFormInvalidGame);
}
switch ((Species)species)
{
case Pikachu when Info.Generation == 6: // Cosplay
@@ -72,6 +66,8 @@ private CheckResult VerifyForm(LegalityAnalysis data)
break;
case Unown when Info.Generation == 2 && form >= 26:
return GetInvalid(string.Format(LFormInvalidRange, "Z", form == 26 ? "!" : "?"));
case Dialga or Palkia or Giratina or Arceus when form > 0 && pkm.LA: // can change forms with key items
break;
case Giratina when form == 1 ^ pkm.HeldItem == 112: // Giratina, Origin form only with Griseous Orb
return GetInvalid(LFormItemInvalid);
@@ -279,6 +275,57 @@ private CheckResult VerifyFormArgument(LegalityAnalysis data, IFormArgument f)
> (uint) AlcremieDecoration.Ribbon => GetInvalid(LFormArgumentHigh),
_ => GetValid(LFormArgumentValid),
},
Overqwil when enc.Species == (int)Overqwil => arg switch
{
not 0 => GetInvalid(LFormArgumentNotAllowed),
_ => GetValid(LFormArgumentValid),
},
Wyrdeer when enc.Species == (int)Wyrdeer => arg switch
{
not 0 => GetInvalid(LFormArgumentNotAllowed),
_ => GetValid(LFormArgumentValid),
},
Basculegion when enc.Species == (int)Basculegion => arg switch
{
not 0 => GetInvalid(LFormArgumentNotAllowed),
_ => GetValid(LFormArgumentValid),
},
Basculin when pkm.Form is 2 => arg switch
{
not 0 when pkm.IsEgg => GetInvalid(LFormArgumentNotAllowed),
> 9_999 => GetInvalid(LFormArgumentHigh),
_ => GetValid(LFormArgumentValid),
},
Qwilfish when pkm.Form is 1 => arg switch
{
not 0 when pkm.IsEgg => GetInvalid(LFormArgumentNotAllowed),
> 9_999 => GetInvalid(LFormArgumentHigh),
_ => GetValid(LFormArgumentValid),
},
Stantler when pkm is PA8 => arg switch
{
not 0 when pkm.IsEgg => GetInvalid(LFormArgumentNotAllowed),
> 9_999 => GetInvalid(LFormArgumentHigh),
_ => GetValid(LFormArgumentValid),
},
Wyrdeer => arg switch // From Stantler
{
< 20 => GetInvalid(LFormArgumentLow),
> 9_999 => GetInvalid(LFormArgumentHigh),
_ => GetValid(LFormArgumentValid),
},
Overqwil => arg switch // From Qwilfish-1
{
< 20 => GetInvalid(LFormArgumentLow),
> 9_999 => GetInvalid(LFormArgumentHigh),
_ => GetValid(LFormArgumentValid),
},
Basculegion => arg switch // From Basculin-2
{
< 294 => GetInvalid(LFormArgumentLow),
> 9_999 => GetInvalid(LFormArgumentHigh),
_ => GetValid(LFormArgumentValid),
},
_ => VerifyFormArgumentNone(pkm, f),
};
}

View File

@@ -187,6 +187,7 @@ public static bool GetCanOTHandle(IEncounterTemplate enc, PKM pkm, int generatio
WC7 wc7 when wc7.OT_Name.Length > 0 && wc7.TID != 18075 => false, // Ash Pikachu QR Gift doesn't set Current Handler
WC8 wc8 when wc8.GetHasOT(pkm.Language) => false,
WB8 wb8 when wb8.GetHasOT(pkm.Language) => false,
WA8 wa8 when wa8.GetHasOT(pkm.Language) => false,
WC8 {IsHOMEGift: true} => false,
_ => true,
};

View File

@@ -18,6 +18,12 @@ public override void Verify(LegalityAnalysis data)
if (!t.IsHyperTrained())
return;
if (!t.IsHyperTrainingAvailable())
{
data.AddLine(GetInvalid(LHyperPerfectUnavailable));
return;
}
if (pkm.CurrentLevel != 100)
{
data.AddLine(GetInvalid(LHyperBelow100));

View File

@@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core;
/// <summary>
/// Verifies the stat details of data that has not yet left <see cref="GameVersion.PLA"/>.
/// </summary>
public sealed class LegendsArceusVerifier : Verifier
{
protected override CheckIdentifier Identifier => CheckIdentifier.RelearnMove;
public override void Verify(LegalityAnalysis data)
{
var pk = data.pkm;
if (!pk.LA || pk is not PA8 pa)
return;
CheckLearnset(data, pa);
CheckMastery(data, pa);
if (pa.IsNoble)
data.AddLine(GetInvalid(LStatNobleInvalid));
if (pa.IsAlpha != data.EncounterMatch is IAlpha { IsAlpha: true })
data.AddLine(GetInvalid(LStatAlphaInvalid));
CheckScalars(data, pa);
}
private void CheckScalars(LegalityAnalysis data, PA8 pa)
{
// Static encounters hard-match the Height & Weight; only slots are unchecked for Alpha Height/Weight.
if (pa.IsAlpha && data.EncounterMatch is EncounterSlot8a)
{
if (pa.HeightScalar != 255)
data.AddLine(GetInvalid(LStatIncorrectHeightValue));
if (pa.WeightScalar != 255)
data.AddLine(GetInvalid(LStatIncorrectWeightValue));
}
// No way to mutate the display height scalar value. Must match!
if (pa.HeightScalar != pa.HeightScalarCopy)
data.AddLine(GetInvalid(LStatIncorrectHeightCopy, CheckIdentifier.Encounter));
}
private static void CheckLearnset(LegalityAnalysis data, PA8 pa)
{
var moveCount = GetMoveCount(pa);
if (moveCount == 4)
return;
// Get the bare minimum moveset.
Span<int> expect = stackalloc int[4];
var minMoveCount = LoadBareMinimumMoveset(data.EncounterMatch, data.Info.EvoChainsAllGens[8], pa, expect);
// Flag move slots that are empty.
for (int i = moveCount; i < minMoveCount; i++)
{
// Expected move should never be empty, but just future-proof against any revisions.
var msg = expect[i] != 0 ? string.Format(LMoveFExpect_0, ParseSettings.MoveStrings[expect[i]]) : LMoveSourceEmpty;
data.Info.Moves[i] = new CheckMoveResult(data.Info.Moves[i], Severity.Invalid, msg, CheckIdentifier.CurrentMove);
}
}
/// <summary>
/// Gets the expected minimum count of moves, and modifies the input <see cref="moves"/> with the bare minimum move IDs.
/// </summary>
private static int LoadBareMinimumMoveset(ISpeciesForm enc, IReadOnlyList<EvoCriteria> evos, PA8 pa, Span<int> moves)
{
// Get any encounter moves
var pt = PersonalTable.LA;
var index = pt.GetFormIndex(enc.Species, enc.Form);
var moveset = Legal.LevelUpLA[index];
moveset.SetEncounterMoves(pa.Met_Level, moves);
var count = moves.IndexOf(0);
if ((uint)count >= 4)
return 4;
// Level up to current level
moveset.SetLevelUpMoves(pa.Met_Level, pa.CurrentLevel, moves, count);
count = moves.IndexOf(0);
if ((uint)count >= 4)
return 4;
// Evolve and try
for (int i = 0; i < evos.Count - 1; i++)
{
var (species, form) = evos[i];
index = pt.GetFormIndex(species, form);
moveset = Legal.LevelUpLA[index];
moveset.SetEvolutionMoves(moves, count);
count = moves.IndexOf(0);
if ((uint)count >= 4)
return 4;
}
// Any tutored moves we don't know about??
return AddMasteredMissing(pa, moves, count);
}
private static int AddMasteredMissing(PA8 pa, Span<int> current, int ctr)
{
for (int i = 0; i < pa.MoveShopPermitIndexes.Length; i++)
{
// Buying the move tutor grants access, but does not learn the move.
// Mastering requires the move to be present in the movepool.
if (!pa.GetMasteredRecordFlag(i))
continue;
// Purchased moves can be swapped with existing moves; we're only interested in special granted moves.
if (pa.GetPurchasedRecordFlag(i))
continue;
var move = pa.MoveShopPermitIndexes[i];
if (current.IndexOf(move) == -1)
current[ctr++] = move;
if (ctr == 4)
return 4;
}
return ctr;
}
private static int GetMoveCount(PKM pa)
{
var count = 0;
for (int i = 0; i < 4; i++)
{
if (pa.GetMove(i) is not 0)
count++;
}
return count;
}
private void CheckMastery(LegalityAnalysis data, PA8 pa)
{
var bits = pa.MoveShopPermitFlags;
var moves = pa.MoveShopPermitIndexes;
var alphaMove = pa.AlphaMove;
if (alphaMove is not 0)
VerifyAlphaMove(data, pa, alphaMove, moves, bits);
else
VerifyAlphaMoveZero(data);
for (int i = 0; i < bits.Length; i++)
VerifyTutorMoveIndex(data, pa, i, bits, moves);
}
private void VerifyTutorMoveIndex(LegalityAnalysis data, PA8 pa, int i, ReadOnlySpan<bool> bits, ReadOnlySpan<int> moves)
{
bool isPurchased = pa.GetPurchasedRecordFlag(i);
if (isPurchased)
{
// Check if the move can be purchased.
if (bits[i])
return; // If it has been legally purchased, then any mastery state is legal.
data.AddLine(GetInvalid(string.Format(LMoveShopPurchaseInvalid_0, ParseSettings.MoveStrings[moves[i]])));
return;
}
bool isMastered = pa.GetMasteredRecordFlag(i);
if (!isMastered)
return; // All good.
// Check if the move can be purchased; using a Mastery Seed checks the permission.
if (pa.AlphaMove == moves[i])
return; // Previously checked.
if (data.EncounterMatch is IAlpha { IsAlpha: true } && CanMasterMoveFromMoveShop(moves[i], moves, bits))
return; // Alpha forced move.
if (!bits[i])
data.AddLine(GetInvalid(string.Format(LMoveShopMasterInvalid_0, ParseSettings.MoveStrings[moves[i]])));
else if (!CanLearnMoveByLevelUp(data, pa, i, moves))
data.AddLine(GetInvalid(string.Format(LMoveShopMasterNotLearned_0, ParseSettings.MoveStrings[moves[i]])));
}
private static bool CanLearnMoveByLevelUp(LegalityAnalysis data, PA8 pa, int i, ReadOnlySpan<int> moves)
{
// Check if the move can be learned in the learnset...
// Changing forms do not have separate tutor permissions, so we don't need to bother with form changes.
// Level up movepools can grant moves for mastery at lower levels for earlier evolutions... find the minimum.
int level = 101;
foreach (var (species, form) in data.Info.EvoChainsAllGens[8])
{
var pt = PersonalTable.LA;
var index = pt.GetFormIndex(species, form);
var moveset = Legal.LevelUpLA[index];
var lvl = moveset.GetLevelLearnMove(moves[i]);
if (lvl == -1)
continue; // cannot learn via level up
level = Math.Min(lvl, level);
}
return pa.CurrentLevel >= level;
}
private void VerifyAlphaMove(LegalityAnalysis data, PA8 pa, int alphaMove, ReadOnlySpan<int> moves, ReadOnlySpan<bool> bits)
{
if (!pa.IsAlpha)
{
data.AddLine(GetInvalid(LMoveShopAlphaMoveShouldBeZero));
return;
}
if (!CanMasterMoveFromMoveShop(alphaMove, moves, bits))
{
data.AddLine(GetInvalid(LMoveShopAlphaMoveShouldBeOther));
return;
}
// An Alpha Move must be marked as mastered.
var masteredIndex = moves.IndexOf(alphaMove);
// Index is already >= 0, implicitly via the above call not returning false.
if (!pa.GetMasteredRecordFlag(masteredIndex))
data.AddLine(GetInvalid(LMoveShopAlphaMoveShouldBeMastered));
}
private void VerifyAlphaMoveZero(LegalityAnalysis data)
{
var enc = data.Info.EncounterMatch;
if (enc is IAlpha { IsAlpha: false })
return; // okay
var pi = PersonalTable.LA.GetFormEntry(enc.Species, enc.Form);
var tutors = pi.SpecialTutors[0];
bool hasAnyTutor = Array.IndexOf(tutors, true) >= 0;
if (hasAnyTutor) // must have had a tutor flag
data.AddLine(GetInvalid(LMoveShopAlphaMoveShouldBeOther));
}
private static bool CanMasterMoveFromMoveShop(int move, ReadOnlySpan<int> moves, ReadOnlySpan<bool> bits)
{
var index = moves.IndexOf(move);
if (index == -1)
return false; // not in the list
if (!bits[index])
return false; // not a possible move
return true;
}
}

View File

@@ -14,10 +14,11 @@ public sealed class MemoryVerifier : Verifier
public override void Verify(LegalityAnalysis data)
{
if (data.pkm.BDSP)
var pkm = data.pkm;
if (pkm.BDSP || pkm.LA)
{
VerifyOTMemoryIs(data, 0, 0, 0, 0);
VerifyHTMemoryNone(data, (ITrainerMemories)data.pkm);
VerifyHTMemoryNone(data, (ITrainerMemories)pkm);
return;
}
VerifyOTMemory(data);
@@ -259,6 +260,7 @@ private static bool CanHaveMemoryForOT(PKM pkm, int origin, int memory)
case 8 when pkm.GO_HOME: // HOME does not set memories.
case 8 when pkm.Met_Location == Locations.HOME8: // HOME does not set memories.
case 8 when pkm.BDSP: // BDSP does not set memories.
case 8 when pkm.LA: // LA does not set memories.
return false;
// Eggs cannot have memories

View File

@@ -62,6 +62,9 @@ public override void Verify(LegalityAnalysis data)
case PB8 pb8:
VerifyBDSPStats(data, pb8);
break;
case PA8 pa8:
VerifyPLAStats(data, pa8);
break;
}
if (pkm.Format >= 6)
@@ -113,8 +116,34 @@ public override void Verify(LegalityAnalysis data)
}
VerifyMiscFatefulEncounter(data);
VerifyMiscPokerus(data);
}
private void VerifyMiscPokerus(LegalityAnalysis data)
{
var pkm = data.pkm;
if (pkm.Format == 1)
return;
var strain = pkm.PKRS_Strain;
var days = pkm.PKRS_Days;
bool strainValid = IsPokerusStrainValid(pkm, strain, days);
if (!strainValid)
data.AddLine(GetInvalid(string.Format(LPokerusStrainUnobtainable_0, strain)));
var expect = (strain % 4) + 1;
if (days > expect)
data.AddLine(GetInvalid(string.Format(LPokerusDaysTooHigh_0, expect)));
}
private static bool IsPokerusStrainValid(PKM pkm, int strain, int days) => strain switch
{
0 when days is not 0 => false,
8 => false,
not 0 when pkm is PA8 => false,
_ => true,
};
public void VerifyMiscG1(LegalityAnalysis data)
{
var pkm = data.pkm;
@@ -246,6 +275,19 @@ private static void VerifyMiscFatefulEncounter(LegalityAnalysis data)
private static void VerifyMiscMovePP(LegalityAnalysis data)
{
var pkm = data.pkm;
if (pkm is PA8) // No PP Ups
{
if (pkm.Move1_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 1), CurrentMove));
if (pkm.Move2_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 2), CurrentMove));
if (pkm.Move3_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 3), CurrentMove));
if (pkm.Move4_PPUps is not 0)
data.AddLine(GetInvalid(string.Format(LMovePPUpsTooHigh_0, 4), CurrentMove));
}
if (pkm.Move1_PP > pkm.GetMovePP(pkm.Move1, pkm.Move1_PPUps))
data.AddLine(GetInvalid(string.Format(LMovePPTooHigh_0, 1), CurrentMove));
if (pkm.Move2_PP > pkm.GetMovePP(pkm.Move2, pkm.Move2_PPUps))
@@ -275,11 +317,11 @@ private static void VerifyMiscEggCommon(LegalityAnalysis data)
data.AddLine(GetInvalid(msg, Egg));
}
if (pkm is G8PKM pk8)
if (pkm is ITechRecord8 pk8)
{
if (pk8.HasAnyMoveRecordFlag())
if (pk8.GetMoveRecordFlagAny())
data.AddLine(GetInvalid(LEggRelearnFlags, Egg));
if (pk8.StatNature != pk8.Nature)
if (pkm.StatNature != pkm.Nature)
data.AddLine(GetInvalid(LEggNature, Egg));
}
}
@@ -331,6 +373,7 @@ private static void VerifyReceivability(LegalityAnalysis data, MysteryGift g)
case WC7 wc7 when !wc7.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
case WC8 wc8 when !wc8.CanBeReceivedByVersion(pkm.Version):
case WB8 wb8 when !wb8.CanBeReceivedByVersion(pkm.Version):
case WA8 wa8 when !wa8.CanBeReceivedByVersion(pkm.Version):
data.AddLine(GetInvalid(LEncGiftVersionNotDistributed, GameOrigin));
return;
case WC6 wc6 when wc6.RestrictLanguage != 0 && pkm.Language != wc6.RestrictLanguage:
@@ -424,16 +467,21 @@ private static void VerifyFullness(LegalityAnalysis data, PKM pkm)
private static void VerifyBelugaStats(LegalityAnalysis data, PB7 pb7)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator -- THESE MUST MATCH EXACTLY
if (!IsCloseEnough(pb7.HeightAbsolute, pb7.CalcHeightAbsolute))
data.AddLine(GetInvalid(LStatIncorrectHeight, Encounter));
// ReSharper disable once CompareOfFloatsByEqualityOperator -- THESE MUST MATCH EXACTLY
if (!IsCloseEnough(pb7.WeightAbsolute, pb7.CalcWeightAbsolute))
data.AddLine(GetInvalid(LStatIncorrectWeight, Encounter));
VerifyAbsoluteSizes(data, pb7);
if (pb7.Stat_CP != pb7.CalcCP && !IsStarterLGPE(pb7))
data.AddLine(GetInvalid(LStatIncorrectCP, Encounter));
}
private static void VerifyAbsoluteSizes(LegalityAnalysis data, IScaledSizeValue obj)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator -- THESE MUST MATCH EXACTLY
if (!IsCloseEnough(obj.HeightAbsolute, obj.CalcHeightAbsolute))
data.AddLine(GetInvalid(LStatIncorrectHeight, Encounter));
// ReSharper disable once CompareOfFloatsByEqualityOperator -- THESE MUST MATCH EXACTLY
if (!IsCloseEnough(obj.WeightAbsolute, obj.CalcWeightAbsolute))
data.AddLine(GetInvalid(LStatIncorrectWeight, Encounter));
}
private static bool IsCloseEnough(float a, float b)
{
// since we don't have access to SingleToInt32Bits on net46, just do a temp write-read.
@@ -518,6 +566,40 @@ private void VerifySWSHStats(LegalityAnalysis data, PK8 pk8)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
}
private void VerifyPLAStats(LegalityAnalysis data, PA8 pa8)
{
VerifyAbsoluteSizes(data, pa8);
if (pa8.Favorite)
data.AddLine(GetInvalid(LFavoriteMarkingUnavailable, Encounter));
var affix = pa8.AffixedRibbon;
if (affix != -1) // None
data.AddLine(GetInvalid(string.Format(LRibbonMarkingAffixedF_0, affix)));
var social = pa8.Sociability;
if (social != 0)
data.AddLine(GetInvalid(LMemorySocialZero, Encounter));
VerifyStatNature(data, pa8);
var bv = pa8.BattleVersion;
if (bv != 0)
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
if (pa8.CanGigantamax)
data.AddLine(GetInvalid(LStatGigantamaxInvalid));
if (pa8.DynamaxLevel != 0)
data.AddLine(GetInvalid(LStatDynamaxInvalid));
if (pa8.GetMoveRecordFlagAny() && !pa8.IsEgg) // already checked for eggs
data.AddLine(GetInvalid(LEggRelearnFlags));
if (CheckHeightWeightOdds(data.EncounterMatch) && pa8.HeightScalar == 0 && pa8.WeightScalar == 0 && ParseSettings.ZeroHeightWeight != Severity.Valid)
data.AddLine(Get(LStatInvalidHeightWeight, ParseSettings.ZeroHeightWeight, Encounter));
}
private void VerifyBDSPStats(LegalityAnalysis data, PB8 pb8)
{
if (pb8.Favorite)
@@ -543,7 +625,7 @@ private void VerifyBDSPStats(LegalityAnalysis data, PB8 pb8)
if (pb8.DynamaxLevel != 0)
data.AddLine(GetInvalid(LStatDynamaxInvalid));
if (pb8.HasAnyMoveRecordFlag() && !pb8.IsEgg) // already checked for eggs
if (pb8.GetMoveRecordFlagAny() && !pb8.IsEgg) // already checked for eggs
data.AddLine(GetInvalid(LEggRelearnFlags));
if (CheckHeightWeightOdds(data.EncounterMatch) && pb8.HeightScalar == 0 && pb8.WeightScalar == 0 && ParseSettings.ZeroHeightWeight != Severity.Valid)
@@ -555,7 +637,7 @@ private static bool CheckHeightWeightOdds(IEncounterTemplate enc)
if (enc.Generation < 8)
return false;
if (GameVersion.BDSP.Contains(enc.Version))
if (GameVersion.BDSP.Contains(enc.Version) || GameVersion.PLA == enc.Version)
return true;
if (enc is WC8 { IsHOMEGift: true })

View File

@@ -467,8 +467,8 @@ private static IEnumerable<RibbonResult> GetInvalidRibbons8Any(PKM pkm, IRibbonS
yield return new RibbonResult(nameof(s8.RibbonTwinklingStar));
}
// new ribbon likely from Legends: Arceus; inaccessible until then
if (s8.RibbonPioneer)
// received when capturing photos with Pokémon in the Photography Studio
if (s8.RibbonPioneer && !pkm.LA)
{
yield return new RibbonResult(nameof(s8.RibbonPioneer));
}

View File

@@ -125,16 +125,21 @@ public void VerifyTransferLegalityG4(LegalityAnalysis data)
public void VerifyTransferLegalityG8(LegalityAnalysis data)
{
var pkm = data.pkm;
if (pkm is PA8 pa8)
{
VerifyTransferLegalityG8a(data, pa8);
return;
}
if (pkm is PB8 pb8)
{
VerifyTransferLegalityG8(data, pb8);
VerifyTransferLegalityG8b(data, pb8);
return;
}
// PK8
int species = pkm.Species;
var pi = (PersonalInfoSWSH)PersonalTable.SWSH.GetFormEntry(species, pkm.Form);
if (!pi.IsPresentInGame || pkm.BDSP) // Can't transfer
if (!pi.IsPresentInGame || pkm.BDSP || pkm.LA) // Can't transfer
{
data.AddLine(GetInvalid(LTransferBad));
return;
@@ -159,9 +164,20 @@ public void VerifyTransferLegalityG8(LegalityAnalysis data)
VerifyHOMETracker(data, pkm);
}
}
private void VerifyTransferLegalityG8a(LegalityAnalysis data, PA8 pk)
{
// Tracker value is set via Transfer across HOME.
// No HOME access yet.
if (pk is IHomeTrack { Tracker: not 0 })
data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
var pi = (PersonalInfoLA)PersonalTable.LA.GetFormEntry(pk.Species, pk.Form);
if (!pi.IsPresentInGame || !pk.LA) // Can't transfer
data.AddLine(GetInvalid(LTransferBad));
}
// bdsp logic
private void VerifyTransferLegalityG8(LegalityAnalysis data, PB8 pk)
private void VerifyTransferLegalityG8b(LegalityAnalysis data, PB8 pk)
{
// Tracker value is set via Transfer across HOME.
// No HOME access yet.

View File

@@ -17,7 +17,7 @@ public abstract class MysteryGift : IEncounterable, IMoveset, IRelearn
/// <returns>A boolean indicating whether or not the given length is valid for a mystery gift.</returns>
public static bool IsMysteryGift(long len) => Sizes.Contains((int)len);
private static readonly HashSet<int> Sizes = new() { WB8.Size, WC8.Size, WC6Full.Size, WC6.Size, PGF.Size, PGT.Size, PCD.Size };
private static readonly HashSet<int> Sizes = new() { WA8.Size, WB8.Size, WC8.Size, WC6Full.Size, WC6.Size, PGF.Size, PGT.Size, PCD.Size };
/// <summary>
/// Converts the given data to a <see cref="MysteryGift"/>.
@@ -37,6 +37,7 @@ public abstract class MysteryGift : IEncounterable, IMoveset, IRelearn
WR7.Size when ext == ".wr7" => new WR7(data),
WC8.Size when ext is ".wc8" or ".wc8full" => new WC8(data),
WB8.Size when ext is ".wb8" => new WB8(data),
WA8.Size when ext is ".wa8" => new WA8(data),
WB7.SizeFull when ext == ".wb7full" => new WB7(data),
WC6Full.Size when ext == ".wc6full" => new WC6Full(data).Gift,
@@ -57,6 +58,7 @@ public abstract class MysteryGift : IEncounterable, IMoveset, IRelearn
WR7.Size => new WR7(data),
WC8.Size => new WC8(data),
WB8.Size => new WB8(data),
WA8.Size => new WA8(data),
// WC6/WC7: Check year
WC6.Size => ReadUInt32LittleEndian(data.AsSpan(0x4C)) / 10000 < 2000 ? new WC7(data) : new WC6(data),

View File

@@ -0,0 +1,773 @@
using System;
using System.Collections.Generic;
using static PKHeX.Core.RibbonIndex;
using static System.Buffers.Binary.BinaryPrimitives;
namespace PKHeX.Core
{
/// <summary>
/// Generation 8 Mystery Gift Template File, same as <see cref="WC8"/> with <see cref="IGanbaru"/> fields at the end.
/// </summary>
public sealed class WA8 : DataMysteryGift, ILangNick, INature, IGigantamax, IDynamaxLevel, IRibbonIndex, IMemoryOT, ILangNicknamedTemplate, IGanbaru,
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8
{
public const int Size = 0x2C8;
public override int Generation => 8;
public enum GiftType : byte
{
None = 0,
Pokemon = 1,
Item = 2,
}
public WA8() : this(new byte[Size]) { }
public WA8(byte[] data) : base(data) { }
public bool CanBeReceivedByVersion(int v) => v is (int) GameVersion.PLA;
// General Card Properties
public override int CardID
{
get => ReadUInt16LittleEndian(Data.AsSpan(0x8));
set => WriteUInt16LittleEndian(Data.AsSpan(0x8), (ushort)value);
}
public byte CardFlags { get => Data[0x10]; set => Data[0x10] = value; }
public GiftType CardType { get => (GiftType)Data[0x11]; set => Data[0x11] = (byte)value; }
public bool GiftRepeatable { get => (CardFlags & 1) == 0; set => CardFlags = (byte)((CardFlags & ~1) | (value ? 0 : 1)); }
public override bool GiftUsed { get => false; set { } }
public int CardTitleIndex
{
get => Data[0x13];
set => Data[0x13] = (byte) value;
}
public override string CardTitle
{
get => "Mystery Gift"; // TODO: Use text string from CardTitleIndex
set => throw new Exception();
}
// Item Properties
public override bool IsItem { get => CardType == GiftType.Item; set { if (value) CardType = GiftType.Item; } }
public override int ItemID
{
get => GetItem(0);
set => SetItem(0, (ushort)value);
}
public override int Quantity
{
get => GetQuantity(0);
set => SetQuantity(0, (ushort)value);
}
public int GetItem(int index) => ReadUInt16LittleEndian(Data.AsSpan(0x18 + (0x4 * index)));
public void SetItem(int index, ushort item) => WriteUInt16LittleEndian(Data.AsSpan(0x18 + (4 * index)), item);
public int GetQuantity(int index) => ReadUInt16LittleEndian(Data.AsSpan(0x1A + (0x4 * index)));
public void SetQuantity(int index, ushort quantity) => WriteUInt16LittleEndian(Data.AsSpan(0x1A + (4 * index)), quantity);
// Pokémon Properties
public override bool IsPokémon { get => CardType == GiftType.Pokemon; set { if (value) CardType = GiftType.Pokemon; } }
public override bool IsShiny => Shiny.IsShiny();
public override Shiny Shiny
{
get
{
var type = PIDType;
if (type is not Shiny.FixedValue)
return type;
return GetShinyXor() switch
{
0 => Shiny.AlwaysSquare,
<= 15 => Shiny.AlwaysStar,
_ => Shiny.Never,
};
}
}
private int GetShinyXor()
{
// Player owned anti-shiny fixed PID
if (TID == 0 && SID == 0)
return int.MaxValue;
var pid = PID;
var psv = (int)(pid >> 16 ^ (pid & 0xFFFF));
var tsv = (TID ^ SID);
return psv ^ tsv;
}
public override int TID
{
get => ReadUInt16LittleEndian(Data.AsSpan(0x18));
set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value);
}
public override int SID {
get => ReadUInt16LittleEndian(Data.AsSpan(0x1A));
set => WriteUInt16LittleEndian(Data.AsSpan(0x1A), (ushort)value);
}
public int OriginGame
{
get => ReadInt32LittleEndian(Data.AsSpan(0x1C));
set => WriteInt32LittleEndian(Data.AsSpan(0x1C), value);
}
public uint EncryptionConstant
{
get => ReadUInt32LittleEndian(Data.AsSpan(0x20));
set => WriteUInt32LittleEndian(Data.AsSpan(0x20), value);
}
public uint PID
{
get => ReadUInt32LittleEndian(Data.AsSpan(0x24));
set => WriteUInt32LittleEndian(Data.AsSpan(0x24), value);
}
// Nicknames, OT Names 0x30 - 0x228
public override int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x220)); set => WriteUInt16LittleEndian(Data.AsSpan(0x220), (ushort)value); }
public int MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x222)); set => WriteUInt16LittleEndian(Data.AsSpan(0x222), (ushort)value); }
public override int Ball
{
get => ReadUInt16LittleEndian(Data.AsSpan(0x224));
set => WriteUInt16LittleEndian(Data.AsSpan(0x224), (ushort)value);
}
public override int HeldItem
{
get => ReadUInt16LittleEndian(Data.AsSpan(0x226));
set => WriteUInt16LittleEndian(Data.AsSpan(0x226), (ushort)value);
}
public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x228)); set => WriteUInt16LittleEndian(Data.AsSpan(0x228), (ushort)value); }
public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x22A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22A), (ushort)value); }
public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x22C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22C), (ushort)value); }
public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x22E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22E), (ushort)value); }
public int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x230)); set => WriteUInt16LittleEndian(Data.AsSpan(0x230), (ushort)value); }
public int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x232)); set => WriteUInt16LittleEndian(Data.AsSpan(0x232), (ushort)value); }
public int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x234)); set => WriteUInt16LittleEndian(Data.AsSpan(0x234), (ushort)value); }
public int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x236)); set => WriteUInt16LittleEndian(Data.AsSpan(0x236), (ushort)value); }
public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x238)); set => WriteUInt16LittleEndian(Data.AsSpan(0x238), (ushort)value); }
public override int Form { get => Data[0x23A]; set => Data[0x23A] = (byte)value; }
public override int Gender { get => Data[0x23B]; set => Data[0x23B] = (byte)value; }
public override int Level { get => Data[0x23C]; set => Data[0x23C] = (byte)value; }
public override bool IsEgg { get => Data[0x23D] == 1; set => Data[0x23D] = value ? (byte)1 : (byte)0; }
public int Nature { get => (sbyte)Data[0x23E]; set => Data[0x23E] = (byte)value; }
public override int AbilityType { get => Data[0x23F]; set => Data[0x23F] = (byte)value; }
private byte PIDTypeValue => Data[0x240];
public Shiny PIDType => PIDTypeValue switch
{
0 => Shiny.Never,
1 => Shiny.Random,
2 => Shiny.AlwaysStar,
3 => Shiny.AlwaysSquare,
4 => Shiny.FixedValue,
_ => throw new ArgumentOutOfRangeException(nameof(PIDType)),
};
public int MetLevel { get => Data[0x241]; set => Data[0x241] = (byte)value; }
public byte DynamaxLevel { get => Data[0x242]; set => Data[0x242] = value; }
public bool CanGigantamax { get => Data[0x243] != 0; set => Data[0x243] = value ? (byte)1 : (byte)0; }
// Ribbons 0x24C-0x26C
private const int RibbonBytesOffset = 0x244;
private const int RibbonBytesCount = 0x20;
private const int RibbonByteNone = 0xFF; // signed -1
public bool HasMark()
{
for (int i = 0; i < RibbonBytesCount; i++)
{
var value = Data[RibbonBytesOffset + i];
if (value == RibbonByteNone)
return false;
if ((RibbonIndex)value is >= MarkLunchtime and <= MarkSlump)
return true;
}
return false;
}
public byte GetRibbonAtIndex(int byteIndex)
{
if ((uint)byteIndex >= RibbonBytesCount)
throw new IndexOutOfRangeException();
return Data[RibbonBytesOffset + byteIndex];
}
public void SetRibbonAtIndex(int byteIndex, byte ribbonIndex)
{
if ((uint)byteIndex >= RibbonBytesCount)
throw new IndexOutOfRangeException();
Data[RibbonBytesOffset + byteIndex] = ribbonIndex;
}
public int IV_HP { get => Data[0x264]; set => Data[0x264] = (byte)value; }
public int IV_ATK { get => Data[0x265]; set => Data[0x265] = (byte)value; }
public int IV_DEF { get => Data[0x266]; set => Data[0x266] = (byte)value; }
public int IV_SPE { get => Data[0x267]; set => Data[0x267] = (byte)value; }
public int IV_SPA { get => Data[0x268]; set => Data[0x268] = (byte)value; }
public int IV_SPD { get => Data[0x269]; set => Data[0x269] = (byte)value; }
public int OTGender { get => Data[0x26A]; set => Data[0x26A] = (byte)value; }
public int EV_HP { get => Data[0x26B]; set => Data[0x26B] = (byte)value; }
public int EV_ATK { get => Data[0x26C]; set => Data[0x26C] = (byte)value; }
public int EV_DEF { get => Data[0x26D]; set => Data[0x26D] = (byte)value; }
public int EV_SPE { get => Data[0x26E]; set => Data[0x26E] = (byte)value; }
public int EV_SPA { get => Data[0x26F]; set => Data[0x26F] = (byte)value; }
public int EV_SPD { get => Data[0x270]; set => Data[0x270] = (byte)value; }
public int OT_Intensity { get => Data[0x271]; set => Data[0x271] = (byte)value; }
public int OT_Memory { get => Data[0x272]; set => Data[0x272] = (byte)value; }
public int OT_Feeling { get => Data[0x273]; set => Data[0x273] = (byte)value; }
public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0x274)); set => WriteUInt16LittleEndian(Data.AsSpan(0x274), (ushort)value); }
// Only derivations to WC8
public int GV_HP { get => Data[0x27E]; set => Data[0x27E] = (byte)value; }
public int GV_ATK { get => Data[0x27F]; set => Data[0x27F] = (byte)value; }
public int GV_DEF { get => Data[0x280]; set => Data[0x280] = (byte)value; }
public int GV_SPE { get => Data[0x281]; set => Data[0x281] = (byte)value; }
public int GV_SPA { get => Data[0x282]; set => Data[0x282] = (byte)value; }
public int GV_SPD { get => Data[0x283]; set => Data[0x283] = (byte)value; }
// Meta Accessible Properties
public override int[] IVs
{
get => new[] { IV_HP, IV_ATK, IV_DEF, IV_SPE, IV_SPA, IV_SPD };
set
{
if (value.Length != 6) return;
IV_HP = value[0]; IV_ATK = value[1]; IV_DEF = value[2];
IV_SPE = value[3]; IV_SPA = value[4]; IV_SPD = value[5];
}
}
public int[] EVs
{
get => new[] { EV_HP, EV_ATK, EV_DEF, EV_SPE, EV_SPA, EV_SPD };
set
{
if (value.Length != 6) return;
EV_HP = value[0]; EV_ATK = value[1]; EV_DEF = value[2];
EV_SPE = value[3]; EV_SPA = value[4]; EV_SPD = value[5];
}
}
public bool GetIsNicknamed(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetNicknameOffset(language))) != 0;
public bool CanBeAnyLanguage()
{
for (int i = 0; i < 9; i++)
{
var ofs = GetLanguageOffset(i);
var lang = ReadInt16LittleEndian(Data.AsSpan(ofs));
if (lang != 0)
return false;
}
return true;
}
public bool CanHaveLanguage(int language)
{
if (language is < (int)LanguageID.Japanese or > (int)LanguageID.ChineseT)
return false;
if (CanBeAnyLanguage())
return true;
for (int i = 0; i < 9; i++)
{
var ofs = GetLanguageOffset(i);
var lang = ReadInt16LittleEndian(Data.AsSpan(ofs));
if (lang == language)
return true;
}
return false;
}
public int GetLanguage(int redeemLanguage) => Data[GetLanguageOffset(GetLanguageIndex(redeemLanguage))];
private static int GetLanguageOffset(int index) => 0x28 + (index * 0x1C) + 0x1A;
public bool GetHasOT(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetOTOffset(language))) != 0;
private static int GetLanguageIndex(int language)
{
var lang = (LanguageID) language;
if (lang is < LanguageID.Japanese or LanguageID.UNUSED_6 or > LanguageID.ChineseT)
return (int) LanguageID.English; // fallback
return lang < LanguageID.UNUSED_6 ? language - 1 : language - 2;
}
public override int Location { get => MetLocation; set => MetLocation = (ushort)value; }
public override IReadOnlyList<int> Moves
{
get => new[] { Move1, Move2, Move3, Move4 };
set
{
if (value.Count > 0) Move1 = value[0];
if (value.Count > 1) Move2 = value[1];
if (value.Count > 2) Move3 = value[2];
if (value.Count > 3) Move4 = value[3];
}
}
public override IReadOnlyList<int> Relearn
{
get => new[] { RelearnMove1, RelearnMove2, RelearnMove3, RelearnMove4 };
set
{
if (value.Count > 0) RelearnMove1 = value[0];
if (value.Count > 1) RelearnMove2 = value[1];
if (value.Count > 2) RelearnMove3 = value[2];
if (value.Count > 3) RelearnMove4 = value[3];
}
}
public override string OT_Name { get; set; } = string.Empty;
public string Nickname => string.Empty;
public bool IsNicknamed => false;
public int Language => 2;
public string GetNickname(int language) => StringConverter8.GetString(Data.AsSpan(GetNicknameOffset(language), 0x1A));
public void SetNickname(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetNicknameOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero);
public string GetOT(int language) => StringConverter8.GetString(Data.AsSpan(GetOTOffset(language), 0x1A));
public void SetOT(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetOTOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero);
private static int GetNicknameOffset(int language)
{
int index = GetLanguageIndex(language);
return 0x28 + (index * 0x1C);
}
private static int GetOTOffset(int language)
{
int index = GetLanguageIndex(language);
return 0x124 + (index * 0x1C);
}
public bool CanHandleOT(int language) => !GetHasOT(language);
public override GameVersion Version
{
get => OriginGame != 0 ? (GameVersion)OriginGame : GameVersion.PLA;
set { }
}
public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria)
{
if (!IsPokémon)
throw new ArgumentException(nameof(IsPokémon));
int currentLevel = Level > 0 ? Level : (1 + Util.Rand.Next(100));
int metLevel = MetLevel > 0 ? MetLevel : currentLevel;
var pi = PersonalTable.LA.GetFormEntry(Species, Form);
var language = sav.Language;
var OT = GetOT(language);
bool hasOT = GetHasOT(language);
var pk = new PA8
{
EncryptionConstant = EncryptionConstant != 0 ? EncryptionConstant : Util.Rand32(),
TID = TID,
SID = SID,
Species = Species,
Form = Form,
CurrentLevel = currentLevel,
Ball = Ball != 0 ? Ball : 4, // Default is Pokeball
Met_Level = metLevel,
HeldItem = HeldItem,
EXP = Experience.GetEXP(currentLevel, pi.EXPGrowth),
Move1 = Move1,
Move2 = Move2,
Move3 = Move3,
Move4 = Move4,
RelearnMove1 = RelearnMove1,
RelearnMove2 = RelearnMove2,
RelearnMove3 = RelearnMove3,
RelearnMove4 = RelearnMove4,
Version = OriginGame != 0 ? OriginGame : sav.Game,
OT_Name = OT.Length > 0 ? OT : sav.OT,
OT_Gender = OTGender < 2 ? OTGender : sav.Gender,
HT_Name = hasOT ? sav.OT : string.Empty,
HT_Gender = hasOT ? sav.Gender : 0,
HT_Language = hasOT ? language : 0,
CurrentHandler = hasOT ? 1 : 0,
OT_Friendship = pi.BaseFriendship,
OT_Intensity = OT_Intensity,
OT_Memory = OT_Memory,
OT_TextVar = OT_TextVar,
OT_Feeling = OT_Feeling,
FatefulEncounter = true,
EVs = EVs,
CanGigantamax = CanGigantamax,
DynamaxLevel = DynamaxLevel,
Met_Location = MetLocation,
Egg_Location = EggLocation,
};
pk.SetMaximumPPCurrent();
if ((sav.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version))
pk.Version = (int)GameVersion.PLA;
if (OTGender >= 2)
{
pk.TID = sav.TID;
pk.SID = sav.SID;
}
// Official code explicitly corrects for Meowstic
if (pk.Species == (int)Core.Species.Meowstic)
pk.Form = pk.Gender;
pk.MetDate = DateTime.Now;
var nickname_language = GetLanguage(language);
pk.Language = nickname_language != 0 ? nickname_language : sav.Language;
pk.IsNicknamed = GetIsNicknamed(language);
pk.Nickname = pk.IsNicknamed ? GetNickname(language) : SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation);
for (var i = 0; i < RibbonBytesCount; i++)
{
var ribbon = GetRibbonAtIndex(i);
if (ribbon != RibbonByteNone)
pk.SetRibbon(ribbon);
}
SetPINGA(pk, criteria);
if (IsEgg)
SetEggMetData(pk);
pk.CurrentFriendship = pk.IsEgg ? pi.HatchCycles : pi.BaseFriendship;
{
pk.HeightScalar = PokeSizeUtil.GetRandomScalar();
pk.WeightScalar = PokeSizeUtil.GetRandomScalar();
pk.HeightScalarCopy = pk.HeightScalar;
pk.ResetHeight();
pk.ResetWeight();
}
pk.ResetPartyStats();
pk.RefreshChecksum();
return pk;
}
private void SetEggMetData(PKM pk)
{
pk.IsEgg = true;
pk.EggMetDate = DateTime.Now;
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(0, pk.Language, Generation);
pk.IsNicknamed = true;
}
private void SetPINGA(PKM pk, EncounterCriteria criteria)
{
var pi = PersonalTable.LA.GetFormEntry(Species, Form);
pk.Nature = (int)criteria.GetNature(Nature == -1 ? Core.Nature.Random : (Nature)Nature);
pk.StatNature = pk.Nature;
pk.Gender = criteria.GetGender(Gender, pi);
var av = GetAbilityIndex(criteria);
pk.RefreshAbility(av);
SetPID(pk);
SetIVs(pk);
}
private int GetAbilityIndex(EncounterCriteria criteria) => AbilityType switch
{
00 or 01 or 02 => AbilityType, // Fixed 0/1/2
03 or 04 => criteria.GetAbilityFromNumber(Ability), // 0/1 or 0/1/H
_ => throw new ArgumentOutOfRangeException(nameof(AbilityType)),
};
public override AbilityPermission Ability => AbilityType switch
{
0 => AbilityPermission.OnlyFirst,
1 => AbilityPermission.OnlySecond,
2 => AbilityPermission.OnlyHidden,
3 => AbilityPermission.Any12,
_ => AbilityPermission.Any12H,
};
private uint GetPID(ITrainerID tr, byte type)
{
return type switch
{
0 => GetAntishiny(tr), // Random, Never Shiny
1 => Util.Rand32(), // Random, Any
2 => (uint) (((tr.TID ^ tr.SID ^ (PID & 0xFFFF) ^ 1) << 16) | (PID & 0xFFFF)), // Fixed, Force Star
3 => (uint) (((tr.TID ^ tr.SID ^ (PID & 0xFFFF) ^ 0) << 16) | (PID & 0xFFFF)), // Fixed, Force Square
4 => PID, // Fixed, Force Value
_ => throw new ArgumentOutOfRangeException(nameof(type)),
};
static uint GetAntishiny(ITrainerID tr)
{
var pid = Util.Rand32();
if (tr.IsShiny(pid, 8))
return pid ^ 0x1000_0000;
return pid;
}
}
private void SetPID(PKM pk)
{
pk.PID = GetPID(pk, PIDTypeValue);
}
private void SetIVs(PKM pk)
{
Span<int> finalIVs = stackalloc int[6];
var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3);
var rng = Util.Rand;
if (ivflag == 0) // Random IVs
{
for (int i = 0; i < 6; i++)
finalIVs[i] = IVs[i] > 31 ? rng.Next(32) : IVs[i];
}
else // 1/2/3 perfect IVs
{
int IVCount = ivflag - 0xFB;
do { finalIVs[rng.Next(6)] = 31; }
while (finalIVs.Count(31) < IVCount);
for (int i = 0; i < 6; i++)
finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32);
}
pk.SetIVs(finalIVs);
}
public override bool IsMatchExact(PKM pkm, DexLevel evo)
{
if (pkm.Egg_Location == 0) // Not Egg
{
if (OTGender < 2)
{
if (SID != pkm.SID) return false;
if (TID != pkm.TID) return false;
if (OTGender != pkm.OT_Gender) return false;
}
if (!CanBeAnyLanguage() && !CanHaveLanguage(pkm.Language))
return false;
var OT = GetOT(pkm.Language); // May not be guaranteed to work.
if (!string.IsNullOrEmpty(OT) && OT != pkm.OT_Name) return false;
if (OriginGame != 0 && OriginGame != pkm.Version) return false;
if (EncryptionConstant != 0)
{
if (EncryptionConstant != pkm.EncryptionConstant)
return false;
}
}
if (Form != evo.Form && !FormInfo.IsFormChangeable(Species, Form, pkm.Form, pkm.Format))
return false;
if (IsEgg)
{
if (EggLocation != pkm.Egg_Location) // traded
{
if (pkm.Egg_Location != Locations.LinkTrade6)
return false;
if (PIDType == Shiny.Random && pkm.IsShiny && pkm.ShinyXor > 1)
return false; // shiny traded egg will always have xor0/1.
}
if (!PIDType.IsValid(pkm))
{
return false; // can't be traded away for unshiny
}
if (pkm.IsEgg && !pkm.IsNative)
return false;
}
else
{
if (!PIDType.IsValid(pkm)) return false;
if (EggLocation != pkm.Egg_Location) return false;
if (MetLocation != pkm.Met_Location) return false;
}
if (MetLevel != 0 && MetLevel != pkm.Met_Level) return false;
if (OTGender < 2 && OTGender != pkm.OT_Gender) return false;
if (Nature != -1 && pkm.Nature != Nature) return false;
if (Gender != 3 && Gender != pkm.Gender) return false;
const int poke = (int)Core.Ball.LAPoke;
var expectedBall = (Ball == 0 ? poke : Ball);
if (expectedBall < poke) // Not even Cherish balls are safe! They get set to the proto-Poké ball.
expectedBall = poke;
if (expectedBall != pkm.Ball)
return false;
if (pkm is IGigantamax g && g.CanGigantamax != CanGigantamax && !g.CanToggleGigantamax(pkm.Species, pkm.Form, Species, Form))
return false;
if (pkm is not IDynamaxLevel dl || dl.DynamaxLevel < DynamaxLevel)
return false;
// PID Types 0 and 1 do not use the fixed PID value.
// Values 2,3 are specific shiny states, and 4 is fixed value.
// 2,3,4 can change if it is a traded egg to ensure the same shiny state.
var type = PIDTypeValue;
if (type <= 1)
return true;
return pkm.PID == GetPID(pkm, type);
}
protected override bool IsMatchDeferred(PKM pkm) => Species != pkm.Species;
protected override bool IsMatchPartial(PKM pkm) => false; // no version compatibility checks yet.
#region Lazy Ribbon Implementation
public bool RibbonEarth { get => this.GetRibbonIndex(Earth); set => this.SetRibbonIndex(Earth, value); }
public bool RibbonNational { get => this.GetRibbonIndex(National); set => this.SetRibbonIndex(National, value); }
public bool RibbonCountry { get => this.GetRibbonIndex(Country); set => this.SetRibbonIndex(Country, value); }
public bool RibbonChampionBattle { get => this.GetRibbonIndex(ChampionBattle); set => this.SetRibbonIndex(ChampionBattle, value); }
public bool RibbonChampionRegional { get => this.GetRibbonIndex(ChampionRegional); set => this.SetRibbonIndex(ChampionRegional, value); }
public bool RibbonChampionNational { get => this.GetRibbonIndex(ChampionNational); set => this.SetRibbonIndex(ChampionNational, value); }
public bool RibbonClassic { get => this.GetRibbonIndex(Classic); set => this.SetRibbonIndex(Classic, value); }
public bool RibbonWishing { get => this.GetRibbonIndex(Wishing); set => this.SetRibbonIndex(Wishing, value); }
public bool RibbonPremier { get => this.GetRibbonIndex(Premier); set => this.SetRibbonIndex(Premier, value); }
public bool RibbonEvent { get => this.GetRibbonIndex(Event); set => this.SetRibbonIndex(Event, value); }
public bool RibbonBirthday { get => this.GetRibbonIndex(Birthday); set => this.SetRibbonIndex(Birthday, value); }
public bool RibbonSpecial { get => this.GetRibbonIndex(Special); set => this.SetRibbonIndex(Special, value); }
public bool RibbonWorld { get => this.GetRibbonIndex(World); set => this.SetRibbonIndex(World, value); }
public bool RibbonChampionWorld { get => this.GetRibbonIndex(ChampionWorld); set => this.SetRibbonIndex(ChampionWorld, value); }
public bool RibbonSouvenir { get => this.GetRibbonIndex(Souvenir); set => this.SetRibbonIndex(Souvenir, value); }
public bool RibbonChampionG3 { get => this.GetRibbonIndex(ChampionG3); set => this.SetRibbonIndex(ChampionG3, value); }
public bool RibbonArtist { get => this.GetRibbonIndex(Artist); set => this.SetRibbonIndex(Artist, value); }
public bool RibbonEffort { get => this.GetRibbonIndex(Effort); set => this.SetRibbonIndex(Effort, value); }
public bool RibbonChampionSinnoh { get => this.GetRibbonIndex(ChampionSinnoh); set => this.SetRibbonIndex(ChampionSinnoh, value); }
public bool RibbonAlert { get => this.GetRibbonIndex(Alert); set => this.SetRibbonIndex(Alert, value); }
public bool RibbonShock { get => this.GetRibbonIndex(Shock); set => this.SetRibbonIndex(Shock, value); }
public bool RibbonDowncast { get => this.GetRibbonIndex(Downcast); set => this.SetRibbonIndex(Downcast, value); }
public bool RibbonCareless { get => this.GetRibbonIndex(Careless); set => this.SetRibbonIndex(Careless, value); }
public bool RibbonRelax { get => this.GetRibbonIndex(Relax); set => this.SetRibbonIndex(Relax, value); }
public bool RibbonSnooze { get => this.GetRibbonIndex(Snooze); set => this.SetRibbonIndex(Snooze, value); }
public bool RibbonSmile { get => this.GetRibbonIndex(Smile); set => this.SetRibbonIndex(Smile, value); }
public bool RibbonGorgeous { get => this.GetRibbonIndex(Gorgeous); set => this.SetRibbonIndex(Gorgeous, value); }
public bool RibbonRoyal { get => this.GetRibbonIndex(Royal); set => this.SetRibbonIndex(Royal, value); }
public bool RibbonGorgeousRoyal { get => this.GetRibbonIndex(GorgeousRoyal); set => this.SetRibbonIndex(GorgeousRoyal, value); }
public bool RibbonFootprint { get => this.GetRibbonIndex(Footprint); set => this.SetRibbonIndex(Footprint, value); }
public bool RibbonRecord { get => this.GetRibbonIndex(Record); set => this.SetRibbonIndex(Record, value); }
public bool RibbonLegend { get => this.GetRibbonIndex(Legend); set => this.SetRibbonIndex(Legend, value); }
public bool RibbonChampionKalos { get => this.GetRibbonIndex(ChampionKalos); set => this.SetRibbonIndex(ChampionKalos, value); }
public bool RibbonChampionG6Hoenn { get => this.GetRibbonIndex(ChampionG6Hoenn); set => this.SetRibbonIndex(ChampionG6Hoenn, value); }
public bool RibbonBestFriends { get => this.GetRibbonIndex(BestFriends); set => this.SetRibbonIndex(BestFriends, value); }
public bool RibbonTraining { get => this.GetRibbonIndex(Training); set => this.SetRibbonIndex(Training, value); }
public bool RibbonBattlerSkillful { get => this.GetRibbonIndex(BattlerSkillful); set => this.SetRibbonIndex(BattlerSkillful, value); }
public bool RibbonBattlerExpert { get => this.GetRibbonIndex(BattlerExpert); set => this.SetRibbonIndex(BattlerExpert, value); }
public bool RibbonContestStar { get => this.GetRibbonIndex(ContestStar); set => this.SetRibbonIndex(ContestStar, value); }
public bool RibbonMasterCoolness { get => this.GetRibbonIndex(MasterCoolness); set => this.SetRibbonIndex(MasterCoolness, value); }
public bool RibbonMasterBeauty { get => this.GetRibbonIndex(MasterBeauty); set => this.SetRibbonIndex(MasterBeauty, value); }
public bool RibbonMasterCuteness { get => this.GetRibbonIndex(MasterCuteness); set => this.SetRibbonIndex(MasterCuteness, value); }
public bool RibbonMasterCleverness { get => this.GetRibbonIndex(MasterCleverness); set => this.SetRibbonIndex(MasterCleverness, value); }
public bool RibbonMasterToughness { get => this.GetRibbonIndex(MasterToughness); set => this.SetRibbonIndex(MasterToughness, value); }
public int RibbonCountMemoryContest { get => 0; set { } }
public int RibbonCountMemoryBattle { get => 0; set { } }
public bool RibbonChampionAlola { get => this.GetRibbonIndex(ChampionAlola); set => this.SetRibbonIndex(ChampionAlola, value); }
public bool RibbonBattleRoyale { get => this.GetRibbonIndex(BattleRoyale); set => this.SetRibbonIndex(BattleRoyale, value); }
public bool RibbonBattleTreeGreat { get => this.GetRibbonIndex(BattleTreeGreat); set => this.SetRibbonIndex(BattleTreeGreat, value); }
public bool RibbonBattleTreeMaster { get => this.GetRibbonIndex(BattleTreeMaster); set => this.SetRibbonIndex(BattleTreeMaster, value); }
public bool RibbonChampionGalar { get => this.GetRibbonIndex(ChampionGalar); set => this.SetRibbonIndex(ChampionGalar, value); }
public bool RibbonTowerMaster { get => this.GetRibbonIndex(TowerMaster); set => this.SetRibbonIndex(TowerMaster, value); }
public bool RibbonMasterRank { get => this.GetRibbonIndex(MasterRank); set => this.SetRibbonIndex(MasterRank, value); }
public bool RibbonMarkLunchtime { get => this.GetRibbonIndex(MarkLunchtime); set => this.SetRibbonIndex(MarkLunchtime, value); }
public bool RibbonMarkSleepyTime { get => this.GetRibbonIndex(MarkSleepyTime); set => this.SetRibbonIndex(MarkSleepyTime, value); }
public bool RibbonMarkDusk { get => this.GetRibbonIndex(MarkDusk); set => this.SetRibbonIndex(MarkDusk, value); }
public bool RibbonMarkDawn { get => this.GetRibbonIndex(MarkDawn); set => this.SetRibbonIndex(MarkDawn, value); }
public bool RibbonMarkCloudy { get => this.GetRibbonIndex(MarkCloudy); set => this.SetRibbonIndex(MarkCloudy, value); }
public bool RibbonMarkRainy { get => this.GetRibbonIndex(MarkRainy); set => this.SetRibbonIndex(MarkRainy, value); }
public bool RibbonMarkStormy { get => this.GetRibbonIndex(MarkStormy); set => this.SetRibbonIndex(MarkStormy, value); }
public bool RibbonMarkSnowy { get => this.GetRibbonIndex(MarkSnowy); set => this.SetRibbonIndex(MarkSnowy, value); }
public bool RibbonMarkBlizzard { get => this.GetRibbonIndex(MarkBlizzard); set => this.SetRibbonIndex(MarkBlizzard, value); }
public bool RibbonMarkDry { get => this.GetRibbonIndex(MarkDry); set => this.SetRibbonIndex(MarkDry, value); }
public bool RibbonMarkSandstorm { get => this.GetRibbonIndex(MarkSandstorm); set => this.SetRibbonIndex(MarkSandstorm, value); }
public bool RibbonMarkMisty { get => this.GetRibbonIndex(MarkMisty); set => this.SetRibbonIndex(MarkMisty, value); }
public bool RibbonMarkDestiny { get => this.GetRibbonIndex(MarkDestiny); set => this.SetRibbonIndex(MarkDestiny, value); }
public bool RibbonMarkFishing { get => this.GetRibbonIndex(MarkFishing); set => this.SetRibbonIndex(MarkFishing, value); }
public bool RibbonMarkCurry { get => this.GetRibbonIndex(MarkCurry); set => this.SetRibbonIndex(MarkCurry, value); }
public bool RibbonMarkUncommon { get => this.GetRibbonIndex(MarkUncommon); set => this.SetRibbonIndex(MarkUncommon, value); }
public bool RibbonMarkRare { get => this.GetRibbonIndex(MarkRare); set => this.SetRibbonIndex(MarkRare, value); }
public bool RibbonMarkRowdy { get => this.GetRibbonIndex(MarkRowdy); set => this.SetRibbonIndex(MarkRowdy, value); }
public bool RibbonMarkAbsentMinded { get => this.GetRibbonIndex(MarkAbsentMinded); set => this.SetRibbonIndex(MarkAbsentMinded, value); }
public bool RibbonMarkJittery { get => this.GetRibbonIndex(MarkJittery); set => this.SetRibbonIndex(MarkJittery, value); }
public bool RibbonMarkExcited { get => this.GetRibbonIndex(MarkExcited); set => this.SetRibbonIndex(MarkExcited, value); }
public bool RibbonMarkCharismatic { get => this.GetRibbonIndex(MarkCharismatic); set => this.SetRibbonIndex(MarkCharismatic, value); }
public bool RibbonMarkCalmness { get => this.GetRibbonIndex(MarkCalmness); set => this.SetRibbonIndex(MarkCalmness, value); }
public bool RibbonMarkIntense { get => this.GetRibbonIndex(MarkIntense); set => this.SetRibbonIndex(MarkIntense, value); }
public bool RibbonMarkZonedOut { get => this.GetRibbonIndex(MarkZonedOut); set => this.SetRibbonIndex(MarkZonedOut, value); }
public bool RibbonMarkJoyful { get => this.GetRibbonIndex(MarkJoyful); set => this.SetRibbonIndex(MarkJoyful, value); }
public bool RibbonMarkAngry { get => this.GetRibbonIndex(MarkAngry); set => this.SetRibbonIndex(MarkAngry, value); }
public bool RibbonMarkSmiley { get => this.GetRibbonIndex(MarkSmiley); set => this.SetRibbonIndex(MarkSmiley, value); }
public bool RibbonMarkTeary { get => this.GetRibbonIndex(MarkTeary); set => this.SetRibbonIndex(MarkTeary, value); }
public bool RibbonMarkUpbeat { get => this.GetRibbonIndex(MarkUpbeat); set => this.SetRibbonIndex(MarkUpbeat, value); }
public bool RibbonMarkPeeved { get => this.GetRibbonIndex(MarkPeeved); set => this.SetRibbonIndex(MarkPeeved, value); }
public bool RibbonMarkIntellectual { get => this.GetRibbonIndex(MarkIntellectual); set => this.SetRibbonIndex(MarkIntellectual, value); }
public bool RibbonMarkFerocious { get => this.GetRibbonIndex(MarkFerocious); set => this.SetRibbonIndex(MarkFerocious, value); }
public bool RibbonMarkCrafty { get => this.GetRibbonIndex(MarkCrafty); set => this.SetRibbonIndex(MarkCrafty, value); }
public bool RibbonMarkScowling { get => this.GetRibbonIndex(MarkScowling); set => this.SetRibbonIndex(MarkScowling, value); }
public bool RibbonMarkKindly { get => this.GetRibbonIndex(MarkKindly); set => this.SetRibbonIndex(MarkKindly, value); }
public bool RibbonMarkFlustered { get => this.GetRibbonIndex(MarkFlustered); set => this.SetRibbonIndex(MarkFlustered, value); }
public bool RibbonMarkPumpedUp { get => this.GetRibbonIndex(MarkPumpedUp); set => this.SetRibbonIndex(MarkPumpedUp, value); }
public bool RibbonMarkZeroEnergy { get => this.GetRibbonIndex(MarkZeroEnergy); set => this.SetRibbonIndex(MarkZeroEnergy, value); }
public bool RibbonMarkPrideful { get => this.GetRibbonIndex(MarkPrideful); set => this.SetRibbonIndex(MarkPrideful, value); }
public bool RibbonMarkUnsure { get => this.GetRibbonIndex(MarkUnsure); set => this.SetRibbonIndex(MarkUnsure, value); }
public bool RibbonMarkHumble { get => this.GetRibbonIndex(MarkHumble); set => this.SetRibbonIndex(MarkHumble, value); }
public bool RibbonMarkThorny { get => this.GetRibbonIndex(MarkThorny); set => this.SetRibbonIndex(MarkThorny, value); }
public bool RibbonMarkVigor { get => this.GetRibbonIndex(MarkVigor); set => this.SetRibbonIndex(MarkVigor, value); }
public bool RibbonMarkSlump { get => this.GetRibbonIndex(MarkSlump); set => this.SetRibbonIndex(MarkSlump, value); }
public bool RibbonTwinklingStar { get => this.GetRibbonIndex(TwinklingStar); set => this.SetRibbonIndex(TwinklingStar, value); }
public bool RibbonPioneer { get => this.GetRibbonIndex(Pioneer); set => this.SetRibbonIndex(Pioneer, value); }
public int GetRibbonByte(int index) => Array.FindIndex(Data, RibbonBytesOffset, RibbonBytesCount, z => z == index);
public bool GetRibbon(int index) => GetRibbonByte(index) >= 0;
public void SetRibbon(int index, bool value = true)
{
if ((uint)index > (uint)MarkSlump)
throw new ArgumentOutOfRangeException(nameof(index));
if (value)
{
if (GetRibbon(index))
return;
var openIndex = Array.FindIndex(Data, RibbonBytesOffset, RibbonBytesCount, z => z != RibbonByteNone);
if (openIndex < 0)
throw new ArgumentOutOfRangeException(nameof(index));
SetRibbonAtIndex(openIndex, (byte)index);
}
else
{
var ofs = GetRibbonByte(index);
if (ofs < 0)
return;
SetRibbonAtIndex(ofs, RibbonByteNone);
}
}
#endregion
}
}

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