Update 21.11.19 - Brilliant Diamond & Shining Pearl (#3289)

Big thanks to @SciresM @sora10pls @Lusamine @architdate @ReignOfComputer for testing and contributing code / test cases. Can't add co-authors from the PR menu :(

Builds will fail because azure pipelines not yet updated with net6.
This commit is contained in:
Kurt 2021-11-19 18:23:49 -08:00 committed by GitHub
parent 5cff79f382
commit 723514e89c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
242 changed files with 15669 additions and 763 deletions

View file

@ -94,6 +94,20 @@ namespace PKHeX.Core
}
}
if (enc is EncounterSlot8b { IsUnderground: true } ug)
{
var moves = legal.Info.Moves;
for (int i = 0; i < moves.Length; i++)
{
if (!moves[i].ShouldBeInRelearnMoves())
continue;
var move = legal.pkm.GetMove(i);
if (ug.CanBeUndergroundMove(move))
return new[] { move, 0, 0, 0 };
}
}
var encounter = EncounterSuggestion.GetSuggestedMetInfo(legal.pkm);
if (encounter is IRelearn {Relearn: {Count: > 0} r})
return r;

View file

@ -375,7 +375,7 @@ namespace PKHeX.Core
{
bool traded = origin == dest;
var today = pk.MetDate = DateTime.Today;
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk.Generation, traded);
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(pk.Generation, origin, traded);
pk.EggMetDate = today;
}

View file

@ -125,6 +125,131 @@ namespace PKHeX.Core
};
}
public sealed class EventLabelCollectionSystem
{
public readonly IReadOnlyList<NamedEventWork> Work;
public readonly IReadOnlyList<NamedEventValue> Flag;
public readonly IReadOnlyList<NamedEventValue> System;
public EventLabelCollectionSystem(string game, int maxFlag = int.MaxValue, int maxSystem = int.MaxValue, int maxValue = int.MaxValue)
{
var f = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "flag");
var s = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "system");
var c = GameLanguage.GetStrings(game, GameInfo.CurrentLanguage, "work");
Flag = GetFlags(f, maxFlag);
System = GetFlags(s, maxSystem);
Work = GetValues(c, maxValue);
}
private static List<NamedEventValue> GetFlags(IReadOnlyCollection<string> strings, int maxValue)
{
var result = new List<NamedEventValue>(strings.Count);
var processed = new HashSet<int>();
foreach (var s in strings)
{
var split = s.Split('\t');
if (split.Length != 3)
continue;
var index = TryParseHexDec(split[0]);
if (index >= maxValue)
throw new ArgumentOutOfRangeException(nameof(index));
if (processed.Contains(index))
throw new ArgumentException("Already have an entry for this!", nameof(index));
var type = GetEventType(split[1]);
var desc = split[2];
var item = new NamedEventValue(desc, index, type);
result.Add(item);
processed.Add(index);
}
return result;
}
private static readonly NamedEventConst Custom = new("Custom", NamedEventConst.CustomMagicValue);
private static readonly NamedEventConst[] Empty = { Custom };
private static IReadOnlyList<NamedEventWork> GetValues(IReadOnlyCollection<string> strings, int maxValue)
{
var result = new List<NamedEventWork>(strings.Count);
var processed = new HashSet<int>();
foreach (var s in strings)
{
var split = s.Split('\t');
if (split.Length is not (3 or 4))
continue;
var index = TryParseHexDecConst(split[0]);
if (index >= maxValue)
throw new ArgumentOutOfRangeException(nameof(index));
if (processed.Contains(index))
throw new ArgumentException("Already have an entry for this!", nameof(index));
var type = GetEventType(split[1]);
var desc = split[2];
var predefined = split.Length is 3 ? Empty : GetPredefinedArray(split[3]);
var item = new NamedEventWork(desc, index, type, predefined);
result.Add(item);
processed.Add(index);
}
return result;
}
private static IReadOnlyList<NamedEventConst> GetPredefinedArray(string combined)
{
var result = new List<NamedEventConst> { Custom };
var split = combined.Split(',');
foreach (var entry in split)
{
var subsplit = entry.Split(':');
var name = subsplit[1];
var value = Convert.ToUInt16(subsplit[0]);
result.Add(new NamedEventConst(name, value));
}
return result;
}
private static int TryParseHexDec(string flag)
{
if (!flag.StartsWith("0x"))
return Convert.ToInt16(flag);
flag = flag[2..];
return Convert.ToInt16(flag, 16);
}
private static int TryParseHexDecConst(string c)
{
if (!c.StartsWith("0x40"))
return Convert.ToInt16(c);
c = c[4..];
return Convert.ToInt16(c, 16);
}
private static NamedEventType GetEventType(string s) => s.Length == 0 ? 0 : GetEventType(s[0]);
private static NamedEventType GetEventType(char c) => c switch
{
'h' => NamedEventType.HiddenItem,
'm' => NamedEventType.Misc,
'f' => NamedEventType.FlyToggle,
't' => NamedEventType.TrainerToggle,
's' => NamedEventType.StoryProgress,
'a' => NamedEventType.Achievement,
'+' => NamedEventType.Statistic,
'*' => NamedEventType.UsefulFeature,
'e' => NamedEventType.EncounterEvent,
'g' => NamedEventType.GiftAvailable,
'r' => NamedEventType.Rebattle,
_ => NamedEventType.None,
};
}
public enum NamedEventType
{
None,

View file

@ -183,4 +183,113 @@ namespace PKHeX.Core
}
}
}
public sealed class EventWorkDiff8b : EventBlockDiff
{
public readonly List<int> WorkChanged = new();
private SaveFile? S1;
public readonly List<int> SetSystem = new();
public readonly List<int> ClearedSystem = new();
public EventWorkDiff8b(string f1, string f2)
{
if (!SanityCheckFiles(f1, f2))
return;
var s1 = SaveUtil.GetVariantSAV(f1);
var s2 = SaveUtil.GetVariantSAV(f2);
if (s1 == null || s2 == null || s1.GetType() != s2.GetType())
{
Message = MsgSaveDifferentTypes;
return;
}
Diff(s1, s2);
}
public EventWorkDiff8b(SaveFile s1, SaveFile s2) => Diff(s1, s2);
protected override void Diff(SaveFile s1, SaveFile s2)
{
if (!SanityCheckSaveInfo(s1, s2))
return;
EventWorkUtil.DiffSavesFlag(((SAV8BS)s1).Work, ((SAV8BS)s2).Work, SetFlags, ClearedFlags);
EventWorkUtil.DiffSavesSystem(((SAV8BS)s1).Work, ((SAV8BS)s2).Work, SetSystem, ClearedSystem);
EventWorkUtil.DiffSavesWork(((SAV8BS)s1).Work, ((SAV8BS)s2).Work, WorkChanged, WorkDiff);
S1 = s1;
}
public IReadOnlyList<string> Summarize()
{
if (S1 == null)
return Array.Empty<string>();
var fOn = SetFlags.Select(z => new FlagSummary(z).ToString());
var fOff = ClearedFlags.Select(z => new FlagSummary(z).ToString());
var sOn = SetSystem.Select(z => new FlagSummary(z).ToString());
var sOff = ClearedSystem.Select(z => new FlagSummary(z).ToString());
var wt = WorkChanged.Select((z, i) => new WorkSummary(z, WorkDiff[i]).ToString());
var list = new List<string> { "Flags: ON", "=========" };
list.AddRange(fOn);
if (SetFlags.Count == 0)
list.Add("None.");
list.Add("");
list.Add("Flags: OFF");
list.Add("==========");
list.AddRange(fOff);
if (ClearedFlags.Count == 0)
list.Add("None.");
list.Add("System: ON");
list.Add("=========");
list.AddRange(sOn);
if (SetFlags.Count == 0)
list.Add("None.");
list.Add("");
list.Add("System: OFF");
list.Add("==========");
list.AddRange(sOff);
if (ClearedSystem.Count == 0)
list.Add("None.");
list.Add("");
list.Add("Work:");
list.Add("=====");
if (WorkChanged.Count == 0)
list.Add("None.");
list.AddRange(wt);
return list;
}
private readonly struct FlagSummary
{
private int Raw { get; }
public override string ToString() => $"{Raw:0000}\t{false}";
public FlagSummary(int rawIndex)
{
Raw = rawIndex;
}
}
private readonly struct WorkSummary
{
private int Raw { get; }
private string Text { get; }
public override string ToString() => $"{Raw:000}\t{Text}";
public WorkSummary(int rawIndex, string text)
{
Raw = rawIndex;
Text = text;
}
}
}
}

View file

@ -70,14 +70,13 @@ namespace PKHeX.Core
/// <summary>
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventFlag"/> changes.
/// </summary>
/// <typeparam name="T">Type of value used by <see cref="EventWork{T}"/></typeparam>
/// <param name="before">Data before the event was triggered</param>
/// <param name="after">Data after the event was triggered</param>
/// <param name="on">List of flags that were turned on</param>
/// <param name="off">List of flags that were turned off</param>
public static void DiffSavesFlag<T>(IEventWork<T> before, IEventWork<T> after, List<int> on, List<int> off)
public static void DiffSavesFlag(IEventFlag before, IEventFlag after, List<int> on, List<int> off)
{
int max = before.MaxFlag;
int max = before.CountFlag;
for (int i = 0; i < max; i++)
{
var b = before.GetFlag(i);
@ -90,6 +89,28 @@ namespace PKHeX.Core
}
}
/// <summary>
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventFlag"/> changes.
/// </summary>
/// <param name="before">Data before the event was triggered</param>
/// <param name="after">Data after the event was triggered</param>
/// <param name="on">List of flags that were turned on</param>
/// <param name="off">List of flags that were turned off</param>
public static void DiffSavesSystem(ISystemFlag before, ISystemFlag after, List<int> on, List<int> off)
{
int max = before.CountSystem;
for (int i = 0; i < max; i++)
{
var b = before.GetSystemFlag(i);
var a = after.GetSystemFlag(i);
if (b == a)
continue;
var arr = a ? on : off;
arr.Add(i);
}
}
/// <summary>
/// Compares a <see cref="before"/> and <see cref="after"/> <see cref="IEventWork{T}"/> object of the same type to find <see cref="EventWork{T}"/> changes.
/// </summary>
@ -100,7 +121,7 @@ namespace PKHeX.Core
/// <param name="changes">Summary of the <see cref="EventWork{T}"/> value change</param>
public static void DiffSavesWork<T>(IEventWork<T> before, IEventWork<T> after, List<int> changed, List<string> changes)
{
int max = before.MaxWork;
int max = before.CountWork;
for (int i = 0; i < max; i++)
{
var b = before.GetWork(i);
@ -113,4 +134,4 @@ namespace PKHeX.Core
}
}
}
}
}

View file

@ -11,9 +11,9 @@ namespace PKHeX.Core
{
public readonly IList<EventVarGroup> Work;
public readonly IList<EventVarGroup> Flag;
public readonly IEventWork<T> Block;
public readonly IEventVar<T> Block;
public SplitEventEditor(IEventWork<T> block, IEnumerable<string> work, IEnumerable<string> flag)
public SplitEventEditor(IEventVar<T> block, IEnumerable<string> work, IEnumerable<string> flag)
{
Block = block;
// load lines

View file

@ -54,6 +54,7 @@ namespace PKHeX.Core
SAV7 sav7 => GetExtraSlots7(sav7, all),
SAV7b lgpe => GetExtraSlots7b(lgpe),
SAV8SWSH ss => GetExtraSlots8(ss),
SAV8BS bs => GetExtraSlots8b(bs),
_ => None,
};
@ -201,5 +202,20 @@ namespace PKHeX.Core
return list;
}
private static List<SlotInfoMisc> GetExtraSlots8b(SAV8BS sav)
{
return new()
{
new SlotInfoMisc(sav.Data, 0, 0x96080, true) { Type = StorageSlotType.Daycare },
new SlotInfoMisc(sav.Data, 1, 0x96080 + PokeCrypto.SIZE_8PARTY, true) { Type = StorageSlotType.Daycare },
new SlotInfoMisc(sav.Data, 0, 0x9A8E8 + (0 * PokeCrypto.SIZE_8PARTY), true) { Type = StorageSlotType.Misc },
new SlotInfoMisc(sav.Data, 1, 0x9A8E8 + (1 * PokeCrypto.SIZE_8PARTY), true) { Type = StorageSlotType.Misc },
new SlotInfoMisc(sav.Data, 2, 0x9A8E8 + (2 * PokeCrypto.SIZE_8PARTY), true) { Type = StorageSlotType.Misc },
new SlotInfoMisc(sav.Data, 3, 0x9A8E8 + (3 * PokeCrypto.SIZE_8PARTY), true) { Type = StorageSlotType.Misc },
new SlotInfoMisc(sav.Data, 4, 0x9A8E8 + (4 * PokeCrypto.SIZE_8PARTY), true) { Type = StorageSlotType.Misc },
};
}
}
}

View file

@ -202,6 +202,19 @@
/// Pokémon Shield (NX)
/// </summary>
SH = 45,
// HOME = 46,
// PLA = 47,
/// <summary>
/// Pokémon Brilliant Diamond (NX)
/// </summary>
BD = 48,
/// <summary>
/// Pokémon Shining Pearl (NX)
/// </summary>
SP = 49,
#endregion
// The following values are not actually stored values in pkm data,
@ -379,6 +392,14 @@
/// <see cref="SH"/>
SWSH,
/// <summary>
/// Pokémon Brilliant Diamond &amp; Shining Pearl
/// </summary>
/// <remarks>Used to lump data from the associated games as data assets are shared.</remarks>
/// <see cref="BD"/>
/// <see cref="SP"/>
BDSP,
/// <summary>
/// Generation 1 Games
/// </summary>
@ -437,6 +458,7 @@
/// Generation 8 Games
/// </summary>
/// <see cref="SWSH"/>
/// <see cref="BDSP"/>
Gen8,
/// <summary>

View file

@ -80,6 +80,7 @@ namespace PKHeX.Core
var list = s.gamelist;
var games = new byte[]
{
48, 49, // 8 bdsp
44, 45, // 8 swsh
42, 43, // 7 gg
30, 31, // 7 sm

View file

@ -24,6 +24,7 @@ namespace PKHeX.Core
public readonly string[] metSM_00000, metSM_30000, metSM_40000, metSM_60000;
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;
// Misc
public readonly string[] wallpapernames, puffs, walkercourses;
@ -136,6 +137,11 @@ namespace PKHeX.Core
metSWSH_40000 = Get("swsh_40000");
metSWSH_60000 = Get("swsh_60000");
metBDSP_00000 = Get("bdsp_00000");
metBDSP_30000 = Get("bdsp_30000");
metBDSP_40000 = Get("bdsp_40000");
metBDSP_60000 = Get("bdsp_60000");
Sanitize();
g4items = (string[])itemlist.Clone();
@ -253,6 +259,7 @@ namespace PKHeX.Core
SanitizeMetG6XY();
SanitizeMetG7SM();
SanitizeMetG8SWSH();
SanitizeMetG8BDSP();
if (lang is "es" or "it")
{
@ -386,6 +393,50 @@ namespace PKHeX.Core
// metSWSH_30000[17] += " (-)"; // Pokémon HOME -- duplicate with 40000's entry
}
private void SanitizeMetG8BDSP()
{
metBDSP_30000[1] += $" ({NPC})"; // Anything from an NPC
metBDSP_30000[2] += $" ({EggName})"; // Egg From Link Trade
Deduplicate(metBDSP_00000);
Deduplicate(metBDSP_30000);
Deduplicate(metBDSP_40000);
Deduplicate(metBDSP_60000);
}
private static void Deduplicate(string[] arr)
{
var counts = new Dictionary<string, int>();
foreach (var s in arr)
{
counts.TryGetValue(s, out var value);
counts[s] = value + 1;
}
#if !DEBUG
var maxCounts = new Dictionary<string, int>(counts);
#endif
for (var i = arr.Length - 1; i >= 0; i--)
{
#if DEBUG
arr[i] += $" ({i:00000})";
#else
var s = arr[i];
var count = counts[s]--;
if (count == 1)
continue;
var format = maxCounts[s] switch
{
>= 100 => " ({0:000})",
>= 10 => " ({0:00})",
_ => " ({0})",
};
arr[i] += string.Format(format, count);
#endif
}
}
public string[] GetItemStrings(int generation, GameVersion game = GameVersion.Any) => generation switch
{
0 => Array.Empty<string>(),
@ -393,9 +444,29 @@ namespace PKHeX.Core
2 => g2items,
3 => GetItemStrings3(game),
4 => g4items, // mail names changed 4->5
8 when game is GameVersion.BD or GameVersion.SP or GameVersion.BDSP => GetItemStrings8b(),
_ => itemlist,
};
private string[] GetItemStrings8b()
{
// Item Indexes
var clone = (string[])itemlist.Clone();
var tm = clone[419][..2];
for (int i = 420; i <= 427; i++)
clone[i] = $"{tm}{i - 420 + 93}";
clone[618] += "(-)"; // TM93
clone[619] += "(-)"; // TM94
clone[620] += "(-)"; // TM95
clone[690] += "(-)"; // TM96
clone[691] += "(-)"; // TM97
clone[692] += "(-)"; // TM98
clone[693] += "(-)"; // TM99
clone[694] += "(-)"; // TM100
return clone;
}
private string[] GetItemStrings3(GameVersion game)
{
switch (game)
@ -487,7 +558,7 @@ namespace PKHeX.Core
5 => GetLocationNames5(bankID),
6 => GetLocationNames6(bankID),
7 => GameVersion.Gen7b.Contains(version) ? GetLocationNames7GG(bankID) : GetLocationNames7(bankID),
8 => GetLocationNames8(bankID),
8 => GameVersion.BDSP.Contains(version) ? GetLocationNames8b(bankID) : GetLocationNames8(bankID),
_ => Array.Empty<string>(),
};
@ -543,5 +614,14 @@ namespace PKHeX.Core
6 => metSWSH_60000,
_ => Array.Empty<string>(),
};
public IReadOnlyList<string> GetLocationNames8b(int bankID) => bankID switch
{
0 => metBDSP_00000,
3 => metBDSP_30000,
4 => metBDSP_40000,
6 => metBDSP_60000,
_ => Array.Empty<string>(),
};
}
}

View file

@ -18,6 +18,7 @@ namespace PKHeX.Core
private readonly List<ComboItem> MetGen7;
private readonly List<ComboItem> MetGen7GG;
private readonly List<ComboItem> MetGen8;
private readonly List<ComboItem> MetGen8b;
private IReadOnlyList<ComboItem>? MetGen4Transfer;
private IReadOnlyList<ComboItem>? MetGen5Transfer;
@ -33,6 +34,7 @@ namespace PKHeX.Core
MetGen7 = CreateGen7(s);
MetGen7GG = CreateGen7GG(s);
MetGen8 = CreateGen8(s);
MetGen8b = CreateGen8b(s);
}
private static List<ComboItem> CreateGen2(GameStrings s)
@ -129,7 +131,7 @@ namespace PKHeX.Core
private static List<ComboItem> CreateGen7GG(GameStrings s)
{
var locations = Util.GetCBList(s.metGG_00000, 0);
Util.AddCBWithOffset(locations, s.metGG_60000, 60001, 60002);
Util.AddCBWithOffset(locations, s.metGG_60000, 60001, Locations.Daycare5);
Util.AddCBWithOffset(locations, s.metGG_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(locations, s.metGG_00000, 00000, Legal.Met_GG_0);
Util.AddCBWithOffset(locations, s.metGG_30000, 30001, Legal.Met_GG_3);
@ -141,7 +143,7 @@ namespace PKHeX.Core
private static List<ComboItem> CreateGen8(GameStrings s)
{
var locations = Util.GetCBList(s.metSWSH_00000, 0);
Util.AddCBWithOffset(locations, s.metSWSH_60000, 60001, 60002);
Util.AddCBWithOffset(locations, s.metSWSH_60000, 60001, Locations.Daycare5);
Util.AddCBWithOffset(locations, s.metSWSH_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(locations, s.metSWSH_00000, 00000, Legal.Met_SWSH_0);
Util.AddCBWithOffset(locations, s.metSWSH_30000, 30001, Legal.Met_SWSH_3);
@ -150,6 +152,19 @@ namespace PKHeX.Core
return locations;
}
private static List<ComboItem> CreateGen8b(GameStrings s)
{
// Manually add invalid (-1) location from SWSH as ID 65535
var locations = new List<ComboItem> { new(s.metSWSH_00000[0], 0xFFFF) };
Util.AddCBWithOffset(locations, s.metBDSP_60000, 60000, Locations.Daycare5);
Util.AddCBWithOffset(locations, s.metBDSP_30000, 30000, Locations.LinkTrade6);
Util.AddCBWithOffset(locations, s.metBDSP_00000, 00000, Legal.Met_BS_0);
Util.AddCBWithOffset(locations, s.metBDSP_30000, 30000, Legal.Met_BS_3);
Util.AddCBWithOffset(locations, s.metBDSP_40000, 40000, Legal.Met_BS_4);
Util.AddCBWithOffset(locations, s.metBDSP_60000, 60000, Legal.Met_BS_6);
return locations;
}
/// <summary>
/// Fetches a Met Location list for a <see cref="version"/> that has been transferred away from and overwritten.
/// </summary>
@ -185,6 +200,7 @@ namespace PKHeX.Core
or GD or SV or C => Partition2(MetGen7, z => z < 234), // Dividing Peak Tunnel
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),
_ => GetLocationListModified(version, currentGen),
};

View file

@ -75,6 +75,7 @@ namespace PKHeX.Core
// Gen8
SW or SH => SWSH,
BD or SP => BDSP,
_ => Invalid,
};
@ -137,6 +138,7 @@ namespace PKHeX.Core
return Legal.MaxSpeciesID_7_USUM;
return Legal.MaxSpeciesID_7_USUM;
}
if (BDSP.Contains(game)) return Legal.MaxSpeciesID_8b;
if (Gen8.Contains(game)) return Legal.MaxSpeciesID_8;
return -1;
}
@ -197,7 +199,8 @@ namespace PKHeX.Core
Gen7b => GG.Contains(g2) || GO == g2,
SWSH => g2 is SW or SH,
Gen8 => SWSH.Contains(g2),
BDSP => g2 is BD or SP,
Gen8 => SWSH.Contains(g2) || BD.Contains(g2),
_ => false,
};
}

View file

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <inheritdoc cref="EncounterArea" />
/// <summary>
/// <see cref="GameVersion.BDSP"/> encounter area
/// </summary>
public sealed record EncounterArea8b : EncounterArea
{
public readonly EncounterSlot8b[] Slots;
protected override IReadOnlyList<EncounterSlot> Raw => Slots;
public static EncounterArea8b[] GetAreas(byte[][] input, GameVersion game)
{
var result = new EncounterArea8b[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea8b(input[i], game);
return result;
}
private EncounterArea8b(byte[] data, GameVersion game) : base(game)
{
Location = data[0] | (data[1] << 8);
Type = (SlotType)data[2];
Slots = ReadSlots(data);
}
private EncounterSlot8b[] ReadSlots(byte[] data)
{
const int size = 4;
int count = (data.Length - 4) / size;
var slots = new EncounterSlot8b[count];
for (int i = 0; i < slots.Length; i++)
{
int offset = 4 + (size * i);
ushort SpecForm = BitConverter.ToUInt16(data, offset);
int species = SpecForm & 0x3FF;
int form = SpecForm >> 11;
int min = data[offset + 2];
int max = data[offset + 3];
slots[i] = new EncounterSlot8b(this, species, form, min, max);
}
return slots;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
foreach (var slot in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (!slot.IsLevelWithinRange(pkm.Met_Level))
break;
if (slot.Form != evo.Form && slot.Species is not ((int)Species.Furfrou or (int)Species.Oricorio))
{
if (!slot.IsRandomUnspecificForm) // Minior, etc
break;
}
yield return slot;
break;
}
}
}
}
}

View file

@ -24,7 +24,7 @@ namespace PKHeX.Core
B, W, B2, W2,
X, Y, OR, AS,
SN, MN, US, UM,
SW, SH,
SW, SH, BD, SP,
GS,
};

View file

@ -50,6 +50,8 @@ namespace PKHeX.Core
// 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"));
public static IReadOnlyList<byte> GetPPTable(PKM pkm, int format)
{
@ -72,6 +74,13 @@ namespace PKHeX.Core
_ => Array.Empty<byte>(),
};
public static ICollection<int> GetDummiedMovesHashSet(PKM pkm) => pkm switch
{
PK8 => DummiedMoves_SWSH,
PB8 => DummiedMoves_BDSP,
_ => Array.Empty<int>(),
};
internal static int GetMaxSpeciesOrigin(PKM pkm)
{
if (pkm.Format == 1)

View file

@ -30,6 +30,9 @@ namespace PKHeX.Core
/// <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<WB8> MGDB_G8B { get; private set; } = Array.Empty<WB8>();
/// <summary>Indicates if the databases are initialized.</summary>
public static bool Initialized => MGDB_G3.Count != 0;
@ -41,6 +44,7 @@ namespace PKHeX.Core
private static WB7[] GetWB7DB(byte[] bin) => Get(bin, WB7.SizeFull, d => new WB7(d));
private static WC8[] GetWC8DB(byte[] bin) => Get(bin, WC8.Size, d => new WC8(d));
private static WB8[] GetWB8DB(byte[] bin) => Get(bin, WB8.Size, d => new WB8(d));
private static T[] Get<T>(byte[] bin, int size, Func<byte[], T> ctor)
{
@ -63,6 +67,7 @@ namespace PKHeX.Core
ICollection<WC7> g7 = GetWC7DB(Util.GetBinaryResource("wc7.pkl"), Util.GetBinaryResource("wc7full.pkl"));
ICollection<WB7> b7 = GetWB7DB(Util.GetBinaryResource("wb7full.pkl"));
ICollection<WC8> g8 = GetWC8DB(Util.GetBinaryResource("wc8.pkl"));
ICollection<WB8> b8 = GetWB8DB(Util.GetBinaryResource("wb8.pkl"));
foreach (var gift in paths.Where(Directory.Exists).SelectMany(MysteryUtil.GetGiftsFromFolder))
{
@ -81,6 +86,7 @@ namespace PKHeX.Core
case WC7 wc7: AddOrExpand(ref g7, wc7); continue;
case WB7 wb7: AddOrExpand(ref b7, wb7); continue;
case WC8 wc8: AddOrExpand(ref g8, wc8); continue;
case WB8 wb8: AddOrExpand(ref b8, wb8); continue;
}
}
@ -102,6 +108,7 @@ namespace PKHeX.Core
MGDB_G7 = SetArray(g7);
MGDB_G7GG = SetArray(b7);
MGDB_G8 = SetArray(g8);
MGDB_G8B = SetArray(b8);
}
public static IEnumerable<MysteryGift> GetAllEvents(bool sorted = true)
@ -114,6 +121,7 @@ namespace PKHeX.Core
MGDB_G7,
MGDB_G7GG,
MGDB_G8,
MGDB_G8B,
}.SelectMany(z => z);
regular = regular.Where(mg => !mg.IsItem && mg.IsPokémon && mg.Species > 0);
var result = MGDB_G3.Concat(regular);

View file

@ -0,0 +1,103 @@
using static PKHeX.Core.EncounterUtil;
using static PKHeX.Core.Shiny;
using static PKHeX.Core.GameVersion;
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);
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 =
{
// Gifts
new (BDSP) { Gift = true, Species = 387, Level = 05, Location = 323 }, // Turtwig
new (BDSP) { Gift = true, Species = 390, Level = 05, Location = 323 }, // Chimchar
new (BDSP) { Gift = true, Species = 393, Level = 05, Location = 323 }, // Piplup
new (BDSP) { Gift = true, Species = 133, Level = 05, Location = 104 }, // Eevee
new (BDSP) { Gift = true, Species = 440, Level = 01, EggLocation = 60007, EggCycles = 40 }, // Happiny Egg from Traveling Man
new (BDSP) { Gift = true, Species = 447, Level = 01, EggLocation = 60005, EggCycles = 25 }, // Riolu Egg from Riley
// Fossils
new (BDSP) { Gift = true, Species = 138, Level = 01, Location = 049, FlawlessIVCount = 3 }, // Omanyte
new (BDSP) { Gift = true, Species = 140, Level = 01, Location = 049, FlawlessIVCount = 3 }, // Kabuto
new (BDSP) { Gift = true, Species = 142, Level = 01, Location = 049, FlawlessIVCount = 3 }, // Aerodactyl
new (BDSP) { Gift = true, Species = 345, Level = 01, Location = 049, FlawlessIVCount = 3 }, // Lileep
new (BDSP) { Gift = true, Species = 347, Level = 01, Location = 049, FlawlessIVCount = 3 }, // Anorith
new (BDSP) { Gift = true, Species = 408, Level = 01, Location = 049, FlawlessIVCount = 3 }, // Cranidos
new (BDSP) { Gift = true, Species = 410, Level = 01, Location = 049, FlawlessIVCount = 3 }, // Shieldon
// Game-specific gifts
new (BDSP) { Gift = true, Species = 151, Level = 01, Ability = 2, Location = 438, Shiny = Never, FlawlessIVCount = 3, Fateful = true }, // Mew
new (BDSP) { Gift = true, Species = 385, Level = 05, Ability = 2, Location = 438, Shiny = Never, FlawlessIVCount = 3, Fateful = true }, // Jirachi
// Stationary
new (BDSP) { Species = 425, Level = 22, Location = 197 }, // Drifloon
new (BDSP) { Species = 442, Level = 25, Location = 367 }, // Spiritomb
new (BDSP) { Species = 479, Level = 15, Location = 311 }, // Rotom
// Roamers
new (BDSP) { Species = 481, Level = 50, FlawlessIVCount = 3, Roaming = true }, // Mesprit
new (BDSP) { Species = 488, Level = 50, FlawlessIVCount = 3, Roaming = true }, // Cresselia
// Legendary
new (BDSP) { Species = 480, Level = 50, Location = 331, FlawlessIVCount = 3 }, // Uxie
new (BDSP) { Species = 482, Level = 50, Location = 328, FlawlessIVCount = 3 }, // Azelf
new (BD ) { Species = 483, Level = 47, Location = 216, FlawlessIVCount = 3 }, // Dialga
new ( SP) { Species = 484, Level = 47, Location = 217, FlawlessIVCount = 3 }, // Palkia
new (BDSP) { Species = 485, Level = 70, Location = 262, FlawlessIVCount = 3 }, // Heatran
new (BDSP) { Species = 486, Level = 70, Location = 291, FlawlessIVCount = 3 }, // Regigigas
new (BDSP) { Species = 487, Level = 70, Location = 266, FlawlessIVCount = 3 }, // Giratina
// Mythical
//new (BDSP) { Species = 491, Level = 50, Location = 333, FlawlessIVCount = 3, Fateful = true }, // Darkrai
//new (BDSP) { Species = 492, Level = 30, Location = 285, FlawlessIVCount = 3, Fateful = true }, // Shaymin
//new (BDSP) { Species = 493, Level = 80, Location = 218, FlawlessIVCount = 3, Fateful = true }, // Arceus
// Ramanas Park (Pure Space)
new ( SP) { Species = 144, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Articuno
new ( SP) { Species = 145, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Zapdos
new ( SP) { Species = 146, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Moltres
new (BD ) { Species = 243, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Raikou
new (BD ) { Species = 244, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Entei
new (BD ) { Species = 245, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Suicune
new (BDSP) { Species = 377, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Regirock
new (BDSP) { Species = 378, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Regice
new (BDSP) { Species = 379, Level = 70, Ability = 4, Location = 506, FlawlessIVCount = 3 }, // Registeel
new (BDSP) { Species = 380, Level = 70, Location = 506, FlawlessIVCount = 3 }, // Latias
new (BDSP) { Species = 381, Level = 70, Location = 506, FlawlessIVCount = 3 }, // Latios
// Ramanas Park (Deep Space)
new (BDSP) { Species = 150, Level = 70, Ability = 4, Location = 507, FlawlessIVCount = 3 }, // Mewtwo
new ( SP) { Species = 249, Level = 70, Ability = 4, Location = 507, FlawlessIVCount = 3 }, // Lugia
new (BD ) { Species = 250, Level = 70, Ability = 4, Location = 507, FlawlessIVCount = 3 }, // Ho-Oh
new (BDSP) { Species = 382, Level = 70, Location = 507, FlawlessIVCount = 3 }, // Kyogre
new (BDSP) { Species = 383, Level = 70, Location = 507, FlawlessIVCount = 3 }, // Groudon
new (BDSP) { Species = 384, Level = 70, Location = 507, FlawlessIVCount = 3 }, // Rayquaza
};
internal static readonly EncounterStatic8b[] StaticBD = GetEncounters(Encounter_BDSP, BD);
internal static readonly EncounterStatic8b[] StaticSP = GetEncounters(Encounter_BDSP, SP);
private const string tradeBDSP = "tradebdsp";
private static readonly string[][] TradeBDSP = Util.GetLanguageStrings10(tradeBDSP, "zh2");
internal static readonly EncounterTrade8b[] TradeGift_BDSP =
{
new (BDSP) { Species = 063, Level = 09, Ability = 1, Gender = 0, OTGender = 0, TID = 25643, IVs = new[] {28,10,09,31,11,03}, Moves = new[] {100,000,000,000}, HeightScalar = 029, WeightScalar = 202, Nature = Nature.Quiet }, // Abra
new (BDSP) { Species = 441, Level = 15, Ability = 2, Gender = 1, OTGender = 0, TID = 44142, IVs = new[] {17,08,29,25,17,23}, Moves = new[] {448,047,064,045}, HeightScalar = 088, WeightScalar = 091, Nature = Nature.Lonely }, // Chatot
new (BDSP) { Species = 093, Level = 33, Ability = 1, Gender = 0, OTGender = 0, TID = 19248, IVs = new[] {18,24,28,02,22,30}, Moves = new[] {247,371,389,109}, HeightScalar = 096, WeightScalar = 208, Nature = Nature.Hasty }, // Haunter
new (BDSP) { Species = 129, Level = 45, Ability = 1, Gender = 1, OTGender = 0, TID = 53277, IVs = new[] {03,03,31,02,11,03}, Moves = new[] {150,000,000,000}, HeightScalar = 169, WeightScalar = 068, Nature = Nature.Mild }, // Magikarp
};
}
}

View file

@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary>
/// Encounter Slot found in <see cref="GameVersion.BDSP"/>.
/// </summary>
/// <inheritdoc cref="EncounterSlot"/>
public sealed record EncounterSlot8b : EncounterSlot
{
public override int Generation => 8;
public bool IsUnderground => Area.Location is (>= 508 and <= 617);
public bool IsMarsh => Area.Location is (>= 219 and <= 224);
public EncounterSlot8b(EncounterArea area, int species, int form, int min, int max) : base(area, species, form, min, max)
{
}
protected override void SetFormatSpecificData(PKM pk)
{
if (IsUnderground)
{
if (GetBaseEggMove(out int move1))
pk.RelearnMove1 = move1;
}
else if (IsMarsh)
{
pk.Ball = (int)Ball.Safari;
}
pk.SetRandomEC();
}
public bool CanBeUndergroundMove(int move)
{
var et = EvolutionTree.Evolves8b;
var sf = et.GetBaseSpeciesForm(Species, Form);
var species = sf & 0x7FF;
var form = sf >> 11;
if (IgnoreEggMoves.TryGetValue(species, out var exclude) && Array.IndexOf(exclude, move) != -1)
return false;
var baseEgg = MoveEgg.GetEggMoves(8, species, form, Version);
return baseEgg.Length == 0 || Array.IndexOf(baseEgg, move) >= 0;
}
public bool GetBaseEggMove(out int move)
{
var et = EvolutionTree.Evolves8b;
var sf = et.GetBaseSpeciesForm(Species, Form);
var species = sf & 0x7FF;
var form = sf >> 11;
int[] Exclude = IgnoreEggMoves.TryGetValue(species, out var exclude) ? exclude : Array.Empty<int>();
var baseEgg = MoveEgg.GetEggMoves(8, species, form, Version);
if (baseEgg.Length == 0)
{
move = 0;
return false;
}
var rnd = Util.Rand;
while (true)
{
var index = rnd.Next(baseEgg.Length);
move = baseEgg[index];
if (Array.IndexOf(Exclude, move) == -1)
return true;
}
}
private static readonly Dictionary<int, int[]> IgnoreEggMoves = new()
{
{004, new[] {394}}, // Charmander
{016, new[] {403}}, // Pidgey
{019, new[] {044}}, // Rattata
{027, new[] {229}}, // Sandshrew
{037, new[] {180,050,326}}, // Vulpix
{050, new[] {310}}, // Diglett
{056, new[] {370}}, // Mankey
{058, new[] {242,336,394}}, // Growlithe
{060, new[] {061,341}}, // Poliwag
{066, new[] {282}}, // Machop
{077, new[] {172}}, // Ponyta
{079, new[] {428}}, // Slowpoke
{083, new[] {348}}, // Farfetchd
{084, new[] {098,283}}, // Doduo
{086, new[] {227}}, // Seel
{098, new[] {175,021}}, // Krabby
{102, new[] {235}}, // Exeggcute
{108, new[] {187}}, // Lickitung
{109, new[] {194}}, // Koffing
{113, new[] {270}}, // Chansey
{114, new[] {072}}, // Tangela
{115, new[] {023,116}}, // Kangaskhan
{116, new[] {225}}, // Horsea
{122, new[] {102,298}}, // Mr. Mime
{127, new[] {450,276}}, // Pinsir
{133, new[] {204,343}}, // Eevee
{140, new[] {341}}, // Kabuto
{143, new[] {122,562}}, // Snorlax
{147, new[] {349,407}}, // Dratini
{152, new[] {267,312,034}}, // Chikorita
{155, new[] {098,038}}, // Cyndaquil
{158, new[] {242,037,056}}, // Totodile
{161, new[] {179}}, // Sentret
{170, new[] {175}}, // Chinchou
{173, new[] {150}}, // Cleffa
{179, new[] {036,268}}, // Mareep
{183, new[] {276}}, // Marill
{187, new[] {388}}, // Hoppip
{190, new[] {103,097}}, // Aipom
{191, new[] {073,275}}, // Sunkern
{198, new[] {017,372}}, // Murkrow
{200, new[] {180}}, // Misdreavus
{204, new[] {038}}, // Pineco
{206, new[] {246}}, // Dunsparce
{209, new[] {242,423,424,422}}, // Snubbull
{214, new[] {224}}, // Heracross
{216, new[] {313}}, // Teddiursa
{218, new[] {414}}, // Slugma
{220, new[] {036}}, // Swinub
{222, new[] {392}}, // Corsola
{223, new[] {062}}, // Remoraid
{226, new[] {056,469}}, // Mantine
{227, new[] {065,413}}, // Skarmory
{228, new[] {251,424}}, // Houndour
{234, new[] {428}}, // Stantler
{236, new[] {270}}, // Tyrogue
{238, new[] {008}}, // Smoochum
{252, new[] {283,437}}, // Treecko
{255, new[] {179,297}}, // Torchic
{261, new[] {281,389,583}}, // Poochyena
{270, new[] {175,055}}, // Lotad
{276, new[] {413}}, // Taillow
{278, new[] {054,097}}, // Wingull
{283, new[] {453}}, // Surskit
{285, new[] {388,402}}, // Shroomish
{296, new[] {197}}, // Makuhita
{298, new[] {021}}, // Azurill
{299, new[] {335}}, // Nosepass
{300, new[] {252}}, // Skitty
{302, new[] {212}}, // Sableye
{303, new[] {389}}, // Mawile
{304, new[] {442}}, // Aron
{309, new[] {435,422}}, // Electrike
{311, new[] {435,204}}, // Plusle
{312, new[] {435,313}}, // Minun
{313, new[] {227}}, // Volbeat
{314, new[] {227}}, // Illumise
{315, new[] {235}}, // Roselia
{316, new[] {220,441}}, // Gulpin
{320, new[] {034}}, // Wailmer
{322, new[] {281}}, // Numel
{324, new[] {284,499}}, // Torkoal
{325, new[] {428}}, // Spoink
{328, new[] {414}}, // Trapinch
{336, new[] {400}}, // Seviper
{339, new[] {330}}, // Barboach
{341, new[] {283,282}}, // Corphish
{345, new[] {072}}, // Lileep
{352, new[] {050,492}}, // Kecleon
{353, new[] {425,566}}, // Shuppet
{357, new[] {437,235,349,692}}, // Tropius
{359, new[] {389,195}}, // Absol
{363, new[] {205}}, // Spheal
{369, new[] {401}}, // Relicanth
{370, new[] {392}}, // Luvdisc
{387, new[] {074}}, // Turtwig
{390, new[] {612}}, // Chimchar
{393, new[] {056}}, // Piplup
{399, new[] {111,205}}, // Bidoof
{408, new[] {043}}, // Cranidos
{417, new[] {608}}, // Pachirisu
{418, new[] {401}}, // Buizel
{422, new[] {262}}, // Shellos
{425, new[] {194,366}}, // Drifloon
{439, new[] {102,298}}, // Mime Jr.
{442, new[] {425}}, // Spiritomb
{443, new[] {225,328}}, // Gible
{446, new[] {122}}, // Munchlax
{449, new[] {303,328}}, // Hippopotas
{451, new[] {400}}, // Skorupi
{456, new[] {175}}, // Finneon
{458, new[] {056,469}}, // Mantyke
{459, new[] {054}}, // Snover
};
}
}

View file

@ -132,7 +132,7 @@ namespace PKHeX.Core
if (Generation >= 4)
{
bool traded = (int)Version == tr.Game;
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(Generation, traded);
pk.Egg_Location = EncounterSuggestion.GetSuggestedEncounterEggLocationEgg(Generation, Version, traded);
pk.EggMetDate = today;
}
pk.Egg_Location = EggLocation;

View file

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.StaticCorrelation8bRequirement;
namespace PKHeX.Core
{
/// <summary>
/// Generation 7 Static Encounter
/// </summary>
/// <inheritdoc cref="EncounterStatic"/>
public sealed record EncounterStatic8b : EncounterStatic, IRelearn, IStaticCorrelation8b
{
public override int Generation => 8;
public IReadOnlyList<int> Relearn { get; init; } = Array.Empty<int>();
public bool Roaming { get; init; } = false;
public EncounterStatic8b(GameVersion game) : base(game)
{
EggLocation = Locations.Default8bNone;
}
protected override bool IsMatchLocation(PKM pkm)
{
if (!Roaming)
return base.IsMatchLocation(pkm);
return Roaming_MetLocation_BDSP.Contains(pkm.Met_Location);
}
public StaticCorrelation8bRequirement GetRequirement(PKM pk) => Roaming
? MustHave
: MustNotHave;
public bool IsStaticCorrelationCorrect(PKM pk)
{
return Roaming8bRNG.ValidateRoamingEncounter(pk, Shiny == Shiny.Random ? Shiny.FixedValue : Shiny, FlawlessIVCount);
}
protected override bool IsMatchEggLocation(PKM pkm)
{
var eggloc = (short)pkm.Egg_Location;
if (!EggEncounter)
return eggloc == (short)EggLocation;
if (!pkm.IsEgg) // hatched
return eggloc == (short)EggLocation || eggloc == Locations.LinkTrade6NPC;
// Unhatched:
if (eggloc != (short)EggLocation)
return false;
if ((short)pkm.Met_Location is not (Locations.Default8bNone or Locations.LinkTrade6NPC))
return false;
return true;
}
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
{
pk.Met_Location = pk.Egg_Location = Locations.Default8bNone;
base.ApplyDetails(sav, criteria, pk);
var req = GetRequirement(pk);
if (req != MustHave)
{
pk.SetRandomEC();
return;
}
var shiny = Shiny == Shiny.Random ? Shiny.FixedValue : Shiny;
Roaming8bRNG.ApplyDetails(pk, criteria, shiny, FlawlessIVCount);
}
protected override void SetMetData(PKM pk, int level, DateTime today)
{
pk.Met_Level = level;
pk.Met_Location = !Roaming ? Location : Roaming_MetLocation_BDSP[0];
pk.MetDate = today;
}
private static readonly int[] Roaming_MetLocation_BDSP =
{
197, 201, 354, 355, 356, 357, 358, 359, 361, 362, 364, 365, 367, 373, 375, 377,
378, 379, 383, 385, 392, 394, 395, 397, 400, 403, 404, 407, 411, 412, 414, 416,
420,
};
}
public interface IStaticCorrelation8b
{
StaticCorrelation8bRequirement GetRequirement(PKM pk);
bool IsStaticCorrelationCorrect(PKM pk);
}
public enum StaticCorrelation8bRequirement
{
CanBeEither,
MustHave,
MustNotHave,
}
}

View file

@ -0,0 +1,54 @@
namespace PKHeX.Core
{
/// <summary>
/// Generation 7 Trade Encounter
/// </summary>
/// <inheritdoc cref="EncounterTrade"/>
public sealed record EncounterTrade8b : EncounterTrade, IContestStats, IScaledSize, IFixedOTFriendship
{
public override int Generation => 8;
public override int Location => Locations.LinkTrade6NPC;
public EncounterTrade8b(GameVersion game) : base(game) => EggLocation = unchecked((ushort)Locations.Default8bNone);
public byte CNT_Cool => BaseContest;
public byte CNT_Beauty => BaseContest;
public byte CNT_Cute => BaseContest;
public byte CNT_Smart => BaseContest;
public byte CNT_Tough => BaseContest;
public byte CNT_Sheen => 0;
public int OT_Friendship => Species == (int)Core.Species.Chatot ? 35 : 50;
private byte BaseContest => Species == (int)Core.Species.Chatot ? (byte)20 : (byte)0;
public int HeightScalar { get; set; }
public int WeightScalar { get; set; }
public override bool IsMatchExact(PKM pkm, DexLevel evo)
{
if (pkm is IContestStats s && s.IsContestBelow(this))
return false;
if (pkm is IScaledSize h && h.HeightScalar != HeightScalar)
return false;
if (pkm is IScaledSize w && w.WeightScalar != WeightScalar)
return false;
return base.IsMatchExact(pkm, evo);
}
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
{
base.ApplyDetails(sav, criteria, pk);
var pb8 = (PB8)pk;
// Has German Language ID for all except German origin, which is English
if (Species == (int)Core.Species.Magikarp)
pb8.Language = (int)(pb8.Language == (int)LanguageID.German ? LanguageID.English : LanguageID.German);
// All other trades received: English games have a Japanese language ID instead of English.
else if (pb8.Language == (int)LanguageID.English)
pb8.Language = (int)LanguageID.Japanese;
this.CopyContestStatsTo(pb8);
pb8.HT_Language = sav.Language;
pb8.HeightScalar = HeightScalar;
pb8.WeightScalar = WeightScalar;
pb8.SetRandomEC();
}
}
}

View file

@ -17,6 +17,7 @@ namespace PKHeX.Core
return pkm.Version switch
{
(int)GameVersion.GO => EncounterGenerator7.GetEncountersGO(pkm, chain),
(int)GameVersion.BD or (int)GameVersion.SP => EncounterGenerator8b.GetEncounters(pkm, chain),
_ => GetEncountersMainline(pkm, chain),
};
}

View file

@ -0,0 +1,90 @@
using System.Collections.Generic;
using static PKHeX.Core.MysteryGiftGenerator;
using static PKHeX.Core.EncounterTradeGenerator;
using static PKHeX.Core.EncounterSlotGenerator;
using static PKHeX.Core.EncounterStaticGenerator;
using static PKHeX.Core.EncounterEggGenerator;
using static PKHeX.Core.EncounterMatchRating;
namespace PKHeX.Core
{
internal static class EncounterGenerator8b
{
public static IEnumerable<IEncounterable> GetEncounters(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
int ctr = 0;
if (pkm.FatefulEncounter)
{
foreach (var z in GetValidGifts(pkm, chain))
{ yield return z; ++ctr; }
if (ctr != 0) yield break;
}
if (Locations.IsEggLocationBred8b(pkm.Egg_Location))
{
foreach (var z in GenerateEggs(pkm, 8))
{ yield return z; ++ctr; }
if (ctr == 0) yield break;
}
IEncounterable? cache = null;
EncounterMatchRating rating = None;
// Trades
if (pkm.Met_Location == Locations.LinkTrade6NPC)
{
foreach (var z in GetValidEncounterTrades(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;
yield break;
}
// 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

@ -145,7 +145,7 @@ namespace PKHeX.Core
var format = pk.Format;
if (format is 2 && version is GameVersion.RD or GameVersion.GN or GameVersion.BU or GameVersion.YW)
format = 1; // try excluding baby pokemon from our evolution chain, for move learning purposes.
var et = EvolutionTree.GetEvolutionTree(format);
var et = EvolutionTree.GetEvolutionTree(pk, format);
var chain = et.GetValidPreEvolutions(pk, maxLevel: 100, skipChecks: true);
int[] needs = GetNeededMoves(pk, moves, chain);
@ -407,6 +407,8 @@ namespace PKHeX.Core
yield return slot;
else if (needs.Count == 1 && slot is EncounterSlot6AO {CanDexNav: true} dn && dn.CanBeDexNavMove(needs[0]))
yield return slot;
else if (needs.Count == 1 && slot is EncounterSlot8b {IsUnderground: true} ug && ug.CanBeUndergroundMove(needs[0]))
yield return slot;
else if (slot.Generation <= 2 && !needs.Except(MoveLevelUp.GetEncounterMoves(slot.Species, 0, slot.LevelMin, slot.Version)).Any())
yield return slot;
}

View file

@ -13,6 +13,7 @@ using static PKHeX.Core.Encounters6;
using static PKHeX.Core.Encounters7;
using static PKHeX.Core.Encounters7b;
using static PKHeX.Core.Encounters8;
using static PKHeX.Core.Encounters8b;
using static PKHeX.Core.EncountersGO;
using static PKHeX.Core.GameVersion;
@ -43,7 +44,7 @@ namespace PKHeX.Core
private static IEnumerable<EncounterSlot> GetRawEncounterSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion gameSource)
{
if (pkm.Egg_Location != 0 || pkm.IsEgg)
if (!Locations.IsNoneLocation(gameSource, pkm.Egg_Location) || pkm.IsEgg)
yield break;
var possibleAreas = GetEncounterAreas(pkm, gameSource);
@ -150,6 +151,8 @@ namespace PKHeX.Core
GO => GetEncounterTableGO(pkm),
SW => SlotsSW,
SH => SlotsSH,
BD => SlotsBD,
SP => SlotsSP,
_ => Array.Empty<EncounterArea>(),
};

View file

@ -13,6 +13,7 @@ using static PKHeX.Core.Encounters6;
using static PKHeX.Core.Encounters7;
using static PKHeX.Core.Encounters7b;
using static PKHeX.Core.Encounters8;
using static PKHeX.Core.Encounters8b;
using static PKHeX.Core.GameVersion;
@ -172,6 +173,8 @@ namespace PKHeX.Core
SW => StaticSW,
SH => StaticSH,
BD => StaticBD,
SP => StaticSP,
_ => Array.Empty<EncounterStatic>(),
};

View file

@ -95,6 +95,7 @@ namespace PKHeX.Core
US or UM => Encounters7.TradeGift_USUM,
GP or GE => Encounters7b.TradeGift_GG,
SW or SH => Encounters8.TradeGift_SWSH,
BD or SP => Encounters8b.TradeGift_BDSP,
_ => Array.Empty<EncounterTrade>(),
};
}

View file

@ -39,7 +39,7 @@ namespace PKHeX.Core
5 => MGDB_G5,
6 => MGDB_G6,
7 => pkm.LGPE ? MGDB_G7GG : MGDB_G7,
8 => MGDB_G8,
8 => pkm.BDSP ? MGDB_G8B : MGDB_G8,
_ => Array.Empty<MysteryGift>(),
};

View file

@ -50,14 +50,15 @@ namespace PKHeX.Core
public static int GetSuggestedEncounterEggLocationEgg(PKM pkm, bool traded = false)
{
return GetSuggestedEncounterEggLocationEgg(pkm.Generation, traded);
return GetSuggestedEncounterEggLocationEgg(pkm.Generation, (GameVersion)pkm.Version, traded);
}
public static int GetSuggestedEncounterEggLocationEgg(int generation, bool traded = false) => generation switch
public static int GetSuggestedEncounterEggLocationEgg(int generation, GameVersion version, bool traded = false) => generation switch
{
1 or 2 or 3 => 0,
4 => traded ? Locations.LinkTrade4 : Locations.Daycare4,
5 => traded ? Locations.LinkTrade5 : Locations.Daycare5,
8 when BDSP.Contains(version)=> traded ? Locations.LinkTrade6NPC : Locations.Daycare8b,
_ => traded ? Locations.LinkTrade6 : Locations.Daycare5,
};
@ -100,6 +101,7 @@ namespace PKHeX.Core
GSC or RBY => pkm.Met_Level == 0 ? 0 : Locations.HatchLocationC,
SW or SH => Locations.HatchLocation8,
BD or SP => Locations.HatchLocation8b,
_ => -1,
};

View file

@ -81,6 +81,7 @@ namespace PKHeX.Core
5 => pkm.IsEgg ? VerifyUnhatchedEgg(pkm, Locations.LinkTrade5) : VerifyEncounterEgg5(pkm),
6 => pkm.IsEgg ? VerifyUnhatchedEgg(pkm, Locations.LinkTrade6) : VerifyEncounterEgg6(pkm),
7 => pkm.IsEgg ? VerifyUnhatchedEgg(pkm, Locations.LinkTrade6) : VerifyEncounterEgg7(pkm),
8 when GameVersion.BDSP.Contains((GameVersion)pkm.Version) => pkm.IsEgg ? VerifyUnhatchedEgg(pkm, Locations.LinkTrade6NPC, Locations.Default8bNone) : VerifyEncounterEgg8BDSP(pkm),
8 => pkm.IsEgg ? VerifyUnhatchedEgg(pkm, Locations.LinkTrade6) : VerifyEncounterEgg8(pkm),
_ => new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter),
};
@ -199,6 +200,15 @@ namespace PKHeX.Core
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
}
private static CheckResult VerifyEncounterEgg8BDSP(PKM pkm)
{
if (pkm.BDSP)
return VerifyEncounterEggLevelLoc(pkm, 1, Legal.ValidMet_BDSP);
// no other games
return new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
}
private static CheckResult VerifyEncounterEggLevelLoc(PKM pkm, int eggLevel, ICollection<int> MetLocations)
{
if (pkm.Met_Level != eggLevel)
@ -208,7 +218,7 @@ namespace PKHeX.Core
: new CheckResult(Severity.Invalid, LEggLocationInvalid, CheckIdentifier.Encounter);
}
private static CheckResult VerifyUnhatchedEgg(PKM pkm, int tradeLoc)
private static CheckResult VerifyUnhatchedEgg(PKM pkm, int tradeLoc, short noneLoc = 0)
{
var eggLevel = pkm.Format < 5 ? 0 : 1;
if (pkm.Met_Level != eggLevel)
@ -219,7 +229,7 @@ namespace PKHeX.Core
var met = pkm.Met_Location;
if (met == tradeLoc)
return new CheckResult(Severity.Valid, LEggLocationTrade, CheckIdentifier.Encounter);
return met == 0
return (short)met == noneLoc
? new CheckResult(Severity.Valid, LEggUnhatched, CheckIdentifier.Encounter)
: new CheckResult(Severity.Invalid, LEggLocationNone, CheckIdentifier.Encounter);
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using static PKHeX.Core.LegalityCheckStrings;
using static PKHeX.Core.ParseSettings;
@ -22,7 +21,8 @@ namespace PKHeX.Core
{
IRelearn s when s.Relearn.Count != 0 => VerifyRelearnSpecifiedMoveset(pkm, s.Relearn, result),
EncounterEgg e => VerifyEggMoveset(e, result, pkm.RelearnMoves),
EncounterSlot6AO z when pkm.RelearnMove1 != 0 && z.CanDexNav => VerifyRelearnDexNav(pkm, result),
EncounterSlot6AO z when pkm.RelearnMove1 != 0 && z.CanDexNav => VerifyRelearnDexNav(pkm, result, z),
EncounterSlot8b {IsUnderground:true} u => VerifyRelearnUnderground(pkm, result, u),
_ => VerifyRelearnNone(pkm, result),
};
}
@ -45,13 +45,10 @@ namespace PKHeX.Core
}
}
private static CheckResult[] VerifyRelearnDexNav(PKM pkm, CheckResult[] result)
private static CheckResult[] VerifyRelearnDexNav(PKM pkm, CheckResult[] result, EncounterSlot6AO slot)
{
// DexNav Pokémon can have 1 random egg move as a relearn move.
var baseSpec = EvoBase.GetBaseSpecies(pkm);
var firstRelearn = pkm.RelearnMove1;
var eggMoves = MoveEgg.GetEggMoves(6, baseSpec.Species, baseSpec.Form, GameVersion.OR);
result[0] = Array.IndexOf(eggMoves, firstRelearn) == -1 // not found
result[0] = !slot.CanBeDexNavMove(pkm.RelearnMove1) // not found
? new CheckResult(Severity.Invalid, LMoveRelearnDexNav, CheckIdentifier.RelearnMove)
: DummyValid;
@ -63,6 +60,21 @@ namespace PKHeX.Core
return result;
}
private static CheckResult[] VerifyRelearnUnderground(PKM pkm, CheckResult[] result, EncounterSlot8b slot)
{
// Underground Pokémon can have 1 random egg move as a relearn move.
result[0] = !slot.CanBeUndergroundMove(pkm.RelearnMove1) // not found
? new CheckResult(Severity.Invalid, LMoveRelearnUnderground, CheckIdentifier.RelearnMove)
: DummyValid;
// All other relearn moves must be empty.
result[3] = pkm.RelearnMove4 == 0 ? DummyValid : DummyNone;
result[2] = pkm.RelearnMove3 == 0 ? DummyValid : DummyNone;
result[1] = pkm.RelearnMove2 == 0 ? DummyValid : DummyNone;
return result;
}
private static CheckResult[] VerifyRelearnNone(PKM pkm, CheckResult[] result)
{
// No relearn moves should be present.

View file

@ -23,6 +23,7 @@ namespace PKHeX.Core
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);
internal static readonly EvolutionTree Evolves8b = new(Unpack("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);
@ -32,6 +33,7 @@ namespace PKHeX.Core
// Add in banned evolution data!
Evolves7.FixEvoTreeSM();
Evolves8.FixEvoTreeSS();
Evolves8b.FixEvoTreeBS();
}
public static EvolutionTree GetEvolutionTree(int generation) => generation switch
@ -55,7 +57,7 @@ namespace PKHeX.Core
5 => Evolves5,
6 => Evolves6,
7 => pkm.Version is (int)GO or (int)GP or (int)GE ? Evolves7b : Evolves7,
_ => Evolves8,
_ => pkm.Version is (int)BD or (int)SP ? Evolves8b : Evolves8,
};
private readonly IReadOnlyList<EvolutionMethod[]> Entries;
@ -172,6 +174,12 @@ namespace PKHeX.Core
BanEvo(s, 0, pkm => pkm is IGigantamax {CanGigantamax: true});
}
private void FixEvoTreeBS()
{
// Eevee is programmed to evolve into Glaceon via Ice Stone or level up at the Ice Rock, but Ice Stone is unreleased.
BanEvo((int)Species.Glaceon, 0, pkm => pkm.CurrentLevel == pkm.Met_Level);
}
private void BanEvo(int species, int form, Func<PKM, bool> func)
{
var key = GetLookupKey(species, form);

View file

@ -354,6 +354,7 @@ namespace PKHeX.Core
public static string LMoveSourceSharedF { get; set; } = "Shared Non-Relearn Move in Generation {0}.";
public static string LMoveRelearnDexNav { get; set; } = "Not an expected DexNav move.";
public static string LMoveRelearnUnderground { get; set; } = "Not an expected Underground egg move.";
public static string LMoveRelearnEgg { get; set; } = "Base Egg move.";
public static string LMoveRelearnEggMissing { get; set; } = "Base Egg move missing.";
public static string LMoveRelearnFExpect_0 { get; set; } = "Expected the following Relearn Moves: {0} ({1}";

View file

@ -141,6 +141,14 @@ namespace PKHeX.Core
return LevelUpSWSH[index].GetMoves(lvl);
}
break;
case BD or SP or BDSP:
if (pkm.InhabitedGeneration(8))
{
int index = PersonalTable.SWSH.GetFormIndex(species, form);
return LevelUpBDSP[index].GetMoves(lvl);
}
break;
}
return Array.Empty<int>();
}
@ -165,11 +173,20 @@ namespace PKHeX.Core
internal static IEnumerable<int> GetValidMoves(PKM pkm, IReadOnlyList<EvoCriteria> evoChain, int generation, MoveSourceType types = MoveSourceType.ExternalSources, bool RemoveTransferHM = true)
{
GameVersion version = (GameVersion)pkm.Version;
if (!pkm.IsUntraded)
if (!pkm.IsUntraded && !IsLandlockedFormat(pkm))
version = Any;
return GetValidMoves(pkm, version, evoChain, generation, types: types, RemoveTransferHM: RemoveTransferHM);
}
private static bool IsLandlockedFormat(PKM pkm)
{
if (pkm.BDSP)
return true;
if (pkm.LGPE)
return pkm.Format == 7;
return false;
}
internal static IEnumerable<int> GetValidRelearn(PKM pkm, int species, int form, GameVersion version = Any)
{
return GetValidRelearn(pkm, species, form, Breeding.GetCanInheritMoves(species), version);

View file

@ -43,6 +43,7 @@ namespace PKHeX.Core
GO or GP or GE or GG => Legal.LevelUpGG,
SW or SH or SWSH => Legal.LevelUpSWSH,
BD or SP or BDSP => Legal.LevelUpBDSP,
Gen1 => Legal.LevelUpY,
Gen2 => Legal.LevelUpC,
@ -86,6 +87,7 @@ namespace PKHeX.Core
GO or GP or GE or GG => PersonalTable.GG,
SW or SH or SWSH => PersonalTable.SWSH,
BD or SP or BDSP => PersonalTable.BDSP,
Gen1 => PersonalTable.Y,
Gen2 => PersonalTable.C,

View file

@ -34,7 +34,8 @@ namespace PKHeX.Core
7 when version is SN or MN => GetFormEggMoves(species, form, EggMovesSM),
7 when version is US or UM => GetFormEggMoves(species, form, EggMovesUSUM),
8 => GetFormEggMoves(species, form, EggMovesSWSH),
8 when version is SW or SH => GetFormEggMoves(species, form, EggMovesSWSH),
8 when version is BD or SP => GetFormEggMoves(species, form, EggMovesBDSP),
_ => Array.Empty<int>(),
};
@ -59,6 +60,7 @@ namespace PKHeX.Core
SN or MN when species <= MaxSpeciesID_7 => getMoves(LevelUpSM, PersonalTable.SM),
US or UM => getMoves(LevelUpUSUM, PersonalTable.USUM),
SW or SH => getMoves(LevelUpSWSH, PersonalTable.SWSH),
BD or SP => getMoves(LevelUpBDSP, PersonalTable.BDSP),
_ => Array.Empty<int>(),
};
@ -77,11 +79,22 @@ namespace PKHeX.Core
{
if (gen < 8 || pkm.IsEgg)
return Array.Empty<int>();
var table = PersonalTable.SWSH;
var entry = (PersonalInfoSWSH)table.GetFormEntry(pkm.Species, pkm.Form);
var baseSpecies = entry.HatchSpecies;
var baseForm = entry.HatchFormIndexEverstone;
return GetEggMoves(8, baseSpecies, baseForm, SW);
if (pkm.BDSP)
{
var table = PersonalTable.BDSP;
var entry = (PersonalInfoBDSP)table.GetFormEntry(pkm.Species, pkm.Form);
var baseSpecies = entry.HatchSpecies;
var baseForm = entry.HatchFormIndex;
return GetEggMoves(8, baseSpecies, baseForm, BD);
}
else
{
var table = PersonalTable.SWSH;
var entry = (PersonalInfoSWSH)table.GetFormEntry(pkm.Species, pkm.Form);
var baseSpecies = entry.HatchSpecies;
var baseForm = entry.HatchFormIndexEverstone;
return GetEggMoves(8, baseSpecies, baseForm, SW);
}
}
}
}

View file

@ -9,6 +9,7 @@ namespace PKHeX.Core
public static class MoveLevelUp
{
private static readonly LearnLookup
LearnBDSP = new(PersonalTable.BDSP, LevelUpBDSP, BDSP),
LearnSWSH = new(PersonalTable.SWSH, LevelUpSWSH, SWSH),
LearnSM = new(PersonalTable.SM, LevelUpSM, SM),
LearnUSUM = new(PersonalTable.USUM, LevelUpUSUM, USUM),
@ -213,6 +214,11 @@ namespace PKHeX.Core
if (species > MaxSpeciesID_8)
return LearnNONE;
return LearnSWSH.GetIsLevelUp(species, form, move, maxLevel);
case BD or SP or BDSP:
if (species > MaxSpeciesID_8b)
return LearnNONE;
return LearnBDSP.GetIsLevelUp(species, form, move, maxLevel);
}
return LearnNONE;
}
@ -471,6 +477,11 @@ namespace PKHeX.Core
if (species > MaxSpeciesID_8)
return moves;
return LearnSWSH.AddMoves(moves, species, form, maxLevel);
case BD or SP or BDSP:
if (species > MaxSpeciesID_8b)
return moves;
return LearnBDSP.AddMoves(moves, species, form, maxLevel);
}
return moves;
}

View file

@ -243,6 +243,18 @@ namespace PKHeX.Core
}
}
if (GameVersion.BDSP.Contains(ver))
{
for (int i = 0; i < PersonalInfoSWSH.CountTM; i++)
{
if (Legal.TMHM_BDSP[i] != move)
continue;
if (PersonalTable.BDSP.GetFormEntry(species, form).TMHM[i])
return GameVersion.BDSP;
break;
}
}
return Legal.NONE;
}
@ -260,7 +272,7 @@ namespace PKHeX.Core
break;
if (allowBit)
return GameVersion.SWSH;
if (((PK8)pkm).GetMoveRecordFlag(i))
if (((G8PKM)pkm).GetMoveRecordFlag(i))
return GameVersion.SWSH;
if (i == 12 && species == (int)Species.Calyrex && form == 0) // TR12
return GameVersion.SWSH; // Agility Calyrex without TR glitch.
@ -409,6 +421,9 @@ namespace PKHeX.Core
case GameVersion.Any:
case GameVersion.SW or GameVersion.SH or GameVersion.SWSH:
AddMachineSWSH(r, species, form);
break;
case GameVersion.BD or GameVersion.SP or GameVersion.BDSP:
AddMachineBDSP(r, species, form);
return;
}
}
@ -461,11 +476,20 @@ namespace PKHeX.Core
}
}
private static void AddMachineBDSP(List<int> r, int species, int form)
{
if (species > Legal.MaxSpeciesID_8b)
return;
var pi = PersonalTable.BDSP.GetFormEntry(species, form);
r.AddRange(Legal.TMHM_BDSP.Where((_, m) => pi.TMHM[m]));
}
public static void AddRecordSWSH(List<int> r, int species, int form, PKM pkm)
{
if (pkm is not PK8 pk8)
return;
var pi = PersonalTable.SWSH.GetFormEntry(species, form);
var tmhm = pi.TMHM;
var pk8 = (PK8)pkm;
for (int i = 0; i < PersonalInfoSWSH.CountTR; i++)
{
var index = i + PersonalInfoSWSH.CountTM;

View file

@ -0,0 +1,73 @@
using System.Runtime.CompilerServices;
namespace PKHeX.Core
{
/// <summary>
/// Self-modifying RNG structure that implements xoroshiro128+ which split-mixes the initial seed to populate all 128-bits of the initial state, rather than using a fixed 64-bit half.
/// </summary>
/// <remarks>https://en.wikipedia.org/wiki/Xoroshiro128%2B</remarks>
/// <seealso cref="Xoroshiro128Plus"/>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Unused")]
public ref struct Xoroshiro128Plus8b
{
private ulong s0, s1;
public Xoroshiro128Plus8b(ulong seed)
{
var _s0 = seed - 0x61C8864680B583EB;
var _s1 = seed + 0x3C6EF372FE94F82A;
_s0 = 0xBF58476D1CE4E5B9 * (_s0 ^ (_s0 >> 30));
_s1 = 0xBF58476D1CE4E5B9 * (_s1 ^ (_s1 >> 30));
_s0 = 0x94D049BB133111EB * (_s0 ^ (_s0 >> 27));
_s1 = 0x94D049BB133111EB * (_s1 ^ (_s1 >> 27));
s0 = _s0 ^ (_s0 >> 31);
s1 = _s1 ^ (_s1 >> 31);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong RotateLeft(ulong x, int k)
{
return (x << k) | (x >> (64 - k));
}
/// <summary>
/// Gets the next random <see cref="ulong"/>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ulong Next()
{
var _s0 = s0;
var _s1 = s1;
ulong result = _s0 + _s1;
_s1 ^= _s0;
// Final calculations and store back to fields
s0 = RotateLeft(_s0, 24) ^ _s1 ^ (_s1 << 16);
s1 = RotateLeft(_s1, 37);
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool NextBool() => (Next() >> 63) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte NextByte() => (byte)(Next() >> 56);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ushort UShort() => (ushort)(Next() >> 48);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint NextUInt() => (uint)(Next() >> 32);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint NextUInt(uint max)
{
var rnd = NextUInt();
return rnd - ((rnd / max) * max);
}
}
}

View file

@ -183,6 +183,12 @@ namespace PKHeX.Core
/// <remarks>Formulaic based on PID &amp; EC values from a 32bit-seed.</remarks>
Overworld8,
/// <summary>
/// Generation 8b Roaming Pokemon PID
/// </summary>
/// <remarks>Formulaic based on EC value = 32bit-seed.</remarks>
Roaming8b,
#endregion
}

View file

@ -0,0 +1,240 @@
namespace PKHeX.Core
{
/// <summary>
/// Contains logic for the Generation 8b (BD/SP) roaming spawns.
/// </summary>
/// <remarks>
/// Roaming encounters use the pokemon's 32-bit <see cref="PKM.EncryptionConstant"/> as RNG seed.
/// </remarks>
public static class Roaming8bRNG
{
private const int NoMatchIVs = -1;
private const int UNSET = -1;
public static void ApplyDetails(PKM pk, EncounterCriteria criteria, Shiny shiny = Shiny.FixedValue, int flawless = -1)
{
if (shiny == Shiny.FixedValue)
shiny = criteria.Shiny is Shiny.Random or Shiny.Never ? Shiny.Never : Shiny.Always;
if (flawless == -1)
flawless = 0;
int ctr = 0;
const int maxAttempts = 50_000;
var rnd = Util.Rand;
do
{
var seed = Util.Rand32(rnd);
if (TryApplyFromSeed(pk, criteria, shiny, flawless, seed))
return;
} while (++ctr != maxAttempts);
TryApplyFromSeed(pk, EncounterCriteria.Unrestricted, shiny, flawless, Util.Rand32(rnd));
}
private static bool TryApplyFromSeed(PKM pk, EncounterCriteria criteria, Shiny shiny, int flawless, uint seed)
{
var xoro = new Xoroshiro128Plus8b(seed);
// Encryption Constant
pk.EncryptionConstant = seed;
var _ = xoro.NextUInt(); // fakeTID
// PID
var pid = xoro.NextUInt();
if (shiny == Shiny.Never)
{
if (GetIsShiny(pk.TID, pk.SID, pid))
return false;
}
else if (shiny != Shiny.Random)
{
if (!GetIsShiny(pk.TID, pk.SID, pid))
return false;
if (shiny == Shiny.AlwaysSquare && pk.ShinyXor != 0)
return false;
if (shiny == Shiny.AlwaysStar && pk.ShinyXor == 0)
return false;
}
pk.PID = pid;
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.
int[] ivs = { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET };
const int MAX = 31;
var determined = 0;
while (determined < flawless)
{
var idx = xoro.NextUInt(6);
if (ivs[idx] != UNSET)
continue;
ivs[idx] = 31;
determined++;
}
for (var i = 0; i < ivs.Length; i++)
{
if (ivs[i] == UNSET)
ivs[i] = (int)xoro.NextUInt(MAX + 1);
}
if (!criteria.IsIVsCompatible(ivs, 8))
return false;
pk.IV_HP = ivs[0];
pk.IV_ATK = ivs[1];
pk.IV_DEF = ivs[2];
pk.IV_SPA = ivs[3];
pk.IV_SPD = ivs[4];
pk.IV_SPE = ivs[5];
// Ability
pk.SetAbilityIndex((int)xoro.NextUInt(2));
// Remainder
var scale = (IScaledSize)pk;
scale.HeightScalar = (int)xoro.NextUInt(0x81) + (int)xoro.NextUInt(0x80);
scale.WeightScalar = (int)xoro.NextUInt(0x81) + (int)xoro.NextUInt(0x80);
return true;
}
public static bool ValidateRoamingEncounter(PKM pk, Shiny shiny = Shiny.Random, int flawless = 0)
{
var seed = pk.EncryptionConstant;
var xoro = new Xoroshiro128Plus8b(seed);
// Check PID
var _ = xoro.NextUInt(); // fakeTID
var pid = xoro.NextUInt();
if (pk.PID != pid)
return false;
// Check IVs: Create flawless IVs at random indexes, then the random IVs for not flawless.
int[] ivs = { UNSET, UNSET, UNSET, UNSET, UNSET, UNSET };
var determined = 0;
while (determined < flawless)
{
var idx = xoro.NextUInt(6);
if (ivs[idx] != UNSET)
continue;
ivs[idx] = 31;
determined++;
}
for (var i = 0; i < ivs.Length; i++)
{
if (ivs[i] == UNSET)
ivs[i] = (int)xoro.NextUInt(31 + 1);
}
if (ivs[0] != pk.GetIV(0)) return false;
if (ivs[1] != pk.GetIV(1)) return false;
if (ivs[2] != pk.GetIV(2)) return false;
if (ivs[3] != pk.GetIV(4)) return false;
if (ivs[4] != pk.GetIV(5)) return false;
if (ivs[5] != pk.GetIV(3)) return false;
// Don't check Hidden ability, as roaming encounters are 1/2 only.
if (pk.AbilityNumber != (1 << (int)xoro.NextUInt(2)))
return false;
return GetIsMatchEnd(pk, xoro) || GetIsMatchEndWithCuteCharm(pk, xoro) || GetIsMatchEndWithSynchronize(pk, xoro);
}
private static bool GetIsMatchEnd(PKM pk, Xoroshiro128Plus8b xoro)
{
// Check that gender matches
var genderRatio = PersonalTable.BDSP.GetFormEntry(pk.Species, pk.Form).Gender;
if (genderRatio == PersonalInfo.RatioMagicGenderless)
{
if (pk.Gender != (int)Gender.Genderless)
return false;
}
else if (genderRatio == PersonalInfo.RatioMagicMale)
{
if (pk.Gender != (int)Gender.Male)
return false;
}
else if (genderRatio == PersonalInfo.RatioMagicFemale)
{
if (pk.Gender != (int)Gender.Female)
return false;
}
else
{
if (pk.Gender != (((int)xoro.NextUInt(253) + 1 < genderRatio) ? 1 : 0))
return false;
}
// Check that the nature matches
if (pk.Nature != (int)xoro.NextUInt(25))
return false;
return GetIsHeightWeightMatch(pk, xoro);
}
private static bool GetIsMatchEndWithCuteCharm(PKM pk, Xoroshiro128Plus8b xoro)
{
// Check that gender matches
// Assume that the gender is a match due to cute charm.
// Check that the nature matches
if (pk.Nature != (int)xoro.NextUInt(25))
return false;
return GetIsHeightWeightMatch(pk, xoro);
}
private static bool GetIsMatchEndWithSynchronize(PKM pk, Xoroshiro128Plus8b xoro)
{
// Check that gender matches
var genderRatio = PersonalTable.BDSP.GetFormEntry(pk.Species, pk.Form).Gender;
if (genderRatio == PersonalInfo.RatioMagicGenderless)
{
if (pk.Gender != (int)Gender.Genderless)
return false;
}
else if (genderRatio == PersonalInfo.RatioMagicMale)
{
if (pk.Gender != (int)Gender.Male)
return false;
}
else if (genderRatio == PersonalInfo.RatioMagicFemale)
{
if (pk.Gender != (int)Gender.Female)
return false;
}
else
{
if (pk.Gender != (((int)xoro.NextUInt(253) + 1 < genderRatio) ? 1 : 0))
return false;
}
// Assume that the nature is a match due to synchronize.
return GetIsHeightWeightMatch(pk, xoro);
}
private static bool GetIsHeightWeightMatch(PKM pk, Xoroshiro128Plus8b xoro)
{
// Check height/weight
if (pk is not IScaledSize s)
return false;
var height = xoro.NextUInt(0x81) + xoro.NextUInt(0x80);
var weight = xoro.NextUInt(0x81) + xoro.NextUInt(0x80);
return s.HeightScalar == height && s.WeightScalar == weight;
}
private static bool GetIsShiny(int tid, int sid, uint pid)
{
return GetShinyXor(pid, (uint)((sid << 16) | tid)) < 16;
}
private static uint GetShinyXor(uint pid, uint oid)
{
var xor = pid ^ oid;
return (xor ^ (xor >> 16)) & 0xFFFF;
}
}
}

View file

@ -86,10 +86,10 @@ namespace PKHeX.Core
new byte[] { 00, 00, 00, 00, 00, 29, 09, 02, 02 }, // Sylveon (Eevee with Fairy Move)
new byte[] { 00, 00, 00, 00, 18, 15, 15, 02, 32 }, // Mr. Mime (Mime Jr with Mimic)
new byte[] { 00, 00, 00, 00, 17, 17, 15, 02, 16 }, // Sudowoodo (Bonsly with Mimic)
new byte[] { 00, 00, 00, 00, 32, 32, 32, 02, 00 }, // Ambipom (Aipom with Double Hit)
new byte[] { 00, 00, 00, 00, 32, 32, 32, 02, 32 }, // Ambipom (Aipom with Double Hit)
new byte[] { 00, 00, 02, 00, 02, 33, 33, 02, 06 }, // Lickilicky (Lickitung with Rollout)
new byte[] { 00, 00, 00, 00, 02, 36, 38, 02, 24 }, // Tangrowth (Tangela with Ancient Power)
new byte[] { 00, 00, 00, 00, 02, 33, 33, 02, 00 }, // Yanmega (Yanma with Ancient Power)
new byte[] { 00, 00, 00, 00, 02, 33, 33, 02, 33 }, // Yanmega (Yanma with Ancient Power)
new byte[] { 00, 00, 00, 00, 02, 02, 02, 02, 02 }, // Mamoswine (Piloswine with Ancient Power)
new byte[] { 00, 00, 00, 00, 00, 00, 00, 02, 28 }, // Tsareena (Steenee with Stomp)
new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 35 }, // Grapploct (Clobbopus with Taunt)

View file

@ -19,7 +19,7 @@ namespace PKHeX.Core
{
if (pk is PB7 pb7) // no held items in game
return pb7.HeldItem == 0;
return IsHeldItemAllowed(pk.HeldItem, pk.Format);
return IsHeldItemAllowed(pk.HeldItem, pk.Format, pk);
}
/// <summary>
@ -28,15 +28,15 @@ namespace PKHeX.Core
/// <param name="item">Held Item ID</param>
/// <param name="generation">Generation Number</param>
/// <returns>True if able to be held, false if not</returns>
public static bool IsHeldItemAllowed(int item, int generation)
public static bool IsHeldItemAllowed(int item, int generation, PKM pk)
{
if (item == 0)
return true;
var items = GetReleasedHeldItems(generation);
var items = GetReleasedHeldItems(generation, pk);
return (uint)item < items.Count && items[item];
}
private static IReadOnlyList<bool> GetReleasedHeldItems(int generation) => generation switch
private static IReadOnlyList<bool> GetReleasedHeldItems(int generation, PKM pk) => generation switch
{
2 => ReleasedHeldItems_2,
3 => ReleasedHeldItems_3,
@ -44,6 +44,7 @@ namespace PKHeX.Core
5 => ReleasedHeldItems_5,
6 => ReleasedHeldItems_6,
7 => ReleasedHeldItems_7,
8 when pk is PB8 => ReleasedHeldItems_8b,
8 => ReleasedHeldItems_8,
_ => Array.Empty<bool>(),
};

View file

@ -136,7 +136,7 @@ namespace PKHeX.Core
return false;
return gen switch
{
8 => move <= Legal.MaxMoveID_8_R2 && !Legal.DummiedMoves_SWSH.Contains(move),
8 => move <= Legal.MaxMoveID_8_R2 && !Legal.GetDummiedMovesHashSet(pkm).Contains(move),
_ => move <= Legal.MaxMoveID_6_AO,
};
}
@ -148,7 +148,7 @@ namespace PKHeX.Core
private static bool IsSpecialEncounterMoveEggDeleted(PKM pkm, IEncounterable enc)
{
if (pkm is IBattleVersion { BattleVersion: not 0 }) // can hide Relearn moves (Gen6+ Eggs, or DexNav)
return enc is EncounterEgg { Generation: >= 6 } or EncounterSlot6AO { CanDexNav: true };
return enc is EncounterEgg { Generation: >= 6 } or EncounterSlot6AO { CanDexNav: true } or EncounterSlot8b { IsUnderground: true };
return enc is EncounterEgg { Generation: < 6 }; // egg moves that are no longer in the movepool
}

View file

@ -11,6 +11,7 @@
public const int Daycare4 = 2000;
public const int Daycare5 = 60002;
public const int Daycare8b = 60010;
public const int LinkTrade2NPC = 126;
public const int LinkTrade3NPC = 254;
@ -55,6 +56,9 @@
/// <summary> Route 5 in <see cref="GameVersion.SWSH"/> </summary>
public const int HatchLocation8 = 40;
/// <summary> Solaceon Town in <see cref="GameVersion.BDSP"/> </summary>
public const int HatchLocation8b = 446;
/// <summary> Generation 1 -> Generation 7 Transfer Location (Kanto) </summary>
public const int Transfer1 = 30013;
@ -96,6 +100,11 @@
public const int BugCatchingContest4 = 207;
/// <summary>
/// -1
/// </summary>
public const int Default8bNone = -1;
public static int TradedEggLocationNPC(int generation) => generation switch
{
1 => LinkTrade2NPC,
@ -106,10 +115,11 @@
_ => LinkTrade6NPC,
};
public static int TradedEggLocation(int generation) => generation switch
public static int TradedEggLocation(int generation, GameVersion ver) => generation switch
{
4 => LinkTrade4,
5 => LinkTrade5,
8 when GameVersion.BDSP.Contains(ver) => LinkTrade6NPC,
_ => LinkTrade6,
};
@ -134,5 +144,14 @@
public static bool IsEggLocationBred5(int loc) => loc is Daycare5 or LinkTrade5;
public static bool IsEggLocationBred6(int loc) => loc is Daycare5 or LinkTrade6;
public static bool IsEggLocationBred8b(int loc) => loc is Daycare8b or LinkTrade6NPC;
public static bool IsNoneLocation(GameVersion ver, int location) => GetNoneLocation(ver) == (short)location;
public static int GetNoneLocation(GameVersion ver) => ver switch
{
GameVersion.BD or GameVersion.SP => Default8bNone,
_ => 0,
};
}
}

View file

@ -127,6 +127,8 @@ namespace PKHeX.Core
return false;
if (generation is 6 && move is ((int)ThousandArrows or (int)ThousandWaves))
return false;
if (generation is 8 && DummiedMoves_BDSP.Contains(move)) // can't Sketch unusable moves in BDSP
return false;
return true;
}

View file

@ -0,0 +1,350 @@
using System.Collections.Generic;
namespace PKHeX.Core
{
public static partial class Legal
{
internal const int MaxSpeciesID_8b = MaxSpeciesID_4; // Arceus-493
internal const int MaxMoveID_8b = MaxMoveID_8_R2;
internal const int MaxItemID_8b = 1822; // DS Sounds
internal const int MaxBallID_8b = (int)Ball.Beast;
internal const int MaxGameID_8b = (int)GameVersion.SP;
internal const int MaxAbilityID_8b = MaxAbilityID_8_R2;
#region Met Locations
// TODO
internal static readonly int[] Met_BS_0 =
{
000, 001, 002, 003, 004, 005, 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, 044, 045, 046, 047, 048, 049,
050, 051, 052, 053, 054, 055, 056, 057, 058, 059,
060, 061, 062, 063, 064, 065, 066, 067, 068, 069,
070, 071, 072, 073, 074, 075, 076, 077, 078, 079,
080, 081, 082, 083, 084, 085, 086, 087, 088, 089,
090, 091, 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, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
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, 301, 302, 303, 304, 305, 306, 307, 308, 309,
310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
320, 321, 322, 323, 324, 325, 326, 327, 328, 329,
330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
340, 341, 342, 343, 344, 345, 346, 347, 348, 349,
350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
360, 361, 362, 363, 364, 365, 366, 367, 368, 369,
370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
380, 381, 382, 383, 384, 385, 386, 387, 388, 389,
390, 391, 392, 393, 394, 395, 396, 397, 398, 399,
400, 401, 402, 403, 404, 405, 406, 407, 408, 409,
410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
420, 421, 422, 423, 424, 425, 426, 427, 428, 429,
430, 431, 432, 433, 434, 435, 436, 437, 438, 439,
440, 441, 442, 443, 444, 445, 446, 447, 448, 449,
450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
460, 461, 462, 463, 464, 465, 466, 467, 468, 469,
470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
480, 481, 482, 483, 484, 485, 486, 487, 488, 489,
490, 491, 492, 493, 494, 495, 496, 497, 498, 499,
500, 501, 502, 503, 504, 505, 506, 507, 508, 509,
510, 511, 512, 513, 514, 515, 516, 517, 518, 519,
520, 521, 522, 523, 524, 525, 526, 527, 528, 529,
530, 531, 532, 533, 534, 535, 536, 537, 538, 539,
540, 541, 542, 543, 544, 545, 546, 547, 548, 549,
550, 551, 552, 553, 554, 555, 556, 557, 558, 559,
560, 561, 562, 563, 564, 565, 566, 567, 568, 569,
570, 571, 572, 573, 574, 575, 576, 577, 578, 579,
580, 581, 582, 583, 584, 585, 586, 587, 588, 589,
590, 591, 592, 593, 594, 595, 596, 597, 598, 599,
600, 601, 602, 603, 604, 605, 606, 607, 608, 609,
610, 611, 612, 613, 614, 615, 616, 617, 618, 619,
620, 621, 622, 623, 624, 625, 626,
// Skip the secret base met location IDs because no Pokémon can be obtained in them.
// Ramanas Park rooms with lights out
648, 649, 650, 651, 652, 653, 654, 655, 656, 657,
};
internal static readonly int[] Met_BS_3 =
{
30001, 30003, 30004, 30005, 30006, 30007, 30009, 30010, 30011, 30012, 30013, 30014, 30015, 30016, 30017, 30018, 30019, 30020, 30021
};
internal static readonly int[] Met_BS_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,
};
internal static readonly int[] Met_BS_6 = {/* XY */ 60001, 60003, /* ORAS */ 60004, /* BDSP */ 60005, 60006, 60007, 60010 };
#endregion
internal static readonly ushort[] Pouch_Regular_BS =
{
045, 046, 047, 048, 049, 050, 051, 052, 053, 072, 073, 074, 075, 076, 077, 078,
079, 080, 081, 082, 083, 084, 085, 093, 094, 107, 108, 109, 110, 111, 112, 135,
136, 213, 214, 215, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228,
229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
245, 246, 247, 248, 249, 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, 301, 302, 303, 304, 305, 306, 307, 308,
309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
325, 326, 327, 537, 565, 566, 567, 568, 569, 570, 644, 645, 849,
1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244,
1245, 1246, 1247, 1248, 1249, 1250, 1251, 1606,
};
internal static readonly ushort[] Pouch_Ball_BS =
{
001, 002, 003, 004, 005, 006, 007, 008, 009, 010, 011, 012, 013, 014, 015, 016,
492, 493, 494, 495, 496, 497, 498, 499, 500,
576,
851,
};
internal static readonly ushort[] Pouch_Battle_BS =
{
055, 056, 057, 058, 059, 060, 061, 062, 063,
};
internal static readonly ushort[] Pouch_Items_BS = ArrayUtil.ConcatAll(Pouch_Regular_BS, Pouch_Ball_BS, Pouch_Battle_BS);
internal static readonly ushort[] Pouch_Key_BS =
{
428, 431, 432, 433, 438, 439, 440, 443, 445, 446, 447, 448, 449, 450, 451, 452,
453, 454, 459, 460, 461, 462, 463, 464, 466, 467, 631, 632,
1267, 1278, 1822,
// 455 Azure Flute (nonfunctional as of 1.1.0)
};
internal static readonly ushort[] Pouch_Medicine_BS =
{
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, 044, 054,
// 134 Sweet Heart (future event item?)
};
internal static readonly ushort[] Pouch_Berries_BS =
{
149, 150, 151, 152, 153, 154, 155, 156, 157, 158,
159, 160, 161, 162, 163, 169, 170, 171, 172, 173,
174, 184, 185, 186, 187, 188, 189, 190, 191, 192,
193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
203, 204, 205, 206, 207, 208, 209, 210, 211, 212,
686,
};
internal static readonly ushort[] Pouch_Treasure_BS =
{
086, 087, 088, 089, 090, 091, 092, 099, 100, 101, 102, 103, 104, 105, 106, 795, 796,
1808, 1809, 1810, 1811, 1812, 1813, 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821,
};
internal static readonly ushort[] Pouch_TMHM_BS = // TM01-TM100
{
328, 329, 330, 331, 332, 333, 334, 335, 336, 337,
338, 339, 340, 341, 342, 343, 344, 345, 346, 347,
348, 349, 350, 351, 352, 353, 354, 355, 356, 357,
358, 359, 360, 361, 362, 363, 364, 365, 366, 367,
368, 369, 370, 371, 372, 373, 374, 375, 376, 377,
378, 379, 380, 381, 382, 383, 384, 385, 386, 387,
388, 389, 390, 391, 392, 393, 394, 395, 396, 397,
398, 399, 400, 401, 402, 403, 404, 405, 406, 407,
408, 409, 410, 411, 412, 413, 414, 415, 416, 417,
418, 419,
420, 421, 422, 423, 424, 425, 426, 427, // Previously called HM0X, in BDSP they're now called TM93-TM100
};
internal static readonly ushort[] HeldItems_BS = ArrayUtil.ConcatAll(Pouch_Items_BS, Pouch_Berries_BS, Pouch_TMHM_BS, Pouch_Medicine_BS, Pouch_Treasure_BS);
#region Moves
public static readonly int[] TMHM_BDSP =
{
264, 337, 352, 347, 046, 092, 258, 339, 331, 237,
241, 269, 058, 059, 063, 113, 182, 240, 202, 219,
218, 076, 231, 085, 087, 089, 216, 091, 094, 247,
280, 104, 115, 351, 053, 188, 201, 126, 317, 332,
259, 263, 290, 156, 213, 168, 211, 285, 289, 315,
355, 411, 412, 206, 362, 374, 451, 203, 406, 409,
261, 318, 373, 153, 421, 371, 278, 416, 397, 148,
444, 419, 086, 360, 014, 446, 244, 445, 399, 157,
404, 214, 363, 398, 138, 447, 207, 365, 369, 164,
430, 433,
015, 019, 057, 070, 432, 249, 127, 431,
};
/// <summary>
/// Moves that are kill
/// </summary>
public static readonly HashSet<int> DummiedMoves_BDSP = new()
{
002, 003, 004, 013, 026, 027, 041, 049, 082, 096,
099, 112, 117, 119, 121, 125, 128, 131, 132, 140,
145, 146, 149, 158, 159, 169, 171, 185, 193, 216,
218, 222, 228, 265, 274, 287, 289, 290, 293, 300,
301, 302, 316, 318, 320, 324, 327, 346, 357, 358,
363, 373, 376, 377, 378, 381, 382, 386, 426, 429,
443, 445, 456, 466, 477, 481, 485, 498, 507, 508,
516, 518, 519, 520, 527, 531, 532, 533, 535, 537,
539, 541, 543, 544, 545, 546, 547, 548, 549, 550,
551, 552, 553, 554, 557, 558, 559, 560, 561, 563,
567, 569, 570, 571, 576, 578, 582, 587, 588, 591,
592, 593, 594, 600, 601, 603, 606, 607, 610, 613,
614, 615, 616, 617, 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,
669, 671, 674, 676, 677, 678, 680, 681, 683, 685,
686, 687, 688, 689, 690, 691, 693, 695, 696, 697,
698, 699, 700, 701, 702, 703, 704, 705, 706, 708,
709, 711, 712, 713, 714, 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, 777, 778, 779, 780, 781,
782, 783, 784, 785, 786, 787, 788, 789, 790, 792,
793, 794, 795, 796, 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
#region Unreleased Items
internal static readonly bool[] ReleasedHeldItems_8b = GetPermitList(MaxItemID_8b, HeldItems_BS, new ushort[]
{
849, // Ice Stone
005, // Safari Ball
016, // Cherish Ball
500, // Park Ball
576, // Dream Ball
851, // Beast Ball
1808, // Mysterious Shard S
1809, // Mysterious Shard L
1810, // Digger Drill
1811, // Kanto Slate
1812, // Johto Slate
1813, // Soul Slate
1814, // Rainbow Slate
1815, // Squall Slate
1816, // Oceanic Slate
1817, // Tectonic Slate
1818, // Stratospheric Slate
1819, // Genome Slate
1820, // Discovery Slate
1821, // Distortion Slate
});
#endregion
internal static readonly HashSet<int> ValidMet_BDSP = new()
{
000, 001, 002, 003, 004, 005, 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, 044, 045, 046, 047, 048, 049,
050, 051, 052, 053, 054, 055, 056, 057, 058, 059,
060, 061, 062, 063, 064, 065, 066, 067, 068, 069,
070, 071, 072, 073, 074, 075, 076, 077, 078, 079,
080, 081, 082, 083, 084, 085, 086, 087, 088, 089,
090, 091, 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, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
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, 301, 302, 303, 304, 305, 306, 307, 308, 309,
310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
320, 321, 322, 323, 324, 325, 326, 327, 328, 329,
330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
340, 341, 342, 343, 344, 345, 346, 347, 348, 349,
350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
360, 361, 362, 363, 364, 365, 366, 367, 368, 369,
370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
380, 381, 382, 383, 384, 385, 386, 387, 388, 389,
390, 391, 392, 393, 394, 395, 396, 397, 398, 399,
400, 401, 402, 403, 404, 405, 406, 407, 408, 409,
410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
420, 421, 422, 423, 424, 425, 426, 427, 428, 429,
430, 431, 432, 433, 434, 435, 436, 437, 438, 439,
440, 441, 442, 443, 444, 445, 446, 447, 448, 449,
450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
460, 461, 462, 463, 464, 465, 466, 467, 468, 469,
470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
480, 481, 482, 483, 484, 485, 486, 487, 488, 489,
490, 491, 492, 493, 494, 495, 496, 497, 498, 499,
500, 501, 502, 503, 504, 505, 506, 507, 508, 509,
510, 511, 512, 513, 514, 515, 516, 517, 518, 519,
520, 521, 522, 523, 524, 525, 526, 527, 528, 529,
530, 531, 532, 533, 534, 535, 536, 537, 538, 539,
540, 541, 542, 543, 544, 545, 546, 547, 548, 549,
550, 551, 552, 553, 554, 555, 556, 557, 558, 559,
560, 561, 562, 563, 564, 565, 566, 567, 568, 569,
570, 571, 572, 573, 574, 575, 576, 577, 578, 579,
580, 581, 582, 583, 584, 585, 586, 587, 588, 589,
590, 591, 592, 593, 594, 595, 596, 597, 598, 599,
600, 601, 602, 603, 604, 605, 606, 607, 608, 609,
610, 611, 612, 613, 614, 615, 616, 617, 618, 619,
620, 621, 622, 623, 624, 625, 626,
// Ramanas Park rooms with lights out
648, 649, 650, 651, 652, 653, 654, 655, 656, 657,
};
}
}

View file

@ -983,5 +983,34 @@ namespace PKHeX.Core
(int)Voltorb, // Voltorb
(int)Flabébé + (3 << 11), // Flabébé-Blue
};
/// <summary>
/// All egg species that can inherit a Safar Ball when bred in BD/SP.
/// </summary>
internal static readonly HashSet<int> InheritSafari_BDSP = new()
{
(int)Ekans,
(int)Azurill,
(int)Barboach,
(int)Bidoof,
(int)Budew,
(int)Carnivine,
(int)Carvanha,
(int)Croagunk,
(int)Exeggcute,
(int)Gulpin,
(int)Hoothoot,
(int)Kangaskhan,
(int)Magikarp,
(int)Marill,
(int)Paras,
(int)Psyduck,
(int)Roselia,
(int)Shroomish,
(int)Skorupi,
(int)Starly,
(int)Wooper,
(int)Yanma,
};
}
}

View file

@ -15,6 +15,7 @@ namespace PKHeX.Core
5 => WildPokeBalls5,
6 => WildPokeballs6,
7 => GameVersion.Gen7b.Contains(game) ? WildPokeballs7b : WildPokeballs7,
8 when GameVersion.BDSP.Contains(game) => WildPokeBalls4_HGSS,
8 => GameVersion.GO == game ? WildPokeballs8g : WildPokeballs8,
_ => Array.Empty<int>(),
};

View file

@ -36,6 +36,8 @@ namespace PKHeX.Core
return VerifyBallEquals(data, s.Ball);
case EncounterSlot8GO: // Already a strict match
return GetResult(true);
case EncounterSlot8b {IsMarsh: true}:
return VerifyBallEquals(data, (int)Safari);
}
// Capture / Inherit cases -- can be one of many balls
@ -104,7 +106,7 @@ namespace PKHeX.Core
{
6 => VerifyBallEggGen6(data), // Gen6 Inheritance Rules
7 => VerifyBallEggGen7(data), // Gen7 Inheritance Rules
8 => VerifyBallEggGen8(data),
8 => data.pkm.BDSP ? VerifyBallEggGen8BDSP(data) : VerifyBallEggGen8(data),
_ => NONE,
};
@ -252,6 +254,23 @@ namespace PKHeX.Core
return NONE;
}
private CheckResult VerifyBallEggGen8BDSP(LegalityAnalysis data)
{
int species = data.EncounterMatch.Species;
if (species is (int)Species.Phione)
return VerifyBallEquals(data, (int)Poke);
if (data.pkm.Ball == (int)Safari)
{
if (BallBreedLegality.InheritSafari_BDSP.Contains(species))
return GetValid(LBallSpeciesPass);
return GetInvalid(LBallSpecies);
}
var balls = BallUseLegality.GetWildBalls(8, GameVersion.BDSP);
return VerifyBallEquals(data, balls);
}
private CheckResult VerifyBallEggGen8(LegalityAnalysis data)
{
var pkm = data.pkm;

View file

@ -26,6 +26,7 @@
4 => true,
5 => s.CNT_Sheen == 0 && pkm.Format >= 6, // ORAS Contests
6 => s.CNT_Sheen == 0 && (!pkm.IsUntraded || pkm.AO),
8 => pkm.BDSP, // BDSP Contests
_ => false,
};
}

View file

@ -90,7 +90,7 @@ namespace PKHeX.Core
// If none match, then it is not a valid OT friendship.
var fs = pkm.OT_Friendship;
var enc = data.Info.EncounterMatch;
if (GetBaseFriendship(origin, enc.Species, enc.Form) != fs)
if (GetBaseFriendship(enc, origin) != fs)
data.AddLine(GetInvalid(LegalityCheckStrings.LMemoryStatFriendshipOTBaseEvent));
}
}
@ -185,11 +185,19 @@ namespace PKHeX.Core
WC6 wc6 when wc6.OT_Name.Length > 0 => false,
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,
WC8 {IsHOMEGift: true} => false,
_ => true,
};
}
private static int GetBaseFriendship(IEncounterTemplate enc, int generation) => enc switch
{
IFixedOTFriendship f => f.OT_Friendship,
{ Version: GameVersion.BD or GameVersion.SP } => PersonalTable.SWSH.GetFormEntry(enc.Species, enc.Form).BaseFriendship,
_ => GetBaseFriendship(generation, enc.Species, enc.Form),
};
private static int GetBaseFriendship(int generation, int species, int form) => generation switch
{
1 => PersonalTable.USUM[species].BaseFriendship,

View file

@ -186,7 +186,7 @@ namespace PKHeX.Core
var clone = pk8.Clone();
clone.Species = (int) Species.Nincada;
((IRibbonIndex) clone).SetRibbon(affix);
var parse = RibbonVerifier.GetRibbonResults(clone, enc);
var parse = RibbonVerifier.GetRibbonResults(clone, data.Info.EvoChainsAllGens, enc);
var expect = $"Ribbon{(RibbonIndex) affix}";
var name = RibbonStrings.GetName(expect);
bool invalid = parse.FirstOrDefault(z => z.Name == name)?.Invalid == true;

View file

@ -14,6 +14,12 @@ namespace PKHeX.Core
public override void Verify(LegalityAnalysis data)
{
if (data.pkm.BDSP)
{
VerifyOTMemoryIs(data, 0, 0, 0, 0);
VerifyHTMemoryNone(data, (ITrainerMemories)data.pkm);
return;
}
VerifyOTMemory(data);
VerifyHTMemory(data);
}
@ -252,6 +258,7 @@ namespace PKHeX.Core
case 7 when pkm.GG: // LGPE does not set memories.
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.
return false;
// Eggs cannot have memories

View file

@ -49,7 +49,7 @@ namespace PKHeX.Core
switch (pkm)
{
case PK7 {ResortEventStatus: >= 20}:
case PK7 {ResortEventStatus: >= ResortEventState.MAX}:
data.AddLine(GetInvalid(LTransferBad));
break;
case PB7 pb7:
@ -58,6 +58,9 @@ namespace PKHeX.Core
case PK8 pk8:
VerifySWSHStats(data, pk8);
break;
case PB8 pb8:
VerifyBDSPStats(data, pb8);
break;
}
if (pkm.Format >= 6)
@ -90,6 +93,23 @@ namespace PKHeX.Core
if (!valid)
data.AddLine(GetInvalid(LPIDTypeMismatch));
}
else if (enc is IStaticCorrelation8b s8b)
{
var match = s8b.IsStaticCorrelationCorrect(pkm);
var req = s8b.GetRequirement(pkm);
if (match)
data.Info.PIDIV = new PIDIV(PIDType.Roaming8b, pkm.EncryptionConstant);
bool valid = req switch
{
StaticCorrelation8bRequirement.MustHave => match,
StaticCorrelation8bRequirement.MustNotHave => !match,
_ => true,
};
if (!valid)
data.AddLine(GetInvalid(LPIDTypeMismatch));
}
VerifyMiscFatefulEncounter(data);
}
@ -255,7 +275,7 @@ namespace PKHeX.Core
data.AddLine(GetInvalid(msg, Egg));
}
if (pkm is PK8 pk8)
if (pkm is G8PKM pk8)
{
if (pk8.HasAnyMoveRecordFlag())
data.AddLine(GetInvalid(LEggRelearnFlags, Egg));
@ -309,7 +329,8 @@ namespace PKHeX.Core
{
case WC6 wc6 when !wc6.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
case WC7 wc7 when !wc7.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
case WC8 wc8 when !wc8.CanBeReceivedByVersion(pkm.Version) && !pkm.WasTradedEgg:
case WC8 wc8 when !wc8.CanBeReceivedByVersion(pkm.Version):
case WB8 wb8 when !wb8.CanBeReceivedByVersion(pkm.Version):
data.AddLine(GetInvalid(LEncGiftVersionNotDistributed, GameOrigin));
return;
case WC6 wc6 when wc6.RestrictLanguage != 0 && pkm.Language != wc6.RestrictLanguage:
@ -443,13 +464,7 @@ namespace PKHeX.Core
data.AddLine(GetInvalid(string.Format(LMemorySocialTooHigh_0, byte.MaxValue), Encounter));
}
var sn = pk8.StatNature;
if (sn != pk8.Nature)
{
// Only allow Serious nature (12); disallow all other neutral natures.
if (sn != 12 && (sn > 24 || sn % 6 == 0))
data.AddLine(GetInvalid(LStatNatureInvalid));
}
VerifyStatNature(data, pk8);
var bv = pk8.BattleVersion;
if (bv != 0)
@ -495,7 +510,44 @@ namespace PKHeX.Core
data.AddLine(GetInvalid(string.Format(LMoveSourceTR, ParseSettings.MoveStrings[Legal.TMHM_SWSH[i + PersonalInfoSWSH.CountTM]])));
}
// weight/height scalars can be legally 0 (1:65536) so don't bother checking
// weight/height scalars can be legally 0 so don't bother checking
}
private void VerifyBDSPStats(LegalityAnalysis data, PB8 pb8)
{
if (pb8.Favorite)
data.AddLine(GetInvalid(LFavoriteMarkingUnavailable, Encounter));
var social = pb8.Sociability;
if (social != 0)
data.AddLine(GetInvalid(LMemorySocialZero, Encounter));
VerifyStatNature(data, pb8);
var bv = pb8.BattleVersion;
if (bv != 0)
data.AddLine(GetInvalid(LStatBattleVersionInvalid));
if (pb8.CanGigantamax)
GetInvalid(LStatGigantamaxInvalid);
if (pb8.DynamaxLevel != 0)
data.AddLine(GetInvalid(LStatDynamaxInvalid));
if (pb8.HasAnyMoveRecordFlag() && !pb8.IsEgg) // already checked for eggs
data.AddLine(GetInvalid(LEggRelearnFlags));
// weight/height scalars can be legally 0 so don't bother checking
}
private void VerifyStatNature(LegalityAnalysis data, PKM pk)
{
var sn = pk.StatNature;
if (sn == pk.Nature)
return;
// Only allow Serious nature (12); disallow all other neutral natures.
if (sn != 12 && (sn > 24 || sn % 6 == 0))
data.AddLine(GetInvalid(LStatNatureInvalid));
}
private static bool CanLearnTR(int species, int form, int tr)

View file

@ -34,6 +34,8 @@ namespace PKHeX.Core
if (enc is ILangNicknamedTemplate n)
{
VerifyFixedNicknameEncounter(data, n, enc, pkm, nickname);
if (pkm.IsEgg)
VerifyNicknameEgg(data);
return;
}
@ -244,8 +246,8 @@ namespace PKHeX.Core
data.AddLine(GetInvalid(pkm.IsNicknamed ? LNickFlagEggNo : LNickFlagEggYes, CheckIdentifier.Egg));
break;
default:
if (!pkm.IsNicknamed)
data.AddLine(GetInvalid(LNickFlagEggYes, CheckIdentifier.Egg));
if (pkm.IsNicknamed == Info.EncounterMatch is (EncounterStatic8b or WB8)) // bdsp doesn't use for ingame gifts
data.AddLine(GetInvalid(pkm.IsNicknamed ? LNickFlagEggNo : LNickFlagEggYes, CheckIdentifier.Egg));
break;
}
@ -269,6 +271,7 @@ namespace PKHeX.Core
case 5: VerifyTrade5(data, t); return;
case 6:
case 7:
case 8 when t is EncounterTrade8b: VerifyTrade8b(data, t); return;
case 8:
VerifyTrade(data, t, data.pkm.Language); return;
}
@ -357,6 +360,15 @@ namespace PKHeX.Core
VerifyTrade(data, t, lang);
}
private static void VerifyTrade8b(LegalityAnalysis data, EncounterTrade t)
{
var pkm = data.pkm;
int lang = pkm.Language;
if (t.Species == (int)Species.Magikarp)
lang = DetectTradeLanguageG4MeisterMagikarp(pkm, t, lang);
VerifyTrade(data, t, lang);
}
private static void FlagKoreanIncompatibleSameGenTrade(LegalityAnalysis data, PKM pkm, int lang)
{
if (pkm.Format != 4 || lang != (int)Korean)

View file

@ -26,7 +26,7 @@ namespace PKHeX.Core
return;
}
var result = GetIncorrectRibbons(pkm, enc);
var result = GetIncorrectRibbons(pkm, data.Info.EvoChainsAllGens, enc);
if (result.Count != 0)
{
var msg = string.Join(Environment.NewLine, result);
@ -38,11 +38,11 @@ namespace PKHeX.Core
}
}
private static List<string> GetIncorrectRibbons(PKM pkm, IEncounterTemplate enc)
private static List<string> GetIncorrectRibbons(PKM pkm, IReadOnlyList<EvoCriteria>[] evos, IEncounterTemplate enc)
{
List<string> missingRibbons = new();
List<string> invalidRibbons = new();
var ribs = GetRibbonResults(pkm, enc);
var ribs = GetRibbonResults(pkm, evos, enc);
foreach (var bad in ribs)
(bad.Invalid ? invalidRibbons : missingRibbons).Add(bad.Name);
@ -75,14 +75,14 @@ namespace PKHeX.Core
return false;
}
internal static IEnumerable<RibbonResult> GetRibbonResults(PKM pkm, IEncounterTemplate enc)
internal static IEnumerable<RibbonResult> GetRibbonResults(PKM pkm, IReadOnlyList<EvoCriteria>[] evos, IEncounterTemplate enc)
{
return GetInvalidRibbons(pkm, enc)
return GetInvalidRibbons(pkm, evos, enc)
.Concat(GetInvalidRibbonsEvent1(pkm, enc))
.Concat(GetInvalidRibbonsEvent2(pkm, enc));
}
private static IEnumerable<RibbonResult> GetInvalidRibbons(PKM pkm, IEncounterTemplate enc)
private static IEnumerable<RibbonResult> GetInvalidRibbons(PKM pkm, IReadOnlyList<EvoCriteria>[] evos, IEncounterTemplate enc)
{
// is a part of Event4, but O3 doesn't have the others
if (pkm is IRibbonSetOnly3 {RibbonWorld: true})
@ -130,9 +130,14 @@ namespace PKHeX.Core
if (pkm is IRibbonSetCommon4 s4)
{
bool inhabited4 = gen is 3 or 4;
var iterate = GetInvalidRibbons4Any(pkm, s4, gen);
var iterate = GetInvalidRibbons4Any(pkm, evos, s4, gen);
if (!inhabited4)
iterate = iterate.Concat(GetInvalidRibbonsNone(s4.RibbonBitsOnly(), s4.RibbonNamesOnly()));
{
if (pkm.BDSP) // Allow Sinnoh Champion. ILCA reused the Gen4 ribbon for the remake.
iterate = iterate.Concat(GetInvalidRibbonsNoneSkipIndex(s4.RibbonBitsOnly(), s4.RibbonNamesOnly(), 1));
else
iterate = iterate.Concat(GetInvalidRibbonsNone(s4.RibbonBitsOnly(), s4.RibbonNamesOnly()));
}
foreach (var z in iterate)
yield return z;
}
@ -200,11 +205,11 @@ namespace PKHeX.Core
}
}
private static IEnumerable<RibbonResult> GetInvalidRibbons4Any(PKM pkm, IRibbonSetCommon4 s4, int gen)
private static IEnumerable<RibbonResult> GetInvalidRibbons4Any(PKM pkm, IReadOnlyList<EvoCriteria>[] evos, IRibbonSetCommon4 s4, int gen)
{
if (s4.RibbonRecord)
yield return new RibbonResult(nameof(s4.RibbonRecord)); // Unobtainable
if (s4.RibbonFootprint && !CanHaveFootprintRibbon(pkm, gen))
if (s4.RibbonFootprint && !CanHaveFootprintRibbon(pkm, evos, gen))
yield return new RibbonResult(nameof(s4.RibbonFootprint));
bool gen34 = gen is 3 or 4;
@ -356,7 +361,7 @@ namespace PKHeX.Core
private static IEnumerable<RibbonResult> GetInvalidRibbons8Any(PKM pkm, IRibbonSetCommon8 s8, IEncounterTemplate enc)
{
if (!pkm.InhabitedGeneration(8) || !((PersonalInfoSWSH)PersonalTable.SWSH[pkm.Species]).IsPresentInGame)
if (!pkm.InhabitedGeneration(8) || !((PersonalInfoSWSH)PersonalTable.SWSH[pkm.Species]).IsPresentInGame || pkm.BDSP)
{
if (s8.RibbonChampionGalar)
yield return new RibbonResult(nameof(s8.RibbonChampionGalar));
@ -397,6 +402,18 @@ namespace PKHeX.Core
}
}
}
// can be expanded upon if SWSH gets updated with the new ribbon when HOME has BDSP support
if (!pkm.BDSP && s8.RibbonTwinklingStar)
{
yield return new RibbonResult(nameof(s8.RibbonTwinklingStar));
}
// new ribbon likely from Legends: Arceus; inaccessible until then
if (s8.RibbonPioneer)
{
yield return new RibbonResult(nameof(s8.RibbonPioneer));
}
}
private static bool CanParticipateInRankedSWSH(PKM pkm, IEncounterTemplate enc)
@ -480,6 +497,15 @@ namespace PKHeX.Core
}
}
private static IEnumerable<RibbonResult> GetInvalidRibbonsNoneSkipIndex(IReadOnlyList<bool> bits, IReadOnlyList<string> names, int skipIndex)
{
for (int i = 0; i < bits.Count; i++)
{
if (bits[i] && i != skipIndex)
yield return new RibbonResult(names[i]);
}
}
private static bool IsAllowedInContest4(int species) => species != 201 && species != 132; // Disallow Unown and Ditto
private static bool IsAllowedBattleFrontier(int species) => !Legal.BattleFrontierBanlist.Contains(species);
@ -491,15 +517,27 @@ namespace PKHeX.Core
return IsAllowedBattleFrontier(species);
}
private static bool CanHaveFootprintRibbon(PKM pkm, int gen)
private static bool CanHaveFootprintRibbon(PKM pkm, IReadOnlyList<EvoCriteria>[] evos, int gen)
{
if (gen <= 4) // Friendship Check unnecessary - can decrease after obtaining ribbon.
return true;
// Gen5: Can't obtain
if (pkm.Format < 6)
return false;
// Gen6/7: Increase level by 30 from original level
if (pkm.Format >= 6 && (gen != 8 && !pkm.GG) && (pkm.CurrentLevel - pkm.Met_Level >= 30))
if (gen != 8 && !pkm.GG && (pkm.CurrentLevel - pkm.Met_Level >= 30))
return true;
// Gen8-BDSP: Variable by species Footprint
if (pkm.BDSP)
{
if (evos[8].Any(z => z.Species <= Legal.MaxSpeciesID_4 && !HasFootprintBDSP[z.Species]))
return true; // no footprint
if (pkm.CurrentLevel - pkm.Met_Level >= 30)
return true; // traveled well
}
// Gen8: Can't obtain
return false;
}
@ -525,5 +563,60 @@ namespace PKHeX.Core
{
return gen == 3 && IsAllowedBattleFrontier(pkm.Species);
}
// Footprint type is not Type 5, requiring 30 levels.
private static readonly bool[] HasFootprintBDSP =
{
true, true, true, true, true, true, true, true, true, true,
true, false, true, true, false, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, false, false, true, false,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, false, false, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
false, false, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, false, true, true,
false, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, false, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, false, true, true, false, false, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, false, true, true, true, true, true, true,
true, true, true, false, true, true, true, true, true, true,
true, true, true, true, true, true, true, false, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, false, true, false, true,
true, true, true, false, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
false, true, true, true, true, true, true, true, true, false,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, false, false, true,
true, true, true, false, false, false, false, false, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, false, true, false, false, true, false, false, false,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, false, false, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true,
true, true, false, true, true, true, true, true, true, true,
true, true, true, true, false, true, false, true, true, true,
true, true, true, true, true, true, false, true, true, true,
true, true, true, true,
};
}
}

View file

@ -125,9 +125,16 @@ namespace PKHeX.Core
public void VerifyTransferLegalityG8(LegalityAnalysis data)
{
var pkm = data.pkm;
if (pkm is PB8 pb8)
{
VerifyTransferLegalityG8(data, pb8);
return;
}
// PK8
int species = pkm.Species;
var pi = (PersonalInfoSWSH)PersonalTable.SWSH.GetFormEntry(species, pkm.Form);
if (!pi.IsPresentInGame) // Can't transfer
if (!pi.IsPresentInGame || pkm.BDSP) // Can't transfer
{
data.AddLine(GetInvalid(LTransferBad));
return;
@ -153,6 +160,19 @@ namespace PKHeX.Core
}
}
// bdsp logic
private void VerifyTransferLegalityG8(LegalityAnalysis data, PB8 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 = (PersonalInfoBDSP)PersonalTable.BDSP.GetFormEntry(pk.Species, pk.Form);
if (!pi.IsPresentInGame || !pk.BDSP) // Can't transfer
data.AddLine(GetInvalid(LTransferBad));
}
private void VerifyHOMETransfer(LegalityAnalysis data, PKM pkm)
{
if (pkm is not IScaledSize s)

View file

@ -16,7 +16,7 @@ namespace PKHeX.Core
/// <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() { WC8.Size, WC6Full.Size, WC6.Size, PGF.Size, PGT.Size, PCD.Size };
private static readonly HashSet<int> Sizes = new() { WB8.Size, WC8.Size, WC6Full.Size, WC6.Size, PGF.Size, PGT.Size, PCD.Size };
/// <summary>
/// Converts the given data to a <see cref="MysteryGift"/>.
@ -35,6 +35,7 @@ namespace PKHeX.Core
WB7.Size when ext == ".wb7" => new WB7(data),
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),
WB7.SizeFull when ext == ".wb7full" => new WB7(data),
WC6Full.Size when ext == ".wc6full" => new WC6Full(data).Gift,
@ -54,6 +55,7 @@ namespace PKHeX.Core
PGF.Size => new PGF(data),
WR7.Size => new WR7(data),
WC8.Size => new WC8(data),
WB8.Size => new WB8(data),
// WC6/WC7: Check year
WC6.Size => BitConverter.ToUInt32(data, 0x4C) / 10000 < 2000 ? new WC7(data) : new WC6(data),

View file

@ -0,0 +1,750 @@
using System;
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.RibbonIndex;
namespace PKHeX.Core
{
/// <summary>
/// Generation 8b Mystery Gift Template File
/// </summary>
public sealed class WB8 : DataMysteryGift, ILangNick, INature, IRibbonIndex, IContestStats, ILangNicknamedTemplate,
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8
{
public const int Size = 0x2DC;
public const int CardStart = 0x0;
public override int Generation => 8;
public enum GiftType : byte
{
None = 0,
Pokemon = 1,
Item = 2,
BP = 3,
Clothing = 4,
Money = 5,
UnderGroundItem = 6,
}
public WB8() : this(new byte[Size]) { }
public WB8(byte[] data) : base(data) { }
// TODO: public byte RestrictVersion?
public bool CanBeReceivedByVersion(int v) => v is (int) GameVersion.BD or (int) GameVersion.SP;
// General Card Properties
// +0x0: Timestamp
public override int CardID
{
get => BitConverter.ToUInt16(Data, CardStart + 0x8);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x8);
}
public byte CardFlags { get => Data[CardStart + 0x10]; set => Data[CardStart + 0x10] = value; }
public GiftType CardType { get => (GiftType)Data[CardStart + 0x11]; set => Data[CardStart + 0x11] = (byte)value; }
public bool GiftRepeatable { get => (CardFlags & 1) == 0; set => CardFlags = (byte)((CardFlags & ~1) | (value ? 0 : 1)); }
public bool GiftOncePerDay { get => (CardFlags & 4) == 4; set => CardFlags = (byte)((CardFlags & ~4) | (value ? 4 : 0)); }
public override bool GiftUsed { get => false; set { } }
public int CardTitleIndex
{
get => Data[CardStart + 0x12];
set => Data[CardStart + 0x12] = (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) => BitConverter.ToUInt16(Data, CardStart + 0x20 + (0x10 * index));
public void SetItem(int index, ushort item) => BitConverter.GetBytes(item).CopyTo(Data, CardStart + 0x20 + (0x10 * index));
public int GetQuantity(int index) => BitConverter.ToUInt16(Data, CardStart + 0x22 + (0x10 * index));
public void SetQuantity(int index, ushort quantity) => BitConverter.GetBytes(quantity).CopyTo(Data, CardStart + 0x22 + (0x10 * index));
// Pokémon Properties
public override bool IsPokémon { get => CardType == GiftType.Pokemon; set { if (value) CardType = GiftType.Pokemon; } }
public override bool IsShiny
{
get
{
var type = PIDType;
if (type is Shiny.AlwaysStar or Shiny.AlwaysSquare)
return true;
if (type != Shiny.FixedValue)
return false;
// Player owned anti-shiny fixed PID
if (TID == 0 && SID == 0)
return false;
var pid = PID;
var psv = (int)((pid >> 16 ^ (pid & 0xFFFF)) >> 4);
var tsv = (TID ^ SID) >> 4;
return (psv ^ tsv) == 0;
}
}
public override int TID
{
get => BitConverter.ToUInt16(Data, CardStart + 0x20);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x20);
}
public override int SID {
get => BitConverter.ToUInt16(Data, CardStart + 0x22);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x22);
}
public int OriginGame
{
get => BitConverter.ToInt32(Data, CardStart + 0x24);
set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x24);
}
public uint EncryptionConstant
{
get => BitConverter.ToUInt32(Data, CardStart + 0x28);
set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x28);
}
public uint PID
{
get => BitConverter.ToUInt32(Data, CardStart + 0x2C);
set => BitConverter.GetBytes(value).CopyTo(Data, CardStart + 0x2C);
}
// Nicknames, OT Names 0x30 - 0x270
public override int EggLocation { get => BitConverter.ToUInt16(Data, CardStart + 0x270); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x270); }
public int MetLocation { get => BitConverter.ToUInt16(Data, CardStart + 0x272); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x272); }
public override int Ball
{
get => BitConverter.ToUInt16(Data, CardStart + 0x274);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x274);
}
public override int HeldItem
{
get => BitConverter.ToUInt16(Data, CardStart + 0x276);
set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x276);
}
public int Move1 { get => BitConverter.ToUInt16(Data, CardStart + 0x278); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x278); }
public int Move2 { get => BitConverter.ToUInt16(Data, CardStart + 0x27A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x27A); }
public int Move3 { get => BitConverter.ToUInt16(Data, CardStart + 0x27C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x27C); }
public int Move4 { get => BitConverter.ToUInt16(Data, CardStart + 0x27E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x27E); }
public int RelearnMove1 { get => BitConverter.ToUInt16(Data, CardStart + 0x280); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x280); }
public int RelearnMove2 { get => BitConverter.ToUInt16(Data, CardStart + 0x282); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x282); }
public int RelearnMove3 { get => BitConverter.ToUInt16(Data, CardStart + 0x284); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x284); }
public int RelearnMove4 { get => BitConverter.ToUInt16(Data, CardStart + 0x286); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x286); }
public override int Species { get => BitConverter.ToUInt16(Data, CardStart + 0x288); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, CardStart + 0x288); }
public override int Form { get => Data[CardStart + 0x28A]; set => Data[CardStart + 0x28A] = (byte)value; }
public override int Gender { get => Data[CardStart + 0x28B]; set => Data[CardStart + 0x28B] = (byte)value; }
public override int Level { get => Data[CardStart + 0x28C]; set => Data[CardStart + 0x28C] = (byte)value; }
public override bool IsEgg { get => Data[CardStart + 0x28D] == 1; set => Data[CardStart + 0x28D] = value ? (byte)1 : (byte)0; }
public int Nature { get => (sbyte)Data[CardStart + 0x28E]; set => Data[CardStart + 0x28E] = (byte)value; }
public override int AbilityType { get => Data[CardStart + 0x28F]; set => Data[CardStart + 0x28F] = (byte)value; }
private byte PIDTypeValue => Data[CardStart + 0x290];
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[CardStart + 0x291]; set => Data[CardStart + 0x291] = (byte)value; }
// Ribbons 0x24C-0x26C
private const int RibbonBytesOffset = 0x292;
private const int RibbonBytesCount = 0x20;
private const int RibbonByteNone = 0xFF; // signed -1
public bool HasMark()
{
for (int i = 0; i < RibbonBytesCount; i++)
{
var val = Data[RibbonBytesOffset + i];
if (val == RibbonByteNone)
return false;
if ((RibbonIndex)val 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[CardStart + 0x2B2]; set => Data[CardStart + 0x2B2] = (byte)value; }
public int IV_ATK { get => Data[CardStart + 0x2B3]; set => Data[CardStart + 0x2B3] = (byte)value; }
public int IV_DEF { get => Data[CardStart + 0x2B4]; set => Data[CardStart + 0x2B4] = (byte)value; }
public int IV_SPE { get => Data[CardStart + 0x2B5]; set => Data[CardStart + 0x2B5] = (byte)value; }
public int IV_SPA { get => Data[CardStart + 0x2B6]; set => Data[CardStart + 0x2B6] = (byte)value; }
public int IV_SPD { get => Data[CardStart + 0x2B7]; set => Data[CardStart + 0x2B7] = (byte)value; }
public int OTGender { get => Data[CardStart + 0x2B8]; set => Data[CardStart + 0x2B8] = (byte)value; }
public int EV_HP { get => Data[CardStart + 0x2B9]; set => Data[CardStart + 0x2B9] = (byte)value; }
public int EV_ATK { get => Data[CardStart + 0x2BA]; set => Data[CardStart + 0x2BA] = (byte)value; }
public int EV_DEF { get => Data[CardStart + 0x2BB]; set => Data[CardStart + 0x2BB] = (byte)value; }
public int EV_SPE { get => Data[CardStart + 0x2BC]; set => Data[CardStart + 0x2BC] = (byte)value; }
public int EV_SPA { get => Data[CardStart + 0x2BD]; set => Data[CardStart + 0x2BD] = (byte)value; }
public int EV_SPD { get => Data[CardStart + 0x2BE]; set => Data[CardStart + 0x2BE] = (byte)value; }
public byte CNT_Cool { get => Data[0x2BF]; set => Data[0x2BF] = value; }
public byte CNT_Beauty { get => Data[0x2C0]; set => Data[0x2C0] = value; }
public byte CNT_Cute { get => Data[0x2C1]; set => Data[0x2C1] = value; }
public byte CNT_Smart { get => Data[0x2C2]; set => Data[0x2C2] = value; }
public byte CNT_Tough { get => Data[0x2C3]; set => Data[0x2C3] = value; }
public byte CNT_Sheen { get => Data[0x2C4]; set => Data[0x2C4] = 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) => BitConverter.ToUInt16(Data, GetNicknameOffset(language)) != 0;
public bool CanBeAnyLanguage()
{
for (int i = 0; i < 9; i++)
{
var ofs = GetLanguageOffset(i);
var lang = BitConverter.ToInt16(Data, 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 = BitConverter.ToInt16(Data, ofs);
if (lang == language)
return true;
}
return false;
}
public int GetLanguage(int redeemLanguage) => Data[GetLanguageOffset(GetLanguageIndex(redeemLanguage))];
private static int GetLanguageOffset(int index) => 0x30 + (index * 0x20) + 0x1A;
public bool GetHasOT(int language) => BitConverter.ToUInt16(Data, 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) => StringConverter.GetString7b(Data, GetNicknameOffset(language), 0x1A);
public void SetNickname(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetNicknameOffset(language));
public string GetOT(int language) => StringConverter.GetString7b(Data, GetOTOffset(language), 0x1A);
public void SetOT(int language, string value) => StringConverter.SetString7b(value, 12, 13).CopyTo(Data, GetOTOffset(language));
private static int GetNicknameOffset(int language)
{
int index = GetLanguageIndex(language);
return 0x30 + (index * 0x20);
}
private static int GetOTOffset(int language)
{
int index = GetLanguageIndex(language);
return 0x150 + (index * 0x20);
}
public bool CanHandleOT(int language) => !GetHasOT(language);
public override GameVersion Version
{
get => OriginGame != 0 ? (GameVersion)OriginGame : GameVersion.BDSP;
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.BDSP.GetFormEntry(Species, Form);
var language = sav.Language;
var OT = GetOT(language);
bool hasOT = GetHasOT(language);
var pk = new PB8
{
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,
FatefulEncounter = true,
EVs = EVs,
CNT_Cool = CNT_Cool,
CNT_Beauty = CNT_Beauty,
CNT_Cute = CNT_Cute,
CNT_Smart = CNT_Smart,
CNT_Tough = CNT_Tough,
CNT_Sheen = CNT_Sheen,
Met_Location = MetLocation,
Egg_Location = EggLocation,
};
if (Species == (int)Core.Species.Manaphy && IsEgg)
{
pk.Egg_Location = MetLocation;
pk.Met_Location = Locations.Default8bNone;
pk.IsNicknamed = false;
}
pk.SetMaximumPPCurrent();
if ((sav.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version))
{
// give random valid game
var rnd = Util.Rand;
do { pk.Version = (int)GameVersion.SW + rnd.Next(2); }
while (!CanBeReceivedByVersion(pk.Version));
}
if (pk.TID == 0 && pk.SID == 0)
{
pk.TID = sav.TID;
pk.SID = sav.SID;
}
pk.MetDate = DateTime.Now;
var nickname_language = GetLanguage(language);
pk.Language = nickname_language != 0 ? nickname_language : sav.Language;
pk.IsNicknamed = !IsEgg && 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.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.BDSP.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.GetAbilityFromType(AbilityType), // 0/1 or 0/1/H
_ => throw new ArgumentOutOfRangeException(nameof(AbilityType)),
};
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)
{
int[] finalIVs = new 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(iv => iv == 31) < IVCount);
for (int i = 0; i < 6; i++)
finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32);
}
pk.IVs = finalIVs;
}
public override bool IsMatchExact(PKM pkm, DexLevel evo)
{
if ((short)pkm.Egg_Location == Locations.Default8bNone) // 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)
{
var eggloc = Species == (int)Core.Species.Manaphy ? MetLocation : EggLocation;
if (eggloc != pkm.Egg_Location) // traded
{
if (pkm.Egg_Location != Locations.LinkTrade6NPC)
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 ((Ball == 0 ? 4 : Ball) != pkm.Ball) 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;
// 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
}
}

View file

@ -759,6 +759,8 @@ namespace PKHeX.Core
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;

504
PKHeX.Core/PKM/G8PKM.cs Normal file
View file

@ -0,0 +1,504 @@
using System;
namespace PKHeX.Core
{
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
public abstract class G8PKM : PKM,
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8, IRibbonSetAffixed,
IContestStats, IContestStatsMutable, IHyperTrain, IScaledSize, IGigantamax, IFavorite, IDynamaxLevel, IRibbonIndex, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories
{
public sealed override int Format => 8;
protected G8PKM() : base(PokeCrypto.SIZE_8PARTY) { }
protected G8PKM(byte[] data) : base(DecryptParty(data)) { }
private static byte[] DecryptParty(byte[] data)
{
PokeCrypto.DecryptIfEncrypted8(ref data);
Array.Resize(ref data, PokeCrypto.SIZE_8PARTY);
return data;
}
protected override ushort CalculateChecksum()
{
ushort chk = 0;
for (int i = 8; i < PokeCrypto.SIZE_8STORED; i += 2)
chk += BitConverter.ToUInt16(Data, i);
return chk;
}
// Simple Generated Attributes
public override int CurrentFriendship
{
get => CurrentHandler == 0 ? OT_Friendship : HT_Friendship;
set { if (CurrentHandler == 0) OT_Friendship = value; else HT_Friendship = value; }
}
private string GetString(int offset, int count) => StringConverter.GetString7b(Data, offset, count);
private static byte[] SetString(string value, int maxLength) => StringConverter.SetString7b(value, maxLength);
public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
public override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
// Trash Bytes
public override Span<byte> Nickname_Trash { get => Data.AsSpan(0x58, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0x58)); } }
public override Span<byte> HT_Trash { get => Data.AsSpan(0xA8, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xA8)); } }
public override Span<byte> OT_Trash { get => Data.AsSpan(0xF8, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xF8)); } }
// Maximums
public override int MaxIV => 31;
public override int MaxEV => 252;
public override int OTLength => 12;
public override int NickLength => 12;
public override int PSV => (int)((PID >> 16 ^ (PID & 0xFFFF)) >> 4);
public override int TSV => (TID ^ SID) >> 4;
public override bool IsUntraded => Data[0xA8] == 0 && Data[0xA8 + 1] == 0 && Format == Generation; // immediately terminated HT_Name data (\0)
// Complex Generated Attributes
public override int Characteristic
{
get
{
int pm6 = (int)(EncryptionConstant % 6);
int maxIV = MaximumIV;
int pm6stat = 0;
for (int i = 0; i < 6; i++)
{
pm6stat = (pm6 + i) % 6;
if (GetIV(pm6stat) == maxIV)
break;
}
return (pm6stat * 5) + (maxIV % 5);
}
}
// Methods
protected override byte[] Encrypt()
{
RefreshChecksum();
return PokeCrypto.EncryptArray8(Data);
}
public void FixRelearn()
{
while (true)
{
if (RelearnMove4 != 0 && RelearnMove3 == 0)
{
RelearnMove3 = RelearnMove4;
RelearnMove4 = 0;
}
if (RelearnMove3 != 0 && RelearnMove2 == 0)
{
RelearnMove2 = RelearnMove3;
RelearnMove3 = 0;
continue;
}
if (RelearnMove2 != 0 && RelearnMove1 == 0)
{
RelearnMove1 = RelearnMove2;
RelearnMove2 = 0;
continue;
}
break;
}
}
public override uint EncryptionConstant { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); }
public override ushort Sanity { get => BitConverter.ToUInt16(Data, 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); }
public override ushort Checksum { get => BitConverter.ToUInt16(Data, 0x06); set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); }
// Structure
#region Block A
public override int Species { get => BitConverter.ToUInt16(Data, 0x08); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); }
public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); }
public override int TID { get => BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); }
public override int SID { get => BitConverter.ToUInt16(Data, 0x0E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); }
public override uint EXP { get => BitConverter.ToUInt32(Data, 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); }
public override int Ability { get => BitConverter.ToUInt16(Data, 0x14); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14); }
public override int AbilityNumber { get => Data[0x16] & 7; set => Data[0x16] = (byte)((Data[0x16] & ~7) | (value & 7)); }
public bool Favorite { get => (Data[0x16] & 8) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~8) | ((value ? 1 : 0) << 3)); } // unused, was in LGPE but not in SWSH
public bool CanGigantamax { get => (Data[0x16] & 16) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~16) | (value ? 16 : 0)); }
// 0x17 alignment unused
public override int MarkValue { get => BitConverter.ToUInt16(Data, 0x18); protected set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); }
// 0x1A alignment unused
// 0x1B alignment unused
public override uint PID { get => BitConverter.ToUInt32(Data, 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1C); }
public override int Nature { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override int StatNature { get => Data[0x21]; set => Data[0x21] = (byte)value; }
public override bool FatefulEncounter { get => (Data[0x22] & 1) == 1; set => Data[0x22] = (byte)((Data[0x22] & ~0x01) | (value ? 1 : 0)); }
public bool Flag2 { get => (Data[0x22] & 2) == 2; set => Data[0x22] = (byte)((Data[0x22] & ~0x02) | (value ? 2 : 0)); }
public override int Gender { get => (Data[0x22] >> 2) & 0x3; set => Data[0x22] = (byte)((Data[0x22] & 0xF3) | (value << 2)); }
// 0x23 alignment unused
public override int Form { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); }
public override int EV_HP { get => Data[0x26]; set => Data[0x26] = (byte)value; }
public override int EV_ATK { get => Data[0x27]; set => Data[0x27] = (byte)value; }
public override int EV_DEF { get => Data[0x28]; set => Data[0x28] = (byte)value; }
public override int EV_SPE { get => Data[0x29]; set => Data[0x29] = (byte)value; }
public override int EV_SPA { get => Data[0x2A]; set => Data[0x2A] = (byte)value; }
public override int EV_SPD { get => Data[0x2B]; set => Data[0x2B] = (byte)value; }
public byte CNT_Cool { get => Data[0x2C]; set => Data[0x2C] = value; }
public byte CNT_Beauty { get => Data[0x2D]; set => Data[0x2D] = value; }
public byte CNT_Cute { get => Data[0x2E]; set => Data[0x2E] = value; }
public byte CNT_Smart { get => Data[0x2F]; set => Data[0x2F] = value; }
public byte CNT_Tough { get => Data[0x30]; set => Data[0x30] = value; }
public byte CNT_Sheen { get => Data[0x31]; set => Data[0x31] = value; }
private byte PKRS { get => Data[0x32]; set => Data[0x32] = value; }
public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); }
public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | value << 4); }
// 0x33 unused padding
// ribbon u32
public bool RibbonChampionKalos { get => FlagUtil.GetFlag(Data, 0x34, 0); set => FlagUtil.SetFlag(Data, 0x34, 0, value); }
public bool RibbonChampionG3 { get => FlagUtil.GetFlag(Data, 0x34, 1); set => FlagUtil.SetFlag(Data, 0x34, 1, value); }
public bool RibbonChampionSinnoh { get => FlagUtil.GetFlag(Data, 0x34, 2); set => FlagUtil.SetFlag(Data, 0x34, 2, value); }
public bool RibbonBestFriends { get => FlagUtil.GetFlag(Data, 0x34, 3); set => FlagUtil.SetFlag(Data, 0x34, 3, value); }
public bool RibbonTraining { get => FlagUtil.GetFlag(Data, 0x34, 4); set => FlagUtil.SetFlag(Data, 0x34, 4, value); }
public bool RibbonBattlerSkillful { get => FlagUtil.GetFlag(Data, 0x34, 5); set => FlagUtil.SetFlag(Data, 0x34, 5, value); }
public bool RibbonBattlerExpert { get => FlagUtil.GetFlag(Data, 0x34, 6); set => FlagUtil.SetFlag(Data, 0x34, 6, value); }
public bool RibbonEffort { get => FlagUtil.GetFlag(Data, 0x34, 7); set => FlagUtil.SetFlag(Data, 0x34, 7, value); }
public bool RibbonAlert { get => FlagUtil.GetFlag(Data, 0x35, 0); set => FlagUtil.SetFlag(Data, 0x35, 0, value); }
public bool RibbonShock { get => FlagUtil.GetFlag(Data, 0x35, 1); set => FlagUtil.SetFlag(Data, 0x35, 1, value); }
public bool RibbonDowncast { get => FlagUtil.GetFlag(Data, 0x35, 2); set => FlagUtil.SetFlag(Data, 0x35, 2, value); }
public bool RibbonCareless { get => FlagUtil.GetFlag(Data, 0x35, 3); set => FlagUtil.SetFlag(Data, 0x35, 3, value); }
public bool RibbonRelax { get => FlagUtil.GetFlag(Data, 0x35, 4); set => FlagUtil.SetFlag(Data, 0x35, 4, value); }
public bool RibbonSnooze { get => FlagUtil.GetFlag(Data, 0x35, 5); set => FlagUtil.SetFlag(Data, 0x35, 5, value); }
public bool RibbonSmile { get => FlagUtil.GetFlag(Data, 0x35, 6); set => FlagUtil.SetFlag(Data, 0x35, 6, value); }
public bool RibbonGorgeous { get => FlagUtil.GetFlag(Data, 0x35, 7); set => FlagUtil.SetFlag(Data, 0x35, 7, value); }
public bool RibbonRoyal { get => FlagUtil.GetFlag(Data, 0x36, 0); set => FlagUtil.SetFlag(Data, 0x36, 0, value); }
public bool RibbonGorgeousRoyal { get => FlagUtil.GetFlag(Data, 0x36, 1); set => FlagUtil.SetFlag(Data, 0x36, 1, value); }
public bool RibbonArtist { get => FlagUtil.GetFlag(Data, 0x36, 2); set => FlagUtil.SetFlag(Data, 0x36, 2, value); }
public bool RibbonFootprint { get => FlagUtil.GetFlag(Data, 0x36, 3); set => FlagUtil.SetFlag(Data, 0x36, 3, value); }
public bool RibbonRecord { get => FlagUtil.GetFlag(Data, 0x36, 4); set => FlagUtil.SetFlag(Data, 0x36, 4, value); }
public bool RibbonLegend { get => FlagUtil.GetFlag(Data, 0x36, 5); set => FlagUtil.SetFlag(Data, 0x36, 5, value); }
public bool RibbonCountry { get => FlagUtil.GetFlag(Data, 0x36, 6); set => FlagUtil.SetFlag(Data, 0x36, 6, value); }
public bool RibbonNational { get => FlagUtil.GetFlag(Data, 0x36, 7); set => FlagUtil.SetFlag(Data, 0x36, 7, value); }
public bool RibbonEarth { get => FlagUtil.GetFlag(Data, 0x37, 0); set => FlagUtil.SetFlag(Data, 0x37, 0, value); }
public bool RibbonWorld { get => FlagUtil.GetFlag(Data, 0x37, 1); set => FlagUtil.SetFlag(Data, 0x37, 1, value); }
public bool RibbonClassic { get => FlagUtil.GetFlag(Data, 0x37, 2); set => FlagUtil.SetFlag(Data, 0x37, 2, value); }
public bool RibbonPremier { get => FlagUtil.GetFlag(Data, 0x37, 3); set => FlagUtil.SetFlag(Data, 0x37, 3, value); }
public bool RibbonEvent { get => FlagUtil.GetFlag(Data, 0x37, 4); set => FlagUtil.SetFlag(Data, 0x37, 4, value); }
public bool RibbonBirthday { get => FlagUtil.GetFlag(Data, 0x37, 5); set => FlagUtil.SetFlag(Data, 0x37, 5, value); }
public bool RibbonSpecial { get => FlagUtil.GetFlag(Data, 0x37, 6); set => FlagUtil.SetFlag(Data, 0x37, 6, value); }
public bool RibbonSouvenir { get => FlagUtil.GetFlag(Data, 0x37, 7); set => FlagUtil.SetFlag(Data, 0x37, 7, value); }
// ribbon u32
public bool RibbonWishing { get => FlagUtil.GetFlag(Data, 0x38, 0); set => FlagUtil.SetFlag(Data, 0x38, 0, value); }
public bool RibbonChampionBattle { get => FlagUtil.GetFlag(Data, 0x38, 1); set => FlagUtil.SetFlag(Data, 0x38, 1, value); }
public bool RibbonChampionRegional { get => FlagUtil.GetFlag(Data, 0x38, 2); set => FlagUtil.SetFlag(Data, 0x38, 2, value); }
public bool RibbonChampionNational { get => FlagUtil.GetFlag(Data, 0x38, 3); set => FlagUtil.SetFlag(Data, 0x38, 3, value); }
public bool RibbonChampionWorld { get => FlagUtil.GetFlag(Data, 0x38, 4); set => FlagUtil.SetFlag(Data, 0x38, 4, value); }
public bool HasContestMemoryRibbon { get => FlagUtil.GetFlag(Data, 0x38, 5); set => FlagUtil.SetFlag(Data, 0x38, 5, value); }
public bool HasBattleMemoryRibbon { get => FlagUtil.GetFlag(Data, 0x38, 6); set => FlagUtil.SetFlag(Data, 0x38, 6, value); }
public bool RibbonChampionG6Hoenn { get => FlagUtil.GetFlag(Data, 0x38, 7); set => FlagUtil.SetFlag(Data, 0x38, 7, value); }
public bool RibbonContestStar { get => FlagUtil.GetFlag(Data, 0x39, 0); set => FlagUtil.SetFlag(Data, 0x39, 0, value); }
public bool RibbonMasterCoolness { get => FlagUtil.GetFlag(Data, 0x39, 1); set => FlagUtil.SetFlag(Data, 0x39, 1, value); }
public bool RibbonMasterBeauty { get => FlagUtil.GetFlag(Data, 0x39, 2); set => FlagUtil.SetFlag(Data, 0x39, 2, value); }
public bool RibbonMasterCuteness { get => FlagUtil.GetFlag(Data, 0x39, 3); set => FlagUtil.SetFlag(Data, 0x39, 3, value); }
public bool RibbonMasterCleverness { get => FlagUtil.GetFlag(Data, 0x39, 4); set => FlagUtil.SetFlag(Data, 0x39, 4, value); }
public bool RibbonMasterToughness { get => FlagUtil.GetFlag(Data, 0x39, 5); set => FlagUtil.SetFlag(Data, 0x39, 5, value); }
public bool RibbonChampionAlola { get => FlagUtil.GetFlag(Data, 0x39, 6); set => FlagUtil.SetFlag(Data, 0x39, 6, value); }
public bool RibbonBattleRoyale { get => FlagUtil.GetFlag(Data, 0x39, 7); set => FlagUtil.SetFlag(Data, 0x39, 7, value); }
public bool RibbonBattleTreeGreat { get => FlagUtil.GetFlag(Data, 0x3A, 0); set => FlagUtil.SetFlag(Data, 0x3A, 0, value); }
public bool RibbonBattleTreeMaster { get => FlagUtil.GetFlag(Data, 0x3A, 1); set => FlagUtil.SetFlag(Data, 0x3A, 1, value); }
public bool RibbonChampionGalar { get => FlagUtil.GetFlag(Data, 0x3A, 2); set => FlagUtil.SetFlag(Data, 0x3A, 2, value); }
public bool RibbonTowerMaster { get => FlagUtil.GetFlag(Data, 0x3A, 3); set => FlagUtil.SetFlag(Data, 0x3A, 3, value); }
public bool RibbonMasterRank { get => FlagUtil.GetFlag(Data, 0x3A, 4); set => FlagUtil.SetFlag(Data, 0x3A, 4, value); }
public bool RibbonMarkLunchtime { get => FlagUtil.GetFlag(Data, 0x3A, 5); set => FlagUtil.SetFlag(Data, 0x3A, 5, value); }
public bool RibbonMarkSleepyTime { get => FlagUtil.GetFlag(Data, 0x3A, 6); set => FlagUtil.SetFlag(Data, 0x3A, 6, value); }
public bool RibbonMarkDusk { get => FlagUtil.GetFlag(Data, 0x3A, 7); set => FlagUtil.SetFlag(Data, 0x3A, 7, value); }
public bool RibbonMarkDawn { get => FlagUtil.GetFlag(Data, 0x3B, 0); set => FlagUtil.SetFlag(Data, 0x3B, 0, value); }
public bool RibbonMarkCloudy { get => FlagUtil.GetFlag(Data, 0x3B, 1); set => FlagUtil.SetFlag(Data, 0x3B, 1, value); }
public bool RibbonMarkRainy { get => FlagUtil.GetFlag(Data, 0x3B, 2); set => FlagUtil.SetFlag(Data, 0x3B, 2, value); }
public bool RibbonMarkStormy { get => FlagUtil.GetFlag(Data, 0x3B, 3); set => FlagUtil.SetFlag(Data, 0x3B, 3, value); }
public bool RibbonMarkSnowy { get => FlagUtil.GetFlag(Data, 0x3B, 4); set => FlagUtil.SetFlag(Data, 0x3B, 4, value); }
public bool RibbonMarkBlizzard { get => FlagUtil.GetFlag(Data, 0x3B, 5); set => FlagUtil.SetFlag(Data, 0x3B, 5, value); }
public bool RibbonMarkDry { get => FlagUtil.GetFlag(Data, 0x3B, 6); set => FlagUtil.SetFlag(Data, 0x3B, 6, value); }
public bool RibbonMarkSandstorm { get => FlagUtil.GetFlag(Data, 0x3B, 7); set => FlagUtil.SetFlag(Data, 0x3B, 7, value); }
public int RibbonCountMemoryContest { get => Data[0x3C]; set => HasContestMemoryRibbon = (Data[0x3C] = (byte)value) != 0; }
public int RibbonCountMemoryBattle { get => Data[0x3D]; set => HasBattleMemoryRibbon = (Data[0x3D] = (byte)value) != 0; }
// 0x3E padding
// 0x3F padding
// 0x40 Ribbon 1
public bool RibbonMarkMisty { get => FlagUtil.GetFlag(Data, 0x40, 0); set => FlagUtil.SetFlag(Data, 0x40, 0, value); }
public bool RibbonMarkDestiny { get => FlagUtil.GetFlag(Data, 0x40, 1); set => FlagUtil.SetFlag(Data, 0x40, 1, value); }
public bool RibbonMarkFishing { get => FlagUtil.GetFlag(Data, 0x40, 2); set => FlagUtil.SetFlag(Data, 0x40, 2, value); }
public bool RibbonMarkCurry { get => FlagUtil.GetFlag(Data, 0x40, 3); set => FlagUtil.SetFlag(Data, 0x40, 3, value); }
public bool RibbonMarkUncommon { get => FlagUtil.GetFlag(Data, 0x40, 4); set => FlagUtil.SetFlag(Data, 0x40, 4, value); }
public bool RibbonMarkRare { get => FlagUtil.GetFlag(Data, 0x40, 5); set => FlagUtil.SetFlag(Data, 0x40, 5, value); }
public bool RibbonMarkRowdy { get => FlagUtil.GetFlag(Data, 0x40, 6); set => FlagUtil.SetFlag(Data, 0x40, 6, value); }
public bool RibbonMarkAbsentMinded { get => FlagUtil.GetFlag(Data, 0x40, 7); set => FlagUtil.SetFlag(Data, 0x40, 7, value); }
public bool RibbonMarkJittery { get => FlagUtil.GetFlag(Data, 0x41, 0); set => FlagUtil.SetFlag(Data, 0x41, 0, value); }
public bool RibbonMarkExcited { get => FlagUtil.GetFlag(Data, 0x41, 1); set => FlagUtil.SetFlag(Data, 0x41, 1, value); }
public bool RibbonMarkCharismatic { get => FlagUtil.GetFlag(Data, 0x41, 2); set => FlagUtil.SetFlag(Data, 0x41, 2, value); }
public bool RibbonMarkCalmness { get => FlagUtil.GetFlag(Data, 0x41, 3); set => FlagUtil.SetFlag(Data, 0x41, 3, value); }
public bool RibbonMarkIntense { get => FlagUtil.GetFlag(Data, 0x41, 4); set => FlagUtil.SetFlag(Data, 0x41, 4, value); }
public bool RibbonMarkZonedOut { get => FlagUtil.GetFlag(Data, 0x41, 5); set => FlagUtil.SetFlag(Data, 0x41, 5, value); }
public bool RibbonMarkJoyful { get => FlagUtil.GetFlag(Data, 0x41, 6); set => FlagUtil.SetFlag(Data, 0x41, 6, value); }
public bool RibbonMarkAngry { get => FlagUtil.GetFlag(Data, 0x41, 7); set => FlagUtil.SetFlag(Data, 0x41, 7, value); }
public bool RibbonMarkSmiley { get => FlagUtil.GetFlag(Data, 0x42, 0); set => FlagUtil.SetFlag(Data, 0x42, 0, value); }
public bool RibbonMarkTeary { get => FlagUtil.GetFlag(Data, 0x42, 1); set => FlagUtil.SetFlag(Data, 0x42, 1, value); }
public bool RibbonMarkUpbeat { get => FlagUtil.GetFlag(Data, 0x42, 2); set => FlagUtil.SetFlag(Data, 0x42, 2, value); }
public bool RibbonMarkPeeved { get => FlagUtil.GetFlag(Data, 0x42, 3); set => FlagUtil.SetFlag(Data, 0x42, 3, value); }
public bool RibbonMarkIntellectual { get => FlagUtil.GetFlag(Data, 0x42, 4); set => FlagUtil.SetFlag(Data, 0x42, 4, value); }
public bool RibbonMarkFerocious { get => FlagUtil.GetFlag(Data, 0x42, 5); set => FlagUtil.SetFlag(Data, 0x42, 5, value); }
public bool RibbonMarkCrafty { get => FlagUtil.GetFlag(Data, 0x42, 6); set => FlagUtil.SetFlag(Data, 0x42, 6, value); }
public bool RibbonMarkScowling { get => FlagUtil.GetFlag(Data, 0x42, 7); set => FlagUtil.SetFlag(Data, 0x42, 7, value); }
public bool RibbonMarkKindly { get => FlagUtil.GetFlag(Data, 0x43, 0); set => FlagUtil.SetFlag(Data, 0x43, 0, value); }
public bool RibbonMarkFlustered { get => FlagUtil.GetFlag(Data, 0x43, 1); set => FlagUtil.SetFlag(Data, 0x43, 1, value); }
public bool RibbonMarkPumpedUp { get => FlagUtil.GetFlag(Data, 0x43, 2); set => FlagUtil.SetFlag(Data, 0x43, 2, value); }
public bool RibbonMarkZeroEnergy { get => FlagUtil.GetFlag(Data, 0x43, 3); set => FlagUtil.SetFlag(Data, 0x43, 3, value); }
public bool RibbonMarkPrideful { get => FlagUtil.GetFlag(Data, 0x43, 4); set => FlagUtil.SetFlag(Data, 0x43, 4, value); }
public bool RibbonMarkUnsure { get => FlagUtil.GetFlag(Data, 0x43, 5); set => FlagUtil.SetFlag(Data, 0x43, 5, value); }
public bool RibbonMarkHumble { get => FlagUtil.GetFlag(Data, 0x43, 6); set => FlagUtil.SetFlag(Data, 0x43, 6, value); }
public bool RibbonMarkThorny { get => FlagUtil.GetFlag(Data, 0x43, 7); set => FlagUtil.SetFlag(Data, 0x43, 7, value); }
// 0x44 Ribbon 2
public bool RibbonMarkVigor { get => FlagUtil.GetFlag(Data, 0x44, 0); set => FlagUtil.SetFlag(Data, 0x44, 0, value); }
public bool RibbonMarkSlump { get => FlagUtil.GetFlag(Data, 0x44, 1); set => FlagUtil.SetFlag(Data, 0x44, 1, value); }
public bool RibbonTwinklingStar { get => FlagUtil.GetFlag(Data, 0x44, 2); set => FlagUtil.SetFlag(Data, 0x44, 2, value); }
public bool RibbonPioneer { get => FlagUtil.GetFlag(Data, 0x44, 3); set => FlagUtil.SetFlag(Data, 0x44, 3, value); }
public bool RIB44_4 { get => FlagUtil.GetFlag(Data, 0x44, 4); set => FlagUtil.SetFlag(Data, 0x44, 4, value); }
public bool RIB44_5 { get => FlagUtil.GetFlag(Data, 0x44, 5); set => FlagUtil.SetFlag(Data, 0x44, 5, value); }
public bool RIB44_6 { get => FlagUtil.GetFlag(Data, 0x44, 6); set => FlagUtil.SetFlag(Data, 0x44, 6, value); }
public bool RIB44_7 { get => FlagUtil.GetFlag(Data, 0x44, 7); set => FlagUtil.SetFlag(Data, 0x44, 7, value); }
public bool RIB45_0 { get => FlagUtil.GetFlag(Data, 0x45, 0); set => FlagUtil.SetFlag(Data, 0x45, 0, value); }
public bool RIB45_1 { get => FlagUtil.GetFlag(Data, 0x45, 1); set => FlagUtil.SetFlag(Data, 0x45, 1, value); }
public bool RIB45_2 { get => FlagUtil.GetFlag(Data, 0x45, 2); set => FlagUtil.SetFlag(Data, 0x45, 2, value); }
public bool RIB45_3 { get => FlagUtil.GetFlag(Data, 0x45, 3); set => FlagUtil.SetFlag(Data, 0x45, 3, value); }
public bool RIB45_4 { get => FlagUtil.GetFlag(Data, 0x45, 4); set => FlagUtil.SetFlag(Data, 0x45, 4, value); }
public bool RIB45_5 { get => FlagUtil.GetFlag(Data, 0x45, 5); set => FlagUtil.SetFlag(Data, 0x45, 5, value); }
public bool RIB45_6 { get => FlagUtil.GetFlag(Data, 0x45, 6); set => FlagUtil.SetFlag(Data, 0x45, 6, value); }
public bool RIB45_7 { get => FlagUtil.GetFlag(Data, 0x45, 7); set => FlagUtil.SetFlag(Data, 0x45, 7, value); }
public bool RIB46_0 { get => FlagUtil.GetFlag(Data, 0x41, 0); set => FlagUtil.SetFlag(Data, 0x41, 0, value); }
public bool RIB46_1 { get => FlagUtil.GetFlag(Data, 0x46, 1); set => FlagUtil.SetFlag(Data, 0x46, 1, value); }
public bool RIB46_2 { get => FlagUtil.GetFlag(Data, 0x46, 2); set => FlagUtil.SetFlag(Data, 0x46, 2, value); }
public bool RIB46_3 { get => FlagUtil.GetFlag(Data, 0x46, 3); set => FlagUtil.SetFlag(Data, 0x46, 3, value); }
public bool RIB46_4 { get => FlagUtil.GetFlag(Data, 0x46, 4); set => FlagUtil.SetFlag(Data, 0x46, 4, value); }
public bool RIB46_5 { get => FlagUtil.GetFlag(Data, 0x46, 5); set => FlagUtil.SetFlag(Data, 0x46, 5, value); }
public bool RIB46_6 { get => FlagUtil.GetFlag(Data, 0x46, 6); set => FlagUtil.SetFlag(Data, 0x46, 6, value); }
public bool RIB46_7 { get => FlagUtil.GetFlag(Data, 0x46, 7); set => FlagUtil.SetFlag(Data, 0x46, 7, value); }
public bool RIB47_0 { get => FlagUtil.GetFlag(Data, 0x47, 0); set => FlagUtil.SetFlag(Data, 0x47, 0, value); }
public bool RIB47_1 { get => FlagUtil.GetFlag(Data, 0x47, 1); set => FlagUtil.SetFlag(Data, 0x47, 1, value); }
public bool RIB47_2 { get => FlagUtil.GetFlag(Data, 0x47, 2); set => FlagUtil.SetFlag(Data, 0x47, 2, value); }
public bool RIB47_3 { get => FlagUtil.GetFlag(Data, 0x47, 3); set => FlagUtil.SetFlag(Data, 0x47, 3, value); }
public bool RIB47_4 { get => FlagUtil.GetFlag(Data, 0x47, 4); set => FlagUtil.SetFlag(Data, 0x47, 4, value); }
public bool RIB47_5 { get => FlagUtil.GetFlag(Data, 0x47, 5); set => FlagUtil.SetFlag(Data, 0x47, 5, value); }
public bool RIB47_6 { get => FlagUtil.GetFlag(Data, 0x47, 6); set => FlagUtil.SetFlag(Data, 0x47, 6, value); }
public bool RIB47_7 { get => FlagUtil.GetFlag(Data, 0x47, 7); set => FlagUtil.SetFlag(Data, 0x47, 7, value); }
public bool HasMark()
{
var d = Data;
if ((BitConverter.ToUInt16(d, 0x3A) & 0xFFE0) != 0)
return true;
if (BitConverter.ToUInt32(d, 0x40) != 0)
return true;
return (d[0x44] & 3) != 0;
}
public uint Sociability { get => BitConverter.ToUInt32(Data, 0x48); set => BitConverter.GetBytes(value).CopyTo(Data, 0x48); }
// 0x4C-0x4F unused
public int HeightScalar { get => Data[0x50]; set => Data[0x50] = (byte)value; }
public int WeightScalar { get => Data[0x51]; set => Data[0x51] = (byte)value; }
// 0x52-0x57 unused
#endregion
#region Block B
public override string Nickname
{
get => GetString(0x58, 24);
set => SetString(value, 12).CopyTo(Data, 0x58);
}
// 2 bytes for \0, automatically handled above
public override int Move1 { get => BitConverter.ToUInt16(Data, 0x72); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x72); }
public override int Move2 { get => BitConverter.ToUInt16(Data, 0x74); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x74); }
public override int Move3 { get => BitConverter.ToUInt16(Data, 0x76); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x76); }
public override int Move4 { get => BitConverter.ToUInt16(Data, 0x78); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x78); }
public override int Move1_PP { get => Data[0x7A]; set => Data[0x7A] = (byte)value; }
public override int Move2_PP { get => Data[0x7B]; set => Data[0x7B] = (byte)value; }
public override int Move3_PP { get => Data[0x7C]; set => Data[0x7C] = (byte)value; }
public override int Move4_PP { get => Data[0x7D]; set => Data[0x7D] = (byte)value; }
public override int Move1_PPUps { get => Data[0x7E]; set => Data[0x7E] = (byte)value; }
public override int Move2_PPUps { get => Data[0x7F]; set => Data[0x7F] = (byte)value; }
public override int Move3_PPUps { get => Data[0x80]; set => Data[0x80] = (byte)value; }
public override int Move4_PPUps { get => Data[0x81]; set => Data[0x81] = (byte)value; }
public override int RelearnMove1 { get => BitConverter.ToUInt16(Data, 0x82); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x82); }
public override int RelearnMove2 { get => BitConverter.ToUInt16(Data, 0x84); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x84); }
public override int RelearnMove3 { get => BitConverter.ToUInt16(Data, 0x86); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x86); }
public override int RelearnMove4 { get => BitConverter.ToUInt16(Data, 0x88); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x88); }
public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0x8A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x8A); }
private uint IV32 { get => BitConverter.ToUInt32(Data, 0x8C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x8C); }
public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); }
public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); }
public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); }
public override int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 15)) | ((value > 31 ? 31u : (uint)value) << 15); }
public override int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 20)) | ((value > 31 ? 31u : (uint)value) << 20); }
public override int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 25)) | ((value > 31 ? 31u : (uint)value) << 25); }
public override bool IsEgg { get => ((IV32 >> 30) & 1) == 1; set => IV32 = (IV32 & ~0x40000000u) | (value ? 0x40000000u : 0u); }
public override bool IsNicknamed { get => ((IV32 >> 31) & 1) == 1; set => IV32 = (IV32 & 0x7FFFFFFFu) | (value ? 0x80000000u : 0u); }
public byte DynamaxLevel { get => Data[0x90]; set => Data[0x90] = value; }
// 0x90-0x93 unused
public override int Status_Condition { get => BitConverter.ToInt32(Data, 0x94); set => BitConverter.GetBytes(value).CopyTo(Data, 0x94); }
public int Unk98 { get => BitConverter.ToInt32(Data, 0x98); set => BitConverter.GetBytes(value).CopyTo(Data, 0x98); }
// 0x9C-0xA7 unused
#endregion
#region Block C
public override string HT_Name { get => GetString(0xA8, 24); set => SetString(value, 12).CopyTo(Data, 0xA8); }
public override int HT_Gender { get => Data[0xC2]; set => Data[0xC2] = (byte)value; }
public int HT_Language { get => Data[0xC3]; set => Data[0xC3] = (byte)value; }
public override int CurrentHandler { get => Data[0xC4]; set => Data[0xC4] = (byte)value; }
// 0xC5 unused (alignment)
public int HT_TrainerID { get => BitConverter.ToUInt16(Data, 0xC6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xC6); } // unused?
public override int HT_Friendship { get => Data[0xC8]; set => Data[0xC8] = (byte)value; }
public int HT_Intensity { get => Data[0xC9]; set => Data[0xC9] = (byte)value; }
public int HT_Memory { get => Data[0xCA]; set => Data[0xCA] = (byte)value; }
public int HT_Feeling { get => Data[0xCB]; set => Data[0xCB] = (byte)value; }
public int HT_TextVar { get => BitConverter.ToUInt16(Data, 0xCC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xCC); }
// 0xCE-0xDB unused
public override byte Fullness { get => Data[0xDC]; set => Data[0xDC] = value; }
public override byte Enjoyment { get => Data[0xDD]; set => Data[0xDD] = value; }
public override int Version { get => Data[0xDE]; set => Data[0xDE] = (byte)value; }
public int BattleVersion { get => Data[0xDF]; set => Data[0xDF] = (byte)value; }
// public override int Region { get => Data[0xE0]; set => Data[0xE0] = (byte)value; }
// public override int ConsoleRegion { get => Data[0xE1]; set => Data[0xE1] = (byte)value; }
public override int Language { get => Data[0xE2]; set => Data[0xE2] = (byte)value; }
public int UnkE3 { get => Data[0xE3]; set => Data[0xE3] = (byte)value; }
public uint FormArgument { get => BitConverter.ToUInt32(Data, 0xE4); set => BitConverter.GetBytes(value).CopyTo(Data, 0xE4); }
public byte FormArgumentRemain { get => (byte)FormArgument; set => FormArgument = (FormArgument & ~0xFFu) | value; }
public byte FormArgumentElapsed { get => (byte)(FormArgument >> 8); set => FormArgument = (FormArgument & ~0xFF00u) | (uint)(value << 8); }
public byte FormArgumentMaximum { get => (byte)(FormArgument >> 16); set => FormArgument = (FormArgument & ~0xFF0000u) | (uint)(value << 16); }
public sbyte AffixedRibbon { get => (sbyte)Data[0xE8]; set => Data[0xE8] = (byte)value; } // selected ribbon
// remainder unused
#endregion
#region Block D
public override string OT_Name { get => GetString(0xF8, 24); set => SetString(value, 12).CopyTo(Data, 0xF8); }
public override int OT_Friendship { get => Data[0x112]; set => Data[0x112] = (byte)value; }
public int OT_Intensity { get => Data[0x113]; set => Data[0x113] = (byte)value; }
public int OT_Memory { get => Data[0x114]; set => Data[0x114] = (byte)value; }
// 0x115 unused align
public int OT_TextVar { get => BitConverter.ToUInt16(Data, 0x116); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x116); }
public int OT_Feeling { get => Data[0x118]; set => Data[0x118] = (byte)value; }
public override int Egg_Year { get => Data[0x119]; set => Data[0x119] = (byte)value; }
public override int Egg_Month { get => Data[0x11A]; set => Data[0x11A] = (byte)value; }
public override int Egg_Day { get => Data[0x11B]; set => Data[0x11B] = (byte)value; }
public override int Met_Year { get => Data[0x11C]; set => Data[0x11C] = (byte)value; }
public override int Met_Month { get => Data[0x11D]; set => Data[0x11D] = (byte)value; }
public override int Met_Day { get => Data[0x11E]; set => Data[0x11E] = (byte)value; }
// 0x11F unused align
public override int Egg_Location { get => BitConverter.ToUInt16(Data, 0x120); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x120); }
public override int Met_Location { get => BitConverter.ToUInt16(Data, 0x122); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x122); }
public override int Ball { get => Data[0x124]; set => Data[0x124] = (byte)value; }
public override int Met_Level { get => Data[0x125] & ~0x80; set => Data[0x125] = (byte)((Data[0x125] & 0x80) | value); }
public override int OT_Gender { get => Data[0x125] >> 7; set => Data[0x125] = (byte)((Data[0x125] & ~0x80) | (value << 7)); }
public int HyperTrainFlags { get => Data[0x126]; set => Data[0x126] = (byte)value; }
public bool HT_HP { get => ((HyperTrainFlags >> 0) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 0)) | ((value ? 1 : 0) << 0); }
public bool HT_ATK { get => ((HyperTrainFlags >> 1) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 1)) | ((value ? 1 : 0) << 1); }
public bool HT_DEF { get => ((HyperTrainFlags >> 2) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 2)) | ((value ? 1 : 0) << 2); }
public bool HT_SPA { get => ((HyperTrainFlags >> 3) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 3)) | ((value ? 1 : 0) << 3); }
public bool HT_SPD { get => ((HyperTrainFlags >> 4) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 4)) | ((value ? 1 : 0) << 4); }
public bool HT_SPE { get => ((HyperTrainFlags >> 5) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 5)) | ((value ? 1 : 0) << 5); }
public bool GetMoveRecordFlag(int index)
{
if ((uint)index > 112) // 14 bytes, 8 bits
throw new ArgumentOutOfRangeException(nameof(index));
int ofs = index >> 3;
return FlagUtil.GetFlag(Data, 0x127 + ofs, index & 7);
}
public void SetMoveRecordFlag(int index, bool value)
{
if ((uint)index > 112) // 14 bytes, 8 bits
throw new ArgumentOutOfRangeException(nameof(index));
int ofs = index >> 3;
FlagUtil.SetFlag(Data, 0x127 + ofs, index & 7, value);
}
public bool HasAnyMoveRecordFlag() => Array.FindIndex(Data, 0x127, 14, z => z != 0) >= 0;
// Why did you mis-align this field, GameFreak?
public ulong Tracker
{
get => BitConverter.ToUInt64(Data, 0x135);
set => BitConverter.GetBytes(value).CopyTo(Data, 0x135);
}
#endregion
#region Battle Stats
public override int Stat_Level { get => Data[0x148]; set => Data[0x148] = (byte)value; }
// 0x149 unused alignment
public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0x14A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14A); }
public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0x14C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14C); }
public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0x14E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14E); }
public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0x150); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x150); }
public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0x152); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x152); }
public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0x154); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x154); }
#endregion
public override int[] Markings
{
get
{
int[] marks = new int[8];
int val = MarkValue;
for (int i = 0; i < marks.Length; i++)
marks[i] = ((val >> (i * 2)) & 3) % 3;
return marks;
}
set
{
if (value.Length > 8)
return;
int v = 0;
for (int i = 0; i < value.Length; i++)
v |= (value[i] % 3) << (i * 2);
MarkValue = v;
}
}
public bool GetRibbon(int index) => FlagUtil.GetFlag(Data, GetRibbonByte(index), index & 7);
public void SetRibbon(int index, bool value = true) => FlagUtil.SetFlag(Data, GetRibbonByte(index), index & 7, value);
public int GetRibbonByte(int index)
{
if ((uint)index >= 128)
throw new ArgumentOutOfRangeException(nameof(index));
if (index < 64)
return 0x34 + (index >> 3);
index -= 64;
return 0x40 + (index >> 3);
}
}
}

110
PKHeX.Core/PKM/PB8.cs Normal file
View file

@ -0,0 +1,110 @@
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
public sealed class PB8 : G8PKM
{
private static readonly ushort[] Unused =
{
// Alignment bytes
0x17, 0x1A, 0x1B, 0x23, 0x33, 0x3E, 0x3F,
0x4C, 0x4D, 0x4E, 0x4F,
0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x91, 0x92, 0x93,
0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xC5,
0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB,
0xE0, 0xE1, // Old Console Region / Region
0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0x115, 0x11F, // Alignment
0x13D, 0x13E, 0x13F,
0x140, 0x141, 0x142, 0x143, 0x144, 0x145, 0x146, 0x147,
};
public override IReadOnlyList<ushort> ExtraBytes => Unused;
public override PersonalInfo PersonalInfo => PersonalTable.BDSP.GetFormEntry(Species, Form);
public PB8() => Egg_Location = Met_Location = Locations.Default8bNone;
public PB8(byte[] data) : base(data) { }
public override PKM Clone() => new PB8((byte[])Data.Clone());
public void Trade(ITrainerInfo tr, int Day = 1, int Month = 1, int Year = 2015)
{
if (IsEgg)
{
// Eggs do not have any modifications done if they are traded
// Apply link trade data, only if it left the OT (ignore if dumped & imported, or cloned, etc)
// if ((tr.OT != OT_Name) || (tr.TID != TID) || (tr.SID != SID) || (tr.Gender != OT_Gender))
// SetLinkTradeEgg(Day, Month, Year, Locations.LinkTrade6NPC);
// return;
}
// Process to the HT if the OT of the Pokémon does not match the SAV's OT info.
if (!TradeOT(tr))
TradeHT(tr);
}
public void FixMemories()
{
if (BDSP)
{
OT_TextVar = OT_Memory = OT_Intensity = OT_Feeling = 0;
HT_TextVar = HT_Memory = HT_Intensity = HT_Feeling = 0; // future inter-format conversion?
}
if (IsEgg) // No memories if is egg.
{
HT_Language = HT_Friendship = HT_TextVar = HT_Memory = HT_Intensity = HT_Feeling =
/* OT_Friendship */ OT_TextVar = OT_Memory = OT_Intensity = OT_Feeling = 0;
// Clear Handler
HT_Trash.Clear();
return;
}
if (IsUntraded)
HT_Language = HT_Friendship = HT_TextVar = HT_Memory = HT_Intensity = HT_Feeling = 0;
int gen = Generation;
if (gen < 6)
OT_TextVar = OT_Memory = OT_Intensity = OT_Feeling = 0;
// if (gen != 8) // must be transferred via HOME, and must have memories
// this.SetTradeMemoryHT8(); // not faking HOME tracker.
}
private bool TradeOT(ITrainerInfo tr)
{
// Check to see if the OT matches the SAV's OT info.
if (!(tr.OT == OT_Name && tr.TID == TID && tr.SID == SID && tr.Gender == OT_Gender))
return false;
CurrentHandler = 0;
return true;
}
private void TradeHT(ITrainerInfo tr)
{
if (HT_Name != tr.OT)
{
HT_Friendship = 50;
HT_Name = tr.OT;
}
CurrentHandler = 1;
HT_Gender = tr.Gender;
HT_Language = tr.Language;
//this.SetTradeMemoryHT8();
}
// Maximums
public override int MaxMoveID => Legal.MaxMoveID_8b;
public override int MaxSpeciesID => Legal.MaxSpeciesID_8b;
public override int MaxAbilityID => Legal.MaxAbilityID_8b;
public override int MaxItemID => Legal.MaxItemID_8b;
public override int MaxBallID => Legal.MaxBallID_8b;
public override int MaxGameID => Legal.MaxGameID_8b;
}
}

View file

@ -109,7 +109,7 @@ namespace PKHeX.Core
public byte CNT_Smart { get => Data[0x27]; set => Data[0x27] = value; }
public byte CNT_Tough { get => Data[0x28]; set => Data[0x28] = value; }
public byte CNT_Sheen { get => Data[0x29]; set => Data[0x29] = value; }
public byte ResortEventStatus { get => Data[0x2A]; set => Data[0x2A] = value; }
public ResortEventState ResortEventStatus { get => (ResortEventState)Data[0x2A]; set => Data[0x2A] = (byte)value; }
private byte PKRS { get => Data[0x2B]; set => Data[0x2B] = value; }
public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); }
public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | value << 4); }
@ -656,4 +656,29 @@ namespace PKHeX.Core
return pk8; // Done!
}
}
public enum ResortEventState : byte
{
NONE = 0,
SEIKAKU = 1,
CARE = 2,
LIKE_RESORT = 3,
LIKE_BATTLE = 4,
LIKE_ADV = 5,
GOOD_FRIEND = 6,
GIM = 7,
HOTSPA = 8,
WILD = 9,
WILD_LOVE = 10,
WILD_LIVE = 11,
POKEMAME_GET1 = 12,
POKEMAME_GET2 = 13,
POKEMAME_GET3 = 14,
KINOMI_HELP = 15,
PLAY_STATE = 16,
HOTSPA_STATE = 17,
HOTSPA_DIZZY = 18,
HOTSPA_EGG_HATCHING = 19,
MAX = 20,
}
}

View file

@ -4,9 +4,7 @@ using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
public sealed class PK8 : PKM,
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8,
IContestStats, IContestStatsMutable, IHyperTrain, IScaledSize, IGigantamax, IFavorite, IDynamaxLevel, IRibbonIndex, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories
public sealed class PK8 : G8PKM
{
private static readonly ushort[] Unused =
{
@ -29,107 +27,12 @@ namespace PKHeX.Core
};
public override IReadOnlyList<ushort> ExtraBytes => Unused;
public override int Format => 8;
public override PersonalInfo PersonalInfo => PersonalTable.SWSH.GetFormEntry(Species, Form);
public PK8() : base(PokeCrypto.SIZE_8PARTY) => AffixedRibbon = -1; // 00 would make it show Kalos Champion :)
public PK8(byte[] data) : base(DecryptParty(data)) { }
private static byte[] DecryptParty(byte[] data)
{
PokeCrypto.DecryptIfEncrypted8(ref data);
Array.Resize(ref data, PokeCrypto.SIZE_8PARTY);
return data;
}
protected override ushort CalculateChecksum()
{
ushort chk = 0;
for (int i = 8; i < PokeCrypto.SIZE_8STORED; i += 2)
chk += BitConverter.ToUInt16(Data, i);
return chk;
}
// Simple Generated Attributes
public override int CurrentFriendship
{
get => CurrentHandler == 0 ? OT_Friendship : HT_Friendship;
set { if (CurrentHandler == 0) OT_Friendship = value; else HT_Friendship = value; }
}
public PK8() => AffixedRibbon = -1; // 00 would make it show Kalos Champion :)
public PK8(byte[] data) : base(data) { }
public override PKM Clone() => new PK8((byte[])Data.Clone());
private string GetString(int offset, int count) => StringConverter.GetString7b(Data, offset, count);
private static byte[] SetString(string value, int maxLength) => StringConverter.SetString7b(value, maxLength);
public override int SIZE_PARTY => PokeCrypto.SIZE_8PARTY;
public override int SIZE_STORED => PokeCrypto.SIZE_8STORED;
// Trash Bytes
public override Span<byte> Nickname_Trash { get => Data.AsSpan(0x58, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0x58)); } }
public override Span<byte> HT_Trash { get => Data.AsSpan(0xA8, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xA8)); } }
public override Span<byte> OT_Trash { get => Data.AsSpan(0xF8, 24); set { if (value.Length == 24) value.CopyTo(Data.AsSpan(0xF8)); } }
// Maximums
public override int MaxIV => 31;
public override int MaxEV => 252;
public override int OTLength => 12;
public override int NickLength => 12;
public override int PSV => (int)((PID >> 16 ^ (PID & 0xFFFF)) >> 4);
public override int TSV => (TID ^ SID) >> 4;
public override bool IsUntraded => Data[0xA8] == 0 && Data[0xA8 + 1] == 0 && Format == Generation; // immediately terminated HT_Name data (\0)
// Complex Generated Attributes
public override int Characteristic
{
get
{
int pm6 = (int)(EncryptionConstant % 6);
int maxIV = MaximumIV;
int pm6stat = 0;
for (int i = 0; i < 6; i++)
{
pm6stat = (pm6 + i) % 6;
if (GetIV(pm6stat) == maxIV)
break;
}
return (pm6stat * 5) + (maxIV % 5);
}
}
// Methods
protected override byte[] Encrypt()
{
RefreshChecksum();
return PokeCrypto.EncryptArray8(Data);
}
public void FixRelearn()
{
while (true)
{
if (RelearnMove4 != 0 && RelearnMove3 == 0)
{
RelearnMove3 = RelearnMove4;
RelearnMove4 = 0;
}
if (RelearnMove3 != 0 && RelearnMove2 == 0)
{
RelearnMove2 = RelearnMove3;
RelearnMove3 = 0;
continue;
}
if (RelearnMove2 != 0 && RelearnMove1 == 0)
{
RelearnMove1 = RelearnMove2;
RelearnMove2 = 0;
continue;
}
break;
}
}
public void Trade(ITrainerInfo tr, int Day = 1, int Month = 1, int Year = 2015)
{
if (IsEgg)
@ -145,391 +48,8 @@ namespace PKHeX.Core
if (!TradeOT(tr))
TradeHT(tr);
}
public override uint EncryptionConstant { get => BitConverter.ToUInt32(Data, 0x00); set => BitConverter.GetBytes(value).CopyTo(Data, 0x00); }
public override ushort Sanity { get => BitConverter.ToUInt16(Data, 0x04); set => BitConverter.GetBytes(value).CopyTo(Data, 0x04); }
public override ushort Checksum { get => BitConverter.ToUInt16(Data, 0x06); set => BitConverter.GetBytes(value).CopyTo(Data, 0x06); }
// Structure
#region Block A
public override int Species { get => BitConverter.ToUInt16(Data, 0x08); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x08); }
public override int HeldItem { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); }
public override int TID { get => BitConverter.ToUInt16(Data, 0x0C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0C); }
public override int SID { get => BitConverter.ToUInt16(Data, 0x0E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0E); }
public override uint EXP { get => BitConverter.ToUInt32(Data, 0x10); set => BitConverter.GetBytes(value).CopyTo(Data, 0x10); }
public override int Ability { get => BitConverter.ToUInt16(Data, 0x14); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14); }
public override int AbilityNumber { get => Data[0x16] & 7; set => Data[0x16] = (byte)((Data[0x16] & ~7) | (value & 7)); }
public bool Favorite { get => (Data[0x16] & 8) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~8) | ((value ? 1 : 0) << 3)); } // unused, was in LGPE but not in SWSH
public bool CanGigantamax { get => (Data[0x16] & 16) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~16) | (value ? 16 : 0)); }
// 0x17 alignment unused
public override int MarkValue { get => BitConverter.ToUInt16(Data, 0x18); protected set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); }
// 0x1A alignment unused
// 0x1B alignment unused
public override uint PID { get => BitConverter.ToUInt32(Data, 0x1C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x1C); }
public override int Nature { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override int StatNature { get => Data[0x21]; set => Data[0x21] = (byte)value; }
public override bool FatefulEncounter { get => (Data[0x22] & 1) == 1; set => Data[0x22] = (byte)((Data[0x22] & ~0x01) | (value ? 1 : 0)); }
public bool Flag2 { get => (Data[0x22] & 2) == 2; set => Data[0x22] = (byte)((Data[0x22] & ~0x02) | (value ? 2 : 0)); }
public override int Gender { get => (Data[0x22] >> 2) & 0x3; set => Data[0x22] = (byte)((Data[0x22] & 0xF3) | (value << 2)); }
// 0x23 alignment unused
public override int Form { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); }
public override int EV_HP { get => Data[0x26]; set => Data[0x26] = (byte)value; }
public override int EV_ATK { get => Data[0x27]; set => Data[0x27] = (byte)value; }
public override int EV_DEF { get => Data[0x28]; set => Data[0x28] = (byte)value; }
public override int EV_SPE { get => Data[0x29]; set => Data[0x29] = (byte)value; }
public override int EV_SPA { get => Data[0x2A]; set => Data[0x2A] = (byte)value; }
public override int EV_SPD { get => Data[0x2B]; set => Data[0x2B] = (byte)value; }
public byte CNT_Cool { get => Data[0x2C]; set => Data[0x2C] = value; }
public byte CNT_Beauty { get => Data[0x2D]; set => Data[0x2D] = value; }
public byte CNT_Cute { get => Data[0x2E]; set => Data[0x2E] = value; }
public byte CNT_Smart { get => Data[0x2F]; set => Data[0x2F] = value; }
public byte CNT_Tough { get => Data[0x30]; set => Data[0x30] = value; }
public byte CNT_Sheen { get => Data[0x31]; set => Data[0x31] = value; }
private byte PKRS { get => Data[0x32]; set => Data[0x32] = value; }
public override int PKRS_Days { get => PKRS & 0xF; set => PKRS = (byte)((PKRS & ~0xF) | value); }
public override int PKRS_Strain { get => PKRS >> 4; set => PKRS = (byte)((PKRS & 0xF) | value << 4); }
// 0x33 unused padding
// ribbon u32
public bool RibbonChampionKalos { get => FlagUtil.GetFlag(Data, 0x34, 0); set => FlagUtil.SetFlag(Data, 0x34, 0, value); }
public bool RibbonChampionG3 { get => FlagUtil.GetFlag(Data, 0x34, 1); set => FlagUtil.SetFlag(Data, 0x34, 1, value); }
public bool RibbonChampionSinnoh { get => FlagUtil.GetFlag(Data, 0x34, 2); set => FlagUtil.SetFlag(Data, 0x34, 2, value); }
public bool RibbonBestFriends { get => FlagUtil.GetFlag(Data, 0x34, 3); set => FlagUtil.SetFlag(Data, 0x34, 3, value); }
public bool RibbonTraining { get => FlagUtil.GetFlag(Data, 0x34, 4); set => FlagUtil.SetFlag(Data, 0x34, 4, value); }
public bool RibbonBattlerSkillful { get => FlagUtil.GetFlag(Data, 0x34, 5); set => FlagUtil.SetFlag(Data, 0x34, 5, value); }
public bool RibbonBattlerExpert { get => FlagUtil.GetFlag(Data, 0x34, 6); set => FlagUtil.SetFlag(Data, 0x34, 6, value); }
public bool RibbonEffort { get => FlagUtil.GetFlag(Data, 0x34, 7); set => FlagUtil.SetFlag(Data, 0x34, 7, value); }
public bool RibbonAlert { get => FlagUtil.GetFlag(Data, 0x35, 0); set => FlagUtil.SetFlag(Data, 0x35, 0, value); }
public bool RibbonShock { get => FlagUtil.GetFlag(Data, 0x35, 1); set => FlagUtil.SetFlag(Data, 0x35, 1, value); }
public bool RibbonDowncast { get => FlagUtil.GetFlag(Data, 0x35, 2); set => FlagUtil.SetFlag(Data, 0x35, 2, value); }
public bool RibbonCareless { get => FlagUtil.GetFlag(Data, 0x35, 3); set => FlagUtil.SetFlag(Data, 0x35, 3, value); }
public bool RibbonRelax { get => FlagUtil.GetFlag(Data, 0x35, 4); set => FlagUtil.SetFlag(Data, 0x35, 4, value); }
public bool RibbonSnooze { get => FlagUtil.GetFlag(Data, 0x35, 5); set => FlagUtil.SetFlag(Data, 0x35, 5, value); }
public bool RibbonSmile { get => FlagUtil.GetFlag(Data, 0x35, 6); set => FlagUtil.SetFlag(Data, 0x35, 6, value); }
public bool RibbonGorgeous { get => FlagUtil.GetFlag(Data, 0x35, 7); set => FlagUtil.SetFlag(Data, 0x35, 7, value); }
public bool RibbonRoyal { get => FlagUtil.GetFlag(Data, 0x36, 0); set => FlagUtil.SetFlag(Data, 0x36, 0, value); }
public bool RibbonGorgeousRoyal { get => FlagUtil.GetFlag(Data, 0x36, 1); set => FlagUtil.SetFlag(Data, 0x36, 1, value); }
public bool RibbonArtist { get => FlagUtil.GetFlag(Data, 0x36, 2); set => FlagUtil.SetFlag(Data, 0x36, 2, value); }
public bool RibbonFootprint { get => FlagUtil.GetFlag(Data, 0x36, 3); set => FlagUtil.SetFlag(Data, 0x36, 3, value); }
public bool RibbonRecord { get => FlagUtil.GetFlag(Data, 0x36, 4); set => FlagUtil.SetFlag(Data, 0x36, 4, value); }
public bool RibbonLegend { get => FlagUtil.GetFlag(Data, 0x36, 5); set => FlagUtil.SetFlag(Data, 0x36, 5, value); }
public bool RibbonCountry { get => FlagUtil.GetFlag(Data, 0x36, 6); set => FlagUtil.SetFlag(Data, 0x36, 6, value); }
public bool RibbonNational { get => FlagUtil.GetFlag(Data, 0x36, 7); set => FlagUtil.SetFlag(Data, 0x36, 7, value); }
public bool RibbonEarth { get => FlagUtil.GetFlag(Data, 0x37, 0); set => FlagUtil.SetFlag(Data, 0x37, 0, value); }
public bool RibbonWorld { get => FlagUtil.GetFlag(Data, 0x37, 1); set => FlagUtil.SetFlag(Data, 0x37, 1, value); }
public bool RibbonClassic { get => FlagUtil.GetFlag(Data, 0x37, 2); set => FlagUtil.SetFlag(Data, 0x37, 2, value); }
public bool RibbonPremier { get => FlagUtil.GetFlag(Data, 0x37, 3); set => FlagUtil.SetFlag(Data, 0x37, 3, value); }
public bool RibbonEvent { get => FlagUtil.GetFlag(Data, 0x37, 4); set => FlagUtil.SetFlag(Data, 0x37, 4, value); }
public bool RibbonBirthday { get => FlagUtil.GetFlag(Data, 0x37, 5); set => FlagUtil.SetFlag(Data, 0x37, 5, value); }
public bool RibbonSpecial { get => FlagUtil.GetFlag(Data, 0x37, 6); set => FlagUtil.SetFlag(Data, 0x37, 6, value); }
public bool RibbonSouvenir { get => FlagUtil.GetFlag(Data, 0x37, 7); set => FlagUtil.SetFlag(Data, 0x37, 7, value); }
// ribbon u32
public bool RibbonWishing { get => FlagUtil.GetFlag(Data, 0x38, 0); set => FlagUtil.SetFlag(Data, 0x38, 0, value); }
public bool RibbonChampionBattle { get => FlagUtil.GetFlag(Data, 0x38, 1); set => FlagUtil.SetFlag(Data, 0x38, 1, value); }
public bool RibbonChampionRegional { get => FlagUtil.GetFlag(Data, 0x38, 2); set => FlagUtil.SetFlag(Data, 0x38, 2, value); }
public bool RibbonChampionNational { get => FlagUtil.GetFlag(Data, 0x38, 3); set => FlagUtil.SetFlag(Data, 0x38, 3, value); }
public bool RibbonChampionWorld { get => FlagUtil.GetFlag(Data, 0x38, 4); set => FlagUtil.SetFlag(Data, 0x38, 4, value); }
public bool HasContestMemoryRibbon { get => FlagUtil.GetFlag(Data, 0x38, 5); set => FlagUtil.SetFlag(Data, 0x38, 5, value); }
public bool HasBattleMemoryRibbon { get => FlagUtil.GetFlag(Data, 0x38, 6); set => FlagUtil.SetFlag(Data, 0x38, 6, value); }
public bool RibbonChampionG6Hoenn { get => FlagUtil.GetFlag(Data, 0x38, 7); set => FlagUtil.SetFlag(Data, 0x38, 7, value); }
public bool RibbonContestStar { get => FlagUtil.GetFlag(Data, 0x39, 0); set => FlagUtil.SetFlag(Data, 0x39, 0, value); }
public bool RibbonMasterCoolness { get => FlagUtil.GetFlag(Data, 0x39, 1); set => FlagUtil.SetFlag(Data, 0x39, 1, value); }
public bool RibbonMasterBeauty { get => FlagUtil.GetFlag(Data, 0x39, 2); set => FlagUtil.SetFlag(Data, 0x39, 2, value); }
public bool RibbonMasterCuteness { get => FlagUtil.GetFlag(Data, 0x39, 3); set => FlagUtil.SetFlag(Data, 0x39, 3, value); }
public bool RibbonMasterCleverness { get => FlagUtil.GetFlag(Data, 0x39, 4); set => FlagUtil.SetFlag(Data, 0x39, 4, value); }
public bool RibbonMasterToughness { get => FlagUtil.GetFlag(Data, 0x39, 5); set => FlagUtil.SetFlag(Data, 0x39, 5, value); }
public bool RibbonChampionAlola { get => FlagUtil.GetFlag(Data, 0x39, 6); set => FlagUtil.SetFlag(Data, 0x39, 6, value); }
public bool RibbonBattleRoyale { get => FlagUtil.GetFlag(Data, 0x39, 7); set => FlagUtil.SetFlag(Data, 0x39, 7, value); }
public bool RibbonBattleTreeGreat { get => FlagUtil.GetFlag(Data, 0x3A, 0); set => FlagUtil.SetFlag(Data, 0x3A, 0, value); }
public bool RibbonBattleTreeMaster { get => FlagUtil.GetFlag(Data, 0x3A, 1); set => FlagUtil.SetFlag(Data, 0x3A, 1, value); }
public bool RibbonChampionGalar { get => FlagUtil.GetFlag(Data, 0x3A, 2); set => FlagUtil.SetFlag(Data, 0x3A, 2, value); }
public bool RibbonTowerMaster { get => FlagUtil.GetFlag(Data, 0x3A, 3); set => FlagUtil.SetFlag(Data, 0x3A, 3, value); }
public bool RibbonMasterRank { get => FlagUtil.GetFlag(Data, 0x3A, 4); set => FlagUtil.SetFlag(Data, 0x3A, 4, value); }
public bool RibbonMarkLunchtime { get => FlagUtil.GetFlag(Data, 0x3A, 5); set => FlagUtil.SetFlag(Data, 0x3A, 5, value); }
public bool RibbonMarkSleepyTime { get => FlagUtil.GetFlag(Data, 0x3A, 6); set => FlagUtil.SetFlag(Data, 0x3A, 6, value); }
public bool RibbonMarkDusk { get => FlagUtil.GetFlag(Data, 0x3A, 7); set => FlagUtil.SetFlag(Data, 0x3A, 7, value); }
public bool RibbonMarkDawn { get => FlagUtil.GetFlag(Data, 0x3B, 0); set => FlagUtil.SetFlag(Data, 0x3B, 0, value); }
public bool RibbonMarkCloudy { get => FlagUtil.GetFlag(Data, 0x3B, 1); set => FlagUtil.SetFlag(Data, 0x3B, 1, value); }
public bool RibbonMarkRainy { get => FlagUtil.GetFlag(Data, 0x3B, 2); set => FlagUtil.SetFlag(Data, 0x3B, 2, value); }
public bool RibbonMarkStormy { get => FlagUtil.GetFlag(Data, 0x3B, 3); set => FlagUtil.SetFlag(Data, 0x3B, 3, value); }
public bool RibbonMarkSnowy { get => FlagUtil.GetFlag(Data, 0x3B, 4); set => FlagUtil.SetFlag(Data, 0x3B, 4, value); }
public bool RibbonMarkBlizzard { get => FlagUtil.GetFlag(Data, 0x3B, 5); set => FlagUtil.SetFlag(Data, 0x3B, 5, value); }
public bool RibbonMarkDry { get => FlagUtil.GetFlag(Data, 0x3B, 6); set => FlagUtil.SetFlag(Data, 0x3B, 6, value); }
public bool RibbonMarkSandstorm { get => FlagUtil.GetFlag(Data, 0x3B, 7); set => FlagUtil.SetFlag(Data, 0x3B, 7, value); }
public int RibbonCountMemoryContest { get => Data[0x3C]; set => HasContestMemoryRibbon = (Data[0x3C] = (byte)value) != 0; }
public int RibbonCountMemoryBattle { get => Data[0x3D]; set => HasBattleMemoryRibbon = (Data[0x3D] = (byte)value) != 0; }
// 0x3E padding
// 0x3F padding
// 0x40 Ribbon 1
public bool RibbonMarkMisty { get => FlagUtil.GetFlag(Data, 0x40, 0); set => FlagUtil.SetFlag(Data, 0x40, 0, value); }
public bool RibbonMarkDestiny { get => FlagUtil.GetFlag(Data, 0x40, 1); set => FlagUtil.SetFlag(Data, 0x40, 1, value); }
public bool RibbonMarkFishing { get => FlagUtil.GetFlag(Data, 0x40, 2); set => FlagUtil.SetFlag(Data, 0x40, 2, value); }
public bool RibbonMarkCurry { get => FlagUtil.GetFlag(Data, 0x40, 3); set => FlagUtil.SetFlag(Data, 0x40, 3, value); }
public bool RibbonMarkUncommon { get => FlagUtil.GetFlag(Data, 0x40, 4); set => FlagUtil.SetFlag(Data, 0x40, 4, value); }
public bool RibbonMarkRare { get => FlagUtil.GetFlag(Data, 0x40, 5); set => FlagUtil.SetFlag(Data, 0x40, 5, value); }
public bool RibbonMarkRowdy { get => FlagUtil.GetFlag(Data, 0x40, 6); set => FlagUtil.SetFlag(Data, 0x40, 6, value); }
public bool RibbonMarkAbsentMinded { get => FlagUtil.GetFlag(Data, 0x40, 7); set => FlagUtil.SetFlag(Data, 0x40, 7, value); }
public bool RibbonMarkJittery { get => FlagUtil.GetFlag(Data, 0x41, 0); set => FlagUtil.SetFlag(Data, 0x41, 0, value); }
public bool RibbonMarkExcited { get => FlagUtil.GetFlag(Data, 0x41, 1); set => FlagUtil.SetFlag(Data, 0x41, 1, value); }
public bool RibbonMarkCharismatic { get => FlagUtil.GetFlag(Data, 0x41, 2); set => FlagUtil.SetFlag(Data, 0x41, 2, value); }
public bool RibbonMarkCalmness { get => FlagUtil.GetFlag(Data, 0x41, 3); set => FlagUtil.SetFlag(Data, 0x41, 3, value); }
public bool RibbonMarkIntense { get => FlagUtil.GetFlag(Data, 0x41, 4); set => FlagUtil.SetFlag(Data, 0x41, 4, value); }
public bool RibbonMarkZonedOut { get => FlagUtil.GetFlag(Data, 0x41, 5); set => FlagUtil.SetFlag(Data, 0x41, 5, value); }
public bool RibbonMarkJoyful { get => FlagUtil.GetFlag(Data, 0x41, 6); set => FlagUtil.SetFlag(Data, 0x41, 6, value); }
public bool RibbonMarkAngry { get => FlagUtil.GetFlag(Data, 0x41, 7); set => FlagUtil.SetFlag(Data, 0x41, 7, value); }
public bool RibbonMarkSmiley { get => FlagUtil.GetFlag(Data, 0x42, 0); set => FlagUtil.SetFlag(Data, 0x42, 0, value); }
public bool RibbonMarkTeary { get => FlagUtil.GetFlag(Data, 0x42, 1); set => FlagUtil.SetFlag(Data, 0x42, 1, value); }
public bool RibbonMarkUpbeat { get => FlagUtil.GetFlag(Data, 0x42, 2); set => FlagUtil.SetFlag(Data, 0x42, 2, value); }
public bool RibbonMarkPeeved { get => FlagUtil.GetFlag(Data, 0x42, 3); set => FlagUtil.SetFlag(Data, 0x42, 3, value); }
public bool RibbonMarkIntellectual { get => FlagUtil.GetFlag(Data, 0x42, 4); set => FlagUtil.SetFlag(Data, 0x42, 4, value); }
public bool RibbonMarkFerocious { get => FlagUtil.GetFlag(Data, 0x42, 5); set => FlagUtil.SetFlag(Data, 0x42, 5, value); }
public bool RibbonMarkCrafty { get => FlagUtil.GetFlag(Data, 0x42, 6); set => FlagUtil.SetFlag(Data, 0x42, 6, value); }
public bool RibbonMarkScowling { get => FlagUtil.GetFlag(Data, 0x42, 7); set => FlagUtil.SetFlag(Data, 0x42, 7, value); }
public bool RibbonMarkKindly { get => FlagUtil.GetFlag(Data, 0x43, 0); set => FlagUtil.SetFlag(Data, 0x43, 0, value); }
public bool RibbonMarkFlustered { get => FlagUtil.GetFlag(Data, 0x43, 1); set => FlagUtil.SetFlag(Data, 0x43, 1, value); }
public bool RibbonMarkPumpedUp { get => FlagUtil.GetFlag(Data, 0x43, 2); set => FlagUtil.SetFlag(Data, 0x43, 2, value); }
public bool RibbonMarkZeroEnergy { get => FlagUtil.GetFlag(Data, 0x43, 3); set => FlagUtil.SetFlag(Data, 0x43, 3, value); }
public bool RibbonMarkPrideful { get => FlagUtil.GetFlag(Data, 0x43, 4); set => FlagUtil.SetFlag(Data, 0x43, 4, value); }
public bool RibbonMarkUnsure { get => FlagUtil.GetFlag(Data, 0x43, 5); set => FlagUtil.SetFlag(Data, 0x43, 5, value); }
public bool RibbonMarkHumble { get => FlagUtil.GetFlag(Data, 0x43, 6); set => FlagUtil.SetFlag(Data, 0x43, 6, value); }
public bool RibbonMarkThorny { get => FlagUtil.GetFlag(Data, 0x43, 7); set => FlagUtil.SetFlag(Data, 0x43, 7, value); }
// 0x44 Ribbon 2
public bool RibbonMarkVigor { get => FlagUtil.GetFlag(Data, 0x44, 0); set => FlagUtil.SetFlag(Data, 0x44, 0, value); }
public bool RibbonMarkSlump { get => FlagUtil.GetFlag(Data, 0x44, 1); set => FlagUtil.SetFlag(Data, 0x44, 1, value); }
public bool RIB44_2 { get => FlagUtil.GetFlag(Data, 0x44, 2); set => FlagUtil.SetFlag(Data, 0x44, 2, value); }
public bool RIB44_3 { get => FlagUtil.GetFlag(Data, 0x44, 3); set => FlagUtil.SetFlag(Data, 0x44, 3, value); }
public bool RIB44_4 { get => FlagUtil.GetFlag(Data, 0x44, 4); set => FlagUtil.SetFlag(Data, 0x44, 4, value); }
public bool RIB44_5 { get => FlagUtil.GetFlag(Data, 0x44, 5); set => FlagUtil.SetFlag(Data, 0x44, 5, value); }
public bool RIB44_6 { get => FlagUtil.GetFlag(Data, 0x44, 6); set => FlagUtil.SetFlag(Data, 0x44, 6, value); }
public bool RIB44_7 { get => FlagUtil.GetFlag(Data, 0x44, 7); set => FlagUtil.SetFlag(Data, 0x44, 7, value); }
public bool RIB45_0 { get => FlagUtil.GetFlag(Data, 0x45, 0); set => FlagUtil.SetFlag(Data, 0x45, 0, value); }
public bool RIB45_1 { get => FlagUtil.GetFlag(Data, 0x45, 1); set => FlagUtil.SetFlag(Data, 0x45, 1, value); }
public bool RIB45_2 { get => FlagUtil.GetFlag(Data, 0x45, 2); set => FlagUtil.SetFlag(Data, 0x45, 2, value); }
public bool RIB45_3 { get => FlagUtil.GetFlag(Data, 0x45, 3); set => FlagUtil.SetFlag(Data, 0x45, 3, value); }
public bool RIB45_4 { get => FlagUtil.GetFlag(Data, 0x45, 4); set => FlagUtil.SetFlag(Data, 0x45, 4, value); }
public bool RIB45_5 { get => FlagUtil.GetFlag(Data, 0x45, 5); set => FlagUtil.SetFlag(Data, 0x45, 5, value); }
public bool RIB45_6 { get => FlagUtil.GetFlag(Data, 0x45, 6); set => FlagUtil.SetFlag(Data, 0x45, 6, value); }
public bool RIB45_7 { get => FlagUtil.GetFlag(Data, 0x45, 7); set => FlagUtil.SetFlag(Data, 0x45, 7, value); }
public bool RIB46_0 { get => FlagUtil.GetFlag(Data, 0x41, 0); set => FlagUtil.SetFlag(Data, 0x41, 0, value); }
public bool RIB46_1 { get => FlagUtil.GetFlag(Data, 0x46, 1); set => FlagUtil.SetFlag(Data, 0x46, 1, value); }
public bool RIB46_2 { get => FlagUtil.GetFlag(Data, 0x46, 2); set => FlagUtil.SetFlag(Data, 0x46, 2, value); }
public bool RIB46_3 { get => FlagUtil.GetFlag(Data, 0x46, 3); set => FlagUtil.SetFlag(Data, 0x46, 3, value); }
public bool RIB46_4 { get => FlagUtil.GetFlag(Data, 0x46, 4); set => FlagUtil.SetFlag(Data, 0x46, 4, value); }
public bool RIB46_5 { get => FlagUtil.GetFlag(Data, 0x46, 5); set => FlagUtil.SetFlag(Data, 0x46, 5, value); }
public bool RIB46_6 { get => FlagUtil.GetFlag(Data, 0x46, 6); set => FlagUtil.SetFlag(Data, 0x46, 6, value); }
public bool RIB46_7 { get => FlagUtil.GetFlag(Data, 0x46, 7); set => FlagUtil.SetFlag(Data, 0x46, 7, value); }
public bool RIB47_0 { get => FlagUtil.GetFlag(Data, 0x47, 0); set => FlagUtil.SetFlag(Data, 0x47, 0, value); }
public bool RIB47_1 { get => FlagUtil.GetFlag(Data, 0x47, 1); set => FlagUtil.SetFlag(Data, 0x47, 1, value); }
public bool RIB47_2 { get => FlagUtil.GetFlag(Data, 0x47, 2); set => FlagUtil.SetFlag(Data, 0x47, 2, value); }
public bool RIB47_3 { get => FlagUtil.GetFlag(Data, 0x47, 3); set => FlagUtil.SetFlag(Data, 0x47, 3, value); }
public bool RIB47_4 { get => FlagUtil.GetFlag(Data, 0x47, 4); set => FlagUtil.SetFlag(Data, 0x47, 4, value); }
public bool RIB47_5 { get => FlagUtil.GetFlag(Data, 0x47, 5); set => FlagUtil.SetFlag(Data, 0x47, 5, value); }
public bool RIB47_6 { get => FlagUtil.GetFlag(Data, 0x47, 6); set => FlagUtil.SetFlag(Data, 0x47, 6, value); }
public bool RIB47_7 { get => FlagUtil.GetFlag(Data, 0x47, 7); set => FlagUtil.SetFlag(Data, 0x47, 7, value); }
public bool HasMark()
{
var d = Data;
if ((BitConverter.ToUInt16(d, 0x3A) & 0xFFE0) != 0)
return true;
if (BitConverter.ToUInt32(d, 0x40) != 0)
return true;
return (d[0x44] & 3) != 0;
}
public uint Sociability { get => BitConverter.ToUInt32(Data, 0x48); set => BitConverter.GetBytes(value).CopyTo(Data, 0x48); }
// 0x4C-0x4F unused
public int HeightScalar { get => Data[0x50]; set => Data[0x50] = (byte)value; }
public int WeightScalar { get => Data[0x51]; set => Data[0x51] = (byte)value; }
// 0x52-0x57 unused
#endregion
#region Block B
public override string Nickname
{
get => GetString(0x58, 24);
set => SetString(value, 12).CopyTo(Data, 0x58);
}
// 2 bytes for \0, automatically handled above
public override int Move1 { get => BitConverter.ToUInt16(Data, 0x72); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x72); }
public override int Move2 { get => BitConverter.ToUInt16(Data, 0x74); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x74); }
public override int Move3 { get => BitConverter.ToUInt16(Data, 0x76); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x76); }
public override int Move4 { get => BitConverter.ToUInt16(Data, 0x78); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x78); }
public override int Move1_PP { get => Data[0x7A]; set => Data[0x7A] = (byte)value; }
public override int Move2_PP { get => Data[0x7B]; set => Data[0x7B] = (byte)value; }
public override int Move3_PP { get => Data[0x7C]; set => Data[0x7C] = (byte)value; }
public override int Move4_PP { get => Data[0x7D]; set => Data[0x7D] = (byte)value; }
public override int Move1_PPUps { get => Data[0x7E]; set => Data[0x7E] = (byte)value; }
public override int Move2_PPUps { get => Data[0x7F]; set => Data[0x7F] = (byte)value; }
public override int Move3_PPUps { get => Data[0x80]; set => Data[0x80] = (byte)value; }
public override int Move4_PPUps { get => Data[0x81]; set => Data[0x81] = (byte)value; }
public override int RelearnMove1 { get => BitConverter.ToUInt16(Data, 0x82); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x82); }
public override int RelearnMove2 { get => BitConverter.ToUInt16(Data, 0x84); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x84); }
public override int RelearnMove3 { get => BitConverter.ToUInt16(Data, 0x86); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x86); }
public override int RelearnMove4 { get => BitConverter.ToUInt16(Data, 0x88); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x88); }
public override int Stat_HPCurrent { get => BitConverter.ToUInt16(Data, 0x8A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x8A); }
private uint IV32 { get => BitConverter.ToUInt32(Data, 0x8C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x8C); }
public override int IV_HP { get => (int)(IV32 >> 00) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 00)) | ((value > 31 ? 31u : (uint)value) << 00); }
public override int IV_ATK { get => (int)(IV32 >> 05) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 05)) | ((value > 31 ? 31u : (uint)value) << 05); }
public override int IV_DEF { get => (int)(IV32 >> 10) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 10)) | ((value > 31 ? 31u : (uint)value) << 10); }
public override int IV_SPE { get => (int)(IV32 >> 15) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 15)) | ((value > 31 ? 31u : (uint)value) << 15); }
public override int IV_SPA { get => (int)(IV32 >> 20) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 20)) | ((value > 31 ? 31u : (uint)value) << 20); }
public override int IV_SPD { get => (int)(IV32 >> 25) & 0x1F; set => IV32 = (IV32 & ~(0x1Fu << 25)) | ((value > 31 ? 31u : (uint)value) << 25); }
public override bool IsEgg { get => ((IV32 >> 30) & 1) == 1; set => IV32 = (IV32 & ~0x40000000u) | (value ? 0x40000000u : 0u); }
public override bool IsNicknamed { get => ((IV32 >> 31) & 1) == 1; set => IV32 = (IV32 & 0x7FFFFFFFu) | (value ? 0x80000000u : 0u); }
public byte DynamaxLevel { get => Data[0x90]; set => Data[0x90] = value; }
// 0x90-0x93 unused
public override int Status_Condition { get => BitConverter.ToInt32(Data, 0x94); set => BitConverter.GetBytes(value).CopyTo(Data, 0x94); }
public int Unk98 { get => BitConverter.ToInt32(Data, 0x98); set => BitConverter.GetBytes(value).CopyTo(Data, 0x98); }
// 0x9C-0xA7 unused
#endregion
#region Block C
public override string HT_Name { get => GetString(0xA8, 24); set => SetString(value, 12).CopyTo(Data, 0xA8); }
public override int HT_Gender { get => Data[0xC2]; set => Data[0xC2] = (byte)value; }
public int HT_Language { get => Data[0xC3]; set => Data[0xC3] = (byte)value; }
public override int CurrentHandler { get => Data[0xC4]; set => Data[0xC4] = (byte)value; }
// 0xC5 unused (alignment)
public int HT_TrainerID { get => BitConverter.ToUInt16(Data, 0xC6); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xC6); } // unused?
public override int HT_Friendship { get => Data[0xC8]; set => Data[0xC8] = (byte)value; }
public int HT_Intensity { get => Data[0xC9]; set => Data[0xC9] = (byte)value; }
public int HT_Memory { get => Data[0xCA]; set => Data[0xCA] = (byte)value; }
public int HT_Feeling { get => Data[0xCB]; set => Data[0xCB] = (byte)value; }
public int HT_TextVar { get => BitConverter.ToUInt16(Data, 0xCC); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0xCC); }
// 0xCE-0xDB unused
public override byte Fullness { get => Data[0xDC]; set => Data[0xDC] = value; }
public override byte Enjoyment { get => Data[0xDD]; set => Data[0xDD] = value; }
public override int Version { get => Data[0xDE]; set => Data[0xDE] = (byte)value; }
public int BattleVersion { get => Data[0xDF]; set => Data[0xDF] = (byte)value; }
// public override int Region { get => Data[0xE0]; set => Data[0xE0] = (byte)value; }
// public override int ConsoleRegion { get => Data[0xE1]; set => Data[0xE1] = (byte)value; }
public override int Language { get => Data[0xE2]; set => Data[0xE2] = (byte)value; }
public int UnkE3 { get => Data[0xE3]; set => Data[0xE3] = (byte)value; }
public uint FormArgument { get => BitConverter.ToUInt32(Data, 0xE4); set => BitConverter.GetBytes(value).CopyTo(Data, 0xE4); }
public byte FormArgumentRemain { get => (byte)FormArgument; set => FormArgument = (FormArgument & ~0xFFu) | value; }
public byte FormArgumentElapsed { get => (byte)(FormArgument >> 8); set => FormArgument = (FormArgument & ~0xFF00u) | (uint)(value << 8); }
public byte FormArgumentMaximum { get => (byte)(FormArgument >> 16); set => FormArgument = (FormArgument & ~0xFF0000u) | (uint)(value << 16); }
public sbyte AffixedRibbon { get => (sbyte)Data[0xE8]; set => Data[0xE8] = (byte)value; } // selected ribbon
// remainder unused
#endregion
#region Block D
public override string OT_Name { get => GetString(0xF8, 24); set => SetString(value, 12).CopyTo(Data, 0xF8); }
public override int OT_Friendship { get => Data[0x112]; set => Data[0x112] = (byte)value; }
public int OT_Intensity { get => Data[0x113]; set => Data[0x113] = (byte)value; }
public int OT_Memory { get => Data[0x114]; set => Data[0x114] = (byte)value; }
// 0x115 unused align
public int OT_TextVar { get => BitConverter.ToUInt16(Data, 0x116); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x116); }
public int OT_Feeling { get => Data[0x118]; set => Data[0x118] = (byte)value; }
public override int Egg_Year { get => Data[0x119]; set => Data[0x119] = (byte)value; }
public override int Egg_Month { get => Data[0x11A]; set => Data[0x11A] = (byte)value; }
public override int Egg_Day { get => Data[0x11B]; set => Data[0x11B] = (byte)value; }
public override int Met_Year { get => Data[0x11C]; set => Data[0x11C] = (byte)value; }
public override int Met_Month { get => Data[0x11D]; set => Data[0x11D] = (byte)value; }
public override int Met_Day { get => Data[0x11E]; set => Data[0x11E] = (byte)value; }
// 0x11F unused align
public override int Egg_Location { get => BitConverter.ToUInt16(Data, 0x120); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x120); }
public override int Met_Location { get => BitConverter.ToUInt16(Data, 0x122); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x122); }
public override int Ball { get => Data[0x124]; set => Data[0x124] = (byte)value; }
public override int Met_Level { get => Data[0x125] & ~0x80; set => Data[0x125] = (byte)((Data[0x125] & 0x80) | value); }
public override int OT_Gender { get => Data[0x125] >> 7; set => Data[0x125] = (byte)((Data[0x125] & ~0x80) | (value << 7)); }
public int HyperTrainFlags { get => Data[0x126]; set => Data[0x126] = (byte)value; }
public bool HT_HP { get => ((HyperTrainFlags >> 0) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 0)) | ((value ? 1 : 0) << 0); }
public bool HT_ATK { get => ((HyperTrainFlags >> 1) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 1)) | ((value ? 1 : 0) << 1); }
public bool HT_DEF { get => ((HyperTrainFlags >> 2) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 2)) | ((value ? 1 : 0) << 2); }
public bool HT_SPA { get => ((HyperTrainFlags >> 3) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 3)) | ((value ? 1 : 0) << 3); }
public bool HT_SPD { get => ((HyperTrainFlags >> 4) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 4)) | ((value ? 1 : 0) << 4); }
public bool HT_SPE { get => ((HyperTrainFlags >> 5) & 1) == 1; set => HyperTrainFlags = (HyperTrainFlags & ~(1 << 5)) | ((value ? 1 : 0) << 5); }
public bool GetMoveRecordFlag(int index)
{
if ((uint) index > 112) // 14 bytes, 8 bits
throw new ArgumentOutOfRangeException(nameof(index));
int ofs = index >> 3;
return FlagUtil.GetFlag(Data, 0x127 + ofs, index & 7);
}
public void SetMoveRecordFlag(int index, bool value)
{
if ((uint)index > 112) // 14 bytes, 8 bits
throw new ArgumentOutOfRangeException(nameof(index));
int ofs = index >> 3;
FlagUtil.SetFlag(Data, 0x127 + ofs, index & 7, value);
}
public bool HasAnyMoveRecordFlag() => Array.FindIndex(Data, 0x127, 14, z => z != 0) >= 0;
// Why did you mis-align this field, GameFreak?
public ulong Tracker
{
get => BitConverter.ToUInt64(Data, 0x135);
set => BitConverter.GetBytes(value).CopyTo(Data, 0x135);
}
#endregion
#region Battle Stats
public override int Stat_Level { get => Data[0x148]; set => Data[0x148] = (byte)value; }
// 0x149 unused alignment
public override int Stat_HPMax { get => BitConverter.ToUInt16(Data, 0x14A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14A); }
public override int Stat_ATK { get => BitConverter.ToUInt16(Data, 0x14C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14C); }
public override int Stat_DEF { get => BitConverter.ToUInt16(Data, 0x14E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x14E); }
public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0x150); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x150); }
public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0x152); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x152); }
public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0x154); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x154); }
public int DynamaxType { get => BitConverter.ToUInt16(Data, 0x156); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x156); }
#endregion
public override int[] Markings
{
get
{
int[] marks = new int[8];
int val = MarkValue;
for (int i = 0; i < marks.Length; i++)
marks[i] = ((val >> (i * 2)) & 3) % 3;
return marks;
}
set
{
if (value.Length > 8)
return;
int v = 0;
for (int i = 0; i < value.Length; i++)
v |= (value[i] % 3) << (i * 2);
MarkValue = v;
}
}
public void FixMemories()
{
@ -583,18 +103,5 @@ namespace PKHeX.Core
public override int MaxItemID => Legal.MaxItemID_8;
public override int MaxBallID => Legal.MaxBallID_8;
public override int MaxGameID => Legal.MaxGameID_8;
public bool GetRibbon(int index) => FlagUtil.GetFlag(Data, GetRibbonByte(index), index & 7);
public void SetRibbon(int index, bool value = true) => FlagUtil.SetFlag(Data, GetRibbonByte(index), index & 7, value);
public int GetRibbonByte(int index)
{
if ((uint)index >= 128)
throw new ArgumentOutOfRangeException(nameof(index));
if (index < 64)
return 0x34 + (index >> 3);
index -= 64;
return 0x40 + (index >> 3);
}
}
}
}

View file

@ -289,12 +289,13 @@ namespace PKHeX.Core
public bool VC2 => Version is >= (int)GD and <= (int)C;
public bool LGPE => Version is (int)GP or (int)GE;
public bool SWSH => Version is (int)SW or (int)SH;
public bool BDSP => Version is (int)BD or (int)SP;
public bool GO_LGPE => GO && Met_Location == Locations.GO7;
public bool GO_HOME => GO && Met_Location == Locations.GO8;
public bool VC => VC1 || VC2;
public bool GG => LGPE || GO_LGPE;
public bool Gen8 => Version is >= 44 and <= 45 || GO_HOME;
public bool Gen8 => Version is >= 44 and <= 49 || GO_HOME;
public bool Gen7 => Version is >= 30 and <= 33 || GG;
public bool Gen6 => Version is >= 24 and <= 29;
public bool Gen5 => Version is >= 20 and <= 23;
@ -486,10 +487,10 @@ namespace PKHeX.Core
}
// Misc Egg Facts
public bool WasEgg => IsEgg || Egg_Location != 0;
public bool WasEgg => IsEgg || !Locations.IsNoneLocation((GameVersion)Version, Egg_Location);
public bool WasTradedEgg => Egg_Location == GetTradedEggLocation();
public bool IsTradedEgg => Met_Location == GetTradedEggLocation();
private int GetTradedEggLocation() => Locations.TradedEggLocation(Generation);
private int GetTradedEggLocation() => Locations.TradedEggLocation(Generation, (GameVersion)Version);
public virtual bool IsUntraded => false;
public bool IsNative => Generation == Format;

View file

@ -13,6 +13,8 @@ namespace PKHeX.Core
{
if (pkm.IsEgg)
return false;
if (pkm.BDSP)
return false;
return CanHaveDynamaxLevel(pkm.Species);
}

View file

@ -0,0 +1,10 @@
namespace PKHeX.Core
{
/// <summary>
/// Exposes a friendship value with the original trainer.
/// </summary>
public interface IFixedOTFriendship
{
int OT_Friendship { get; }
}
}

View file

@ -117,11 +117,19 @@ namespace PKHeX.Core
4 => BitConverter.ToUInt16(data, 0x04) == 0 ? new PK4(data) : new BK4(data),
5 => new PK5(data),
6 => CheckPKMFormat7(new PK6(data), prefer),
8 => new PK8(data),
8 => CheckPKMFormat8(data),
_ => null,
};
}
private static PKM CheckPKMFormat8(byte[] data)
{
var ver = data[0xDE];
if (GameVersion.BDSP.Contains(ver))
return new PB8(data);
return new PK8(data);
}
/// <summary>
/// Checks if the input PK6 file is really a PK7, if so, updates the object.
/// </summary>
@ -432,6 +440,7 @@ namespace PKHeX.Core
{
1 when ver == GameVersion.BU => new PK1(true),
7 when GameVersion.Gen7b.Contains(ver) => new PB7(),
8 when GameVersion.BDSP.Contains(ver) => new PB8(),
_ => GetBlank(gen),
};

View file

@ -199,6 +199,7 @@ namespace PKHeX.Core
};
internal const string ExtensionPB7 = "pb7";
internal const string ExtensionPB8 = "pb8";
/// <summary>
/// Gets an array of valid <see cref="PKM"/> file extensions.
@ -221,6 +222,8 @@ namespace PKHeX.Core
result.Add("bk4"); // battle revolution
if (maxGeneration >= 7)
result.Add(ExtensionPB7); // let's go
if (maxGeneration >= 8)
result.Add(ExtensionPB8); // Brilliant Diamond & Shining Pearl
return result.ToArray();
}

View file

@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary>
/// <see cref="PersonalInfo"/> class with values from the <see cref="GameVersion.BDSP"/> games.
/// </summary>
public sealed class PersonalInfoBDSP : PersonalInfo
{
public const int SIZE = 0x44;
private const int CountTM = 100;
public override int HP { get => Data[0x00]; set => Data[0x00] = (byte)value; }
public override int ATK { get => Data[0x01]; set => Data[0x01] = (byte)value; }
public override int DEF { get => Data[0x02]; set => Data[0x02] = (byte)value; }
public override int SPE { get => Data[0x03]; set => Data[0x03] = (byte)value; }
public override int SPA { get => Data[0x04]; set => Data[0x04] = (byte)value; }
public override int SPD { get => Data[0x05]; set => Data[0x05] = (byte)value; }
public override int Type1 { get => Data[0x06]; set => Data[0x06] = (byte)value; }
public override int Type2 { get => Data[0x07]; set => Data[0x07] = (byte)value; }
public override int CatchRate { get => Data[0x08]; set => Data[0x08] = (byte)value; }
public override int EvoStage { get => Data[0x09]; set => Data[0x09] = (byte)value; }
private int EVYield { get => BitConverter.ToUInt16(Data, 0x0A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x0A); }
public override int EV_HP { get => EVYield >> 0 & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | (value & 0x3) << 0; }
public override int EV_ATK { get => EVYield >> 2 & 0x3; set => EVYield = (EVYield & ~(0x3 << 2)) | (value & 0x3) << 2; }
public override int EV_DEF { get => EVYield >> 4 & 0x3; set => EVYield = (EVYield & ~(0x3 << 4)) | (value & 0x3) << 4; }
public override int EV_SPE { get => EVYield >> 6 & 0x3; set => EVYield = (EVYield & ~(0x3 << 6)) | (value & 0x3) << 6; }
public override int EV_SPA { get => EVYield >> 8 & 0x3; set => EVYield = (EVYield & ~(0x3 << 8)) | (value & 0x3) << 8; }
public override int EV_SPD { get => EVYield >> 10 & 0x3; set => EVYield = (EVYield & ~(0x3 << 10)) | (value & 0x3) << 10; }
public int Item1 { get => BitConverter.ToInt16(Data, 0x0C); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0C); }
public int Item2 { get => BitConverter.ToInt16(Data, 0x0E); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x0E); }
public int Item3 { get => BitConverter.ToInt16(Data, 0x10); set => BitConverter.GetBytes((short)value).CopyTo(Data, 0x10); }
public override int Gender { get => Data[0x12]; set => Data[0x12] = (byte)value; }
public override int HatchCycles { get => Data[0x13]; set => Data[0x13] = (byte)value; }
public override int BaseFriendship { get => Data[0x14]; set => Data[0x14] = (byte)value; }
public override int EXPGrowth { get => Data[0x15]; set => Data[0x15] = (byte)value; }
public override int EggGroup1 { get => Data[0x16]; set => Data[0x16] = (byte)value; }
public override int EggGroup2 { get => Data[0x17]; set => Data[0x17] = (byte)value; }
public int Ability1 { get => BitConverter.ToUInt16(Data, 0x18); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x18); }
public int Ability2 { get => BitConverter.ToUInt16(Data, 0x1A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1A); }
public int AbilityH { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1C); }
public override int EscapeRate { get => 0; set { } } // moved?
protected internal override int FormStatsIndex { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); }
public override int FormSprite { get => 0; set { } } // No longer defined in personal
public override int FormCount { get => Data[0x20]; set => Data[0x20] = (byte)value; }
public override int Color { get => Data[0x21] & 0x3F; set => Data[0x21] = (byte)((Data[0x21] & 0xC0) | (value & 0x3F)); }
public bool IsPresentInGame { get => ((Data[0x21] >> 6) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x40) | (value ? 0x40 : 0)); }
public bool SpriteForm { get => false; set { } } // Unspecified in table
public override int BaseEXP { get => BitConverter.ToUInt16(Data, 0x22); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x22); }
public override int Height { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); }
public override int Weight { get => BitConverter.ToUInt16(Data, 0x26); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x26); }
//public uint TM1 { get => BitConverter.ToUInt32(Data, 0x28); set => BitConverter.GetBytes(value).CopyTo(Data, 0x28); }
//public uint TM2 { get => BitConverter.ToUInt32(Data, 0x2C); set => BitConverter.GetBytes(value).CopyTo(Data, 0x2C); }
//public uint TM3 { get => BitConverter.ToUInt32(Data, 0x30); set => BitConverter.GetBytes(value).CopyTo(Data, 0x30); }
//public uint TM4 { get => BitConverter.ToUInt32(Data, 0x34); set => BitConverter.GetBytes(value).CopyTo(Data, 0x34); }
//public uint Tutor { get => BitConverter.ToUInt32(Data, 0x38); set => BitConverter.GetBytes(value).CopyTo(Data, 0x38); }
public int Species { get => BitConverter.ToUInt16(Data, 0x3C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x3C); }
public int HatchSpecies { get => BitConverter.ToUInt16(Data, 0x3E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x3E); }
public int HatchFormIndex { get => BitConverter.ToUInt16(Data, 0x40); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x40); }
public int PokeDexIndex { get => BitConverter.ToUInt16(Data, 0x42); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x42); }
public PersonalInfoBDSP(byte[] data) : base(data)
{
TMHM = new bool[CountTM];
for (var i = 0; i < CountTM; i++)
TMHM[i] = FlagUtil.GetFlag(Data, 0x28 + (i >> 3), i);
// 0x38-0x3B type tutors, but only 8 bits are valid flags.
var typeTutors = new bool[8];
for (int i = 0; i < typeTutors.Length; i++)
typeTutors[i] = FlagUtil.GetFlag(Data, 0x38, i);
TypeTutors = typeTutors;
}
public override byte[] Write()
{
for (var i = 0; i < CountTM; i++)
FlagUtil.SetFlag(Data, 0x28 + (i >> 3), i, TMHM[i]);
for (int i = 0; i < TypeTutors.Length; i++)
FlagUtil.SetFlag(Data, 0x38, i, TypeTutors[i]);
return Data;
}
public override IReadOnlyList<int> Items
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Count != 3) return;
Ability1 = value[0];
Ability2 = value[1];
AbilityH = value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
/// <summary>
/// Checks if the entry shows up in any of the built-in Pokédex.
/// </summary>
public bool IsInDex => PokeDexIndex != 0;
}
}

View file

@ -12,6 +12,11 @@ namespace PKHeX.Core
/// </remarks>
public class PersonalTable
{
/// <summary>
/// Personal Table used in <see cref="GameVersion.BDSP"/>.
/// </summary>
public static readonly PersonalTable BDSP = GetTable("bdsp", GameVersion.BDSP);
/// <summary>
/// Personal Table used in <see cref="GameVersion.SWSH"/>.
/// </summary>
@ -124,7 +129,8 @@ namespace PKHeX.Core
GameVersion.ORAS => z => new PersonalInfoORAS(z),
GameVersion.SM or GameVersion.USUM => z => new PersonalInfoSM(z),
GameVersion.GG => z => new PersonalInfoGG(z),
_ => z => new PersonalInfoSWSH(z),
GameVersion.SWSH => z => new PersonalInfoSWSH(z),
_ => z => new PersonalInfoBDSP(z),
};
private static int GetEntrySize(GameVersion format) => format switch
@ -139,6 +145,7 @@ namespace PKHeX.Core
GameVersion.ORAS => PersonalInfoORAS.SIZE,
GameVersion.SM or GameVersion.USUM or GameVersion.GG => PersonalInfoSM.SIZE,
GameVersion.SWSH => PersonalInfoSWSH.SIZE,
GameVersion.BDSP => PersonalInfoBDSP.SIZE,
_ => -1,
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,8 @@
Salla
Plaupa
Pollo
Pador
Hilary
Norbert
Mara
The Master

View file

@ -0,0 +1,8 @@
Kazza
Charap
Gaspar
Foppa
Hilary
Norton
Mindy
Meister

View file

@ -0,0 +1,8 @@
Kazza
Charap
Gaspar
Foppa
Amanda
Alfio
Regla
Meister

View file

@ -0,0 +1,8 @@
Kazou
Pijouk
Trumi
Bloupi
Paula
Pierre
Marlène
Meister

View file

@ -0,0 +1,8 @@
Keikei
Charap
Gaspar
Mossy
Hilary
Norton
Mindy
The Master

View file

@ -0,0 +1,8 @@
ケーケー
ペッチャラ
ドロりん
ポッちゃん
ハッシー
ノブリン
ミナッチ
マイスター

View file

@ -0,0 +1,8 @@
케이케이
조자리
고옹이
Pador
에이
신달
미나
Master

View file

@ -0,0 +1,8 @@
凯凯
聒查拉
小无形
Pador
赫西
诺布林
米娜琦
Master

View file

@ -0,0 +1,8 @@
凱凱
小聒聒
無形仔
Pador
荷希
諾布林
美琦
Master

Binary file not shown.

View file

@ -1605,4 +1605,219 @@ Phantomkarotte
Dyna-Erz
Karottensamen
Fähigk.-Pflaster
Zügel des Bundes
Zügel des Bundes
Enigmafragment S
Enigmafragment L
Einwegbohrer
Kanto-Platte
Johto-Platte
Gefühlsplatte
Regenbogenplatte
Sturmplatte
Meeresplatte
Kontinentplatte
Himmelsplatte
Genplatte
Weisungsplatte
Zerrplatte
DS-Player

View file

@ -1605,4 +1605,219 @@ Shaderoot Carrot
Dynite Ore
Carrot Seeds
Ability Patch
Reins of Unity
Reins of Unity
Mysterious Shard S
Mysterious Shard L
Digger Drill
Kanto Slate
Johto Slate
Soul Slate
Rainbow Slate
Squall Slate
Oceanic Slate
Tectonic Slate
Stratospheric Slate
Genome Slate
Discovery Slate
Distortion Slate
DS Sounds

View file

@ -1605,4 +1605,219 @@ Zanahoria Oscura
Maxinium
Sem. Zanahoria
Parche Habilidad
Riendas Unión
Riendas Unión
Esquirla Extraña S
Esquirla Extraña L
Taladro
Losa Kanto
Losa Johto
Losa Empatía
Losa Arcoíris
Losa Tormenta
Losa Mar
Losa Tierra
Losa Cielo
Losa Genética
Losa Inicio
Losa Distorsión
Reproductor DS

View file

@ -1605,4 +1605,219 @@ Carotte Sombre
Dynamaxium
Graines Carotte
Patch Talent
Rênes de lUnité
Rênes de lUnité
Éclat Étrange S
Éclat Étrange L
Foreuse
Tablette Kanto
Tablette Johto
Tablette Cœur
Tablette Arc-en-ciel
Tablette Tempête
Tablette Océans
Tablette Continents
Tablette Cieux
Tablette Génétique
Tablette Guides
Tablette Distorsion
Lecteur DS

View file

@ -1605,4 +1605,219 @@ Carota oscura
Rocciamax
Semi di carota
Cerotto abilità
Briglie legame
Briglie legame
Frammento insolito S
Frammento insolito L
Trivella
Piastra di Kanto
Piastra di Johto
Piastra del cuore
Piastra arcobaleno
Piastra dei venti
Piastra dei mari
Piastra delle terre
Piastra dei cieli
Piastra del gene
Piastra degli esordi
Piastra distorta
Lettore DS

View file

@ -1605,4 +1605,219 @@
マックスこうせき
にんじんのタネ
とくせいパッチ
キズナのタヅナ
キズナのタヅナ
なぞのかけらS
なぞのかけらL
あなほりドリル
カントーのせきばん
ジョウトのせきばん
こころのせきばん
にじのせきばん
あらしのせきばん
たいかいのせきばん
たいりくのせきばん
てんくうのせきばん
いでんしのせきばん
みちびきのせきばん
やぶれたせきばん
DSプレイヤー

View file

@ -1605,4 +1605,219 @@ PP회복캡슐토이
맥스광석
당근씨
특성패치
유대의고삐
유대의고삐
수수께끼의조각S
수수께끼의조각L
구멍파기드릴
관동의석판
성도의석판
마음의석판
무지개의석판
폭풍의석판
대해의석판
대륙의석판
천공의석판
유전자의석판
인도의석판
깨어진석판
DS플레이어

View file

@ -1605,4 +1605,219 @@
极矿石
萝卜种子
特性膏药
牵绊缰绳
牵绊缰绳
谜之碎片S
谜之碎片L
挖洞钻
关都石板
城都石板
心之石板
彩虹石板
暴风石板
大海石板
大陆石板
天空石板
基因石板
引导石板
毁坏石板
DS播放器

View file

@ -0,0 +1,658 @@
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Jubelstadt
Fleetburg
Fleetburg
Fleetburg-Arena
Fleetburg
Fleetburg
Fleetburg
Fleetburg
Fleetburg
Fleetburg
Fleetburg
Fleetburg
Fleetburg
Fleetburg
Fleetburg
Erzelingen
Erzelingen
Erzelingen-Arena
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Erzelingen
Ewigenau
Ewigenau
Ewigenau-Arena
Ewigenau-Arena
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Ewigenau
Herzhofen
Herzhofen
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen-Arena
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Herzhofen
Weideburg
Weideburg
Weideburg-Arena
Weideburg
Weideburg
Weideburg
Weideburg
Weideburg
Weideburg
Weideburg
Weideburg
Weideburg
Weideburg
Schleiede
Schleiede-Arena
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Schleiede
Sonnewik
Sonnewik
Sonnewik-Arena
Sonnewik-Arena
Sonnewik-Arena
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Sonnewik
Blizzach
Blizzach
Blizzach-Arena
Blizzach
Blizzach
Blizzach
Blizzach
Blizzach
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Pokémon-Liga
Kampfareal
Kampfareal
Kampfareal
Kampfareal
Kampfareal
Kampfareal
Kampfareal
Kampfareal
Kampfareal
Erzelingen-Mine
Erzelingen-Mine
Windkraftwerk
Windkraftwerk
Ewigwald
Ewigwald
Feurio-Hütte
Feurio-Hütte
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Kraterberg
Speersäule
Speersäule
Halle des Beginns
Großmoor
Großmoor
Großmoor
Großmoor
Großmoor
Großmoor
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Trostu-Ruinen
Siegesstraße
Siegesstraße
Siegesstraße
Siegesstraße
Siegesstraße
Siegesstraße
Hamanasu-Park
Platz der Treue
Verwüsteter Pfad
Auen von Flori
Auen von Flori
Erzelingen-Tor
Erzelingen-Tor
Vollmondinsel
Vollmondinsel
Kahlberg
Kahlberg
Kahlberg
Kahlberg
Scheidequelle
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Höhle der Umkehr
Blumenparadies
Blizzach-Tempel
Blizzach-Tempel
Blizzach-Tempel
Blizzach-Tempel
Blizzach-Tempel
Blizzach-Tempel
Bizarre Höhle
Bizarre Höhle
Maniac-Höhle
Maniac-Höhle
Maniac-Tunnel
Ziergarten
Eiseninsel
Eiseninsel
Eiseninsel
Eiseninsel
Eiseninsel
Eiseninsel
Eiseninsel
Eiseninsel
Alte Villa
Alte Villa
Alte Villa
Alte Villa
Alte Villa
Alte Villa
Alte Villa
Alte Villa
Alte Villa
Galaktik-Zentrale
Galaktik-Zentrale
Galaktik-Zentrale
Galaktik-Zentrale
Galaktik-Zentrale
Galaktik-Zentrale
Galaktik-Zentrale
Galaktik-Zentrale
See der Wahrheit
See der Wahrheit
See der Wahrheit
See der Kühnheit
See der Kühnheit
See der Kühnheit
See der Stärke
See der Stärke
See der Stärke
Neumondinsel
Neumondinsel
Duellpark
Duellpark
Duellpark
Duellpark
Duellpark
Duellturm
Duellturm
Duellturm
Duellturm
Duellturm
Duellturm
Mysteriöser Ort
Wahrheitsufer
Kühnheitsufer
Kühnheitsufer
Kühnheitsufer
Kühnheitsufer
Stärkeufer
Quellenpfad
Mysteriöser Ort
Route 201
Route 202
Route 203
Route 204
Route 204
Route 205
Route 205
Route 205
Route 206
Route 206
Route 207
Route 208
Route 208
Route 209
Route 209
Route 209
Route 209
Route 209
Route 209
Route 210
Route 210
Route 210
Route 210
Route 211
Route 211
Route 212
Route 212
Route 212
Route 212
Route 212
Route 212
Route 213
Route 213
Route 213
Route 213
Route 213
Route 213
Route 213
Route 214
Route 214
Route 215
Route 216
Route 216
Route 217
Route 217
Route 217
Route 218
Route 218
Route 218
Route 219
Route 221
Route 221
Route 221
Route 222
Route 222
Route 222
Route 222
Route 224
Route 225
Route 225
Route 227
Route 227
Route 228
Route 228
Route 228
Route 228
Route 229
Mysteriöser Ort
Zweiblattdorf
Zweiblattdorf
Zweiblattdorf
Zweiblattdorf
Zweiblattdorf
Zweiblattdorf
Zweiblattdorf
Sandgemme
Sandgemme
Sandgemme
Sandgemme
Sandgemme
Sandgemme
Sandgemme
Sandgemme
Sandgemme
Flori
Flori
Flori
Flori
Flori
Flori
Flori
Flori
Trostu
Trostu
Trostu
Trostu
Trostu
Trostu
Trostu
Trostu
Trostu
Trostu
Elyses
Elyses
Elyses
Elyses
Elyses
Elyses
Elyses
Elyses
Elyses
Überlebensareal
Überlebensareal
Überlebensareal
Überlebensareal
Überlebensareal
Überlebensareal
Überlebensareal
Überlebensareal
Erholungsareal
Erholungsareal
Erholungsareal
Erholungsareal
Erholungsareal
Erholungsareal
Erholungsareal
Erholungsareal
Erholungsareal
Erholungsareal
Mysteriöser Ort
Mysteriöser Ort
Route 220
Route 223
Route 226
Route 226
Route 230
Buhnenpfad
Mysteriöser Ort
Mysteriöser Ort
Mysteriöser Ort
Ewigenau
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Hamanasu-Park
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Untergrundhöhlen
Hamanasu-Park
Hamanasu-Park
Jubelstadt
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park
Hamanasu-Park

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