mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-10 06:34:19 +00:00
Add new abstractions for pkm/personal/mysterygift
Co-Authored-By: SciresM <8676005+SciresM@users.noreply.github.com>
This commit is contained in:
parent
e577f18013
commit
429c80e9f5
10 changed files with 1960 additions and 2 deletions
773
PKHeX.Core/MysteryGifts/WA8.cs
Normal file
773
PKHeX.Core/MysteryGifts/WA8.cs
Normal file
|
@ -0,0 +1,773 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static PKHeX.Core.RibbonIndex;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Generation 8 Mystery Gift Template File, same as <see cref="WC8"/> with <see cref="IGanbaru"/> fields at the end.
|
||||
/// </summary>
|
||||
public sealed class WA8 : DataMysteryGift, ILangNick, INature, IGigantamax, IDynamaxLevel, IRibbonIndex, IMemoryOT, ILangNicknamedTemplate, IGanbaru,
|
||||
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8
|
||||
{
|
||||
public const int Size = 0x2C8;
|
||||
|
||||
public override int Generation => 8;
|
||||
|
||||
public enum GiftType : byte
|
||||
{
|
||||
None = 0,
|
||||
Pokemon = 1,
|
||||
Item = 2,
|
||||
}
|
||||
|
||||
public WA8() : this(new byte[Size]) { }
|
||||
public WA8(byte[] data) : base(data) { }
|
||||
|
||||
public bool CanBeReceivedByVersion(int v) => v is (int) GameVersion.PLA;
|
||||
|
||||
// General Card Properties
|
||||
public override int CardID
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x8));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x8), (ushort)value);
|
||||
}
|
||||
|
||||
public byte CardFlags { get => Data[0x10]; set => Data[0x10] = value; }
|
||||
public GiftType CardType { get => (GiftType)Data[0x11]; set => Data[0x11] = (byte)value; }
|
||||
public bool GiftRepeatable { get => (CardFlags & 1) == 0; set => CardFlags = (byte)((CardFlags & ~1) | (value ? 0 : 1)); }
|
||||
public override bool GiftUsed { get => false; set { } }
|
||||
|
||||
public int CardTitleIndex
|
||||
{
|
||||
get => Data[0x13];
|
||||
set => Data[0x13] = (byte) value;
|
||||
}
|
||||
|
||||
public override string CardTitle
|
||||
{
|
||||
get => "Mystery Gift"; // TODO: Use text string from CardTitleIndex
|
||||
set => throw new Exception();
|
||||
}
|
||||
|
||||
// Item Properties
|
||||
public override bool IsItem { get => CardType == GiftType.Item; set { if (value) CardType = GiftType.Item; } }
|
||||
|
||||
public override int ItemID
|
||||
{
|
||||
get => GetItem(0);
|
||||
set => SetItem(0, (ushort)value);
|
||||
}
|
||||
|
||||
public override int Quantity
|
||||
{
|
||||
get => GetQuantity(0);
|
||||
set => SetQuantity(0, (ushort)value);
|
||||
}
|
||||
|
||||
public int GetItem(int index) => ReadUInt16LittleEndian(Data.AsSpan(0x18 + (0x4 * index)));
|
||||
public void SetItem(int index, ushort item) => WriteUInt16LittleEndian(Data.AsSpan(0x18 + (4 * index)), item);
|
||||
public int GetQuantity(int index) => ReadUInt16LittleEndian(Data.AsSpan(0x1A + (0x4 * index)));
|
||||
public void SetQuantity(int index, ushort quantity) => WriteUInt16LittleEndian(Data.AsSpan(0x1A + (4 * index)), quantity);
|
||||
|
||||
// Pokémon Properties
|
||||
public override bool IsPokémon { get => CardType == GiftType.Pokemon; set { if (value) CardType = GiftType.Pokemon; } }
|
||||
|
||||
public override bool IsShiny => Shiny.IsShiny();
|
||||
|
||||
public override Shiny Shiny
|
||||
{
|
||||
get
|
||||
{
|
||||
var type = PIDType;
|
||||
if (type is not Shiny.FixedValue)
|
||||
return type;
|
||||
return GetShinyXor() switch
|
||||
{
|
||||
0 => Shiny.AlwaysSquare,
|
||||
<= 15 => Shiny.AlwaysStar,
|
||||
_ => Shiny.Never,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private int GetShinyXor()
|
||||
{
|
||||
// Player owned anti-shiny fixed PID
|
||||
if (TID == 0 && SID == 0)
|
||||
return int.MaxValue;
|
||||
|
||||
var pid = PID;
|
||||
var psv = (int)(pid >> 16 ^ (pid & 0xFFFF));
|
||||
var tsv = (TID ^ SID);
|
||||
return psv ^ tsv;
|
||||
}
|
||||
|
||||
public override int TID
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x18));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value);
|
||||
}
|
||||
|
||||
public override int SID {
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x1A));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x1A), (ushort)value);
|
||||
}
|
||||
|
||||
public int OriginGame
|
||||
{
|
||||
get => ReadInt32LittleEndian(Data.AsSpan(0x1C));
|
||||
set => WriteInt32LittleEndian(Data.AsSpan(0x1C), value);
|
||||
}
|
||||
|
||||
public uint EncryptionConstant
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data.AsSpan(0x20));
|
||||
set => WriteUInt32LittleEndian(Data.AsSpan(0x20), value);
|
||||
}
|
||||
|
||||
public uint PID
|
||||
{
|
||||
get => ReadUInt32LittleEndian(Data.AsSpan(0x24));
|
||||
set => WriteUInt32LittleEndian(Data.AsSpan(0x24), value);
|
||||
}
|
||||
|
||||
// Nicknames, OT Names 0x30 - 0x228
|
||||
public override int EggLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x220)); set => WriteUInt16LittleEndian(Data.AsSpan(0x220), (ushort)value); }
|
||||
public int MetLocation { get => ReadUInt16LittleEndian(Data.AsSpan(0x222)); set => WriteUInt16LittleEndian(Data.AsSpan(0x222), (ushort)value); }
|
||||
|
||||
public override int Ball
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x224));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x224), (ushort)value);
|
||||
}
|
||||
|
||||
public override int HeldItem
|
||||
{
|
||||
get => ReadUInt16LittleEndian(Data.AsSpan(0x226));
|
||||
set => WriteUInt16LittleEndian(Data.AsSpan(0x226), (ushort)value);
|
||||
}
|
||||
|
||||
public int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x228)); set => WriteUInt16LittleEndian(Data.AsSpan(0x228), (ushort)value); }
|
||||
public int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x22A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22A), (ushort)value); }
|
||||
public int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x22C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22C), (ushort)value); }
|
||||
public int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x22E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22E), (ushort)value); }
|
||||
public int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x230)); set => WriteUInt16LittleEndian(Data.AsSpan(0x230), (ushort)value); }
|
||||
public int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x232)); set => WriteUInt16LittleEndian(Data.AsSpan(0x232), (ushort)value); }
|
||||
public int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x234)); set => WriteUInt16LittleEndian(Data.AsSpan(0x234), (ushort)value); }
|
||||
public int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x236)); set => WriteUInt16LittleEndian(Data.AsSpan(0x236), (ushort)value); }
|
||||
|
||||
public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x238)); set => WriteUInt16LittleEndian(Data.AsSpan(0x238), (ushort)value); }
|
||||
public override int Form { get => Data[0x23A]; set => Data[0x23A] = (byte)value; }
|
||||
public override int Gender { get => Data[0x23B]; set => Data[0x23B] = (byte)value; }
|
||||
public override int Level { get => Data[0x23C]; set => Data[0x23C] = (byte)value; }
|
||||
public override bool IsEgg { get => Data[0x23D] == 1; set => Data[0x23D] = value ? (byte)1 : (byte)0; }
|
||||
public int Nature { get => (sbyte)Data[0x23E]; set => Data[0x23E] = (byte)value; }
|
||||
public override int AbilityType { get => Data[0x23F]; set => Data[0x23F] = (byte)value; }
|
||||
|
||||
private byte PIDTypeValue => Data[0x240];
|
||||
|
||||
public Shiny PIDType => PIDTypeValue switch
|
||||
{
|
||||
0 => Shiny.Never,
|
||||
1 => Shiny.Random,
|
||||
2 => Shiny.AlwaysStar,
|
||||
3 => Shiny.AlwaysSquare,
|
||||
4 => Shiny.FixedValue,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(PIDType)),
|
||||
};
|
||||
|
||||
public int MetLevel { get => Data[0x241]; set => Data[0x241] = (byte)value; }
|
||||
public byte DynamaxLevel { get => Data[0x242]; set => Data[0x242] = value; }
|
||||
public bool CanGigantamax { get => Data[0x243] != 0; set => Data[0x243] = value ? (byte)1 : (byte)0; }
|
||||
|
||||
// Ribbons 0x24C-0x26C
|
||||
private const int RibbonBytesOffset = 0x244;
|
||||
private const int RibbonBytesCount = 0x20;
|
||||
private const int RibbonByteNone = 0xFF; // signed -1
|
||||
|
||||
public bool HasMark()
|
||||
{
|
||||
for (int i = 0; i < RibbonBytesCount; i++)
|
||||
{
|
||||
var value = Data[RibbonBytesOffset + i];
|
||||
if (value == RibbonByteNone)
|
||||
return false;
|
||||
if ((RibbonIndex)value is >= MarkLunchtime and <= MarkSlump)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public byte GetRibbonAtIndex(int byteIndex)
|
||||
{
|
||||
if ((uint)byteIndex >= RibbonBytesCount)
|
||||
throw new IndexOutOfRangeException();
|
||||
return Data[RibbonBytesOffset + byteIndex];
|
||||
}
|
||||
|
||||
public void SetRibbonAtIndex(int byteIndex, byte ribbonIndex)
|
||||
{
|
||||
if ((uint)byteIndex >= RibbonBytesCount)
|
||||
throw new IndexOutOfRangeException();
|
||||
Data[RibbonBytesOffset + byteIndex] = ribbonIndex;
|
||||
}
|
||||
|
||||
public int IV_HP { get => Data[0x264]; set => Data[0x264] = (byte)value; }
|
||||
public int IV_ATK { get => Data[0x265]; set => Data[0x265] = (byte)value; }
|
||||
public int IV_DEF { get => Data[0x266]; set => Data[0x266] = (byte)value; }
|
||||
public int IV_SPE { get => Data[0x267]; set => Data[0x267] = (byte)value; }
|
||||
public int IV_SPA { get => Data[0x268]; set => Data[0x268] = (byte)value; }
|
||||
public int IV_SPD { get => Data[0x269]; set => Data[0x269] = (byte)value; }
|
||||
|
||||
public int OTGender { get => Data[0x26A]; set => Data[0x26A] = (byte)value; }
|
||||
|
||||
public int EV_HP { get => Data[0x26B]; set => Data[0x26B] = (byte)value; }
|
||||
public int EV_ATK { get => Data[0x26C]; set => Data[0x26C] = (byte)value; }
|
||||
public int EV_DEF { get => Data[0x26D]; set => Data[0x26D] = (byte)value; }
|
||||
public int EV_SPE { get => Data[0x26E]; set => Data[0x26E] = (byte)value; }
|
||||
public int EV_SPA { get => Data[0x26F]; set => Data[0x26F] = (byte)value; }
|
||||
public int EV_SPD { get => Data[0x270]; set => Data[0x270] = (byte)value; }
|
||||
|
||||
public int OT_Intensity { get => Data[0x271]; set => Data[0x271] = (byte)value; }
|
||||
public int OT_Memory { get => Data[0x272]; set => Data[0x272] = (byte)value; }
|
||||
public int OT_Feeling { get => Data[0x273]; set => Data[0x273] = (byte)value; }
|
||||
public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0x274)); set => WriteUInt16LittleEndian(Data.AsSpan(0x274), (ushort)value); }
|
||||
|
||||
// Only derivations to WC8
|
||||
public int GV_HP { get => Data[0x27E]; set => Data[0x27E] = (byte)value; }
|
||||
public int GV_ATK { get => Data[0x27F]; set => Data[0x27F] = (byte)value; }
|
||||
public int GV_DEF { get => Data[0x280]; set => Data[0x280] = (byte)value; }
|
||||
public int GV_SPE { get => Data[0x281]; set => Data[0x281] = (byte)value; }
|
||||
public int GV_SPA { get => Data[0x282]; set => Data[0x282] = (byte)value; }
|
||||
public int GV_SPD { get => Data[0x283]; set => Data[0x283] = (byte)value; }
|
||||
|
||||
// Meta Accessible Properties
|
||||
public override int[] IVs
|
||||
{
|
||||
get => new[] { IV_HP, IV_ATK, IV_DEF, IV_SPE, IV_SPA, IV_SPD };
|
||||
set
|
||||
{
|
||||
if (value.Length != 6) return;
|
||||
IV_HP = value[0]; IV_ATK = value[1]; IV_DEF = value[2];
|
||||
IV_SPE = value[3]; IV_SPA = value[4]; IV_SPD = value[5];
|
||||
}
|
||||
}
|
||||
|
||||
public int[] EVs
|
||||
{
|
||||
get => new[] { EV_HP, EV_ATK, EV_DEF, EV_SPE, EV_SPA, EV_SPD };
|
||||
set
|
||||
{
|
||||
if (value.Length != 6) return;
|
||||
EV_HP = value[0]; EV_ATK = value[1]; EV_DEF = value[2];
|
||||
EV_SPE = value[3]; EV_SPA = value[4]; EV_SPD = value[5];
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetIsNicknamed(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetNicknameOffset(language))) != 0;
|
||||
|
||||
public bool CanBeAnyLanguage()
|
||||
{
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
var ofs = GetLanguageOffset(i);
|
||||
var lang = ReadInt16LittleEndian(Data.AsSpan(ofs));
|
||||
if (lang != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanHaveLanguage(int language)
|
||||
{
|
||||
if (language is < (int)LanguageID.Japanese or > (int)LanguageID.ChineseT)
|
||||
return false;
|
||||
|
||||
if (CanBeAnyLanguage())
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
{
|
||||
var ofs = GetLanguageOffset(i);
|
||||
var lang = ReadInt16LittleEndian(Data.AsSpan(ofs));
|
||||
if (lang == language)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetLanguage(int redeemLanguage) => Data[GetLanguageOffset(GetLanguageIndex(redeemLanguage))];
|
||||
private static int GetLanguageOffset(int index) => 0x28 + (index * 0x1C) + 0x1A;
|
||||
|
||||
public bool GetHasOT(int language) => ReadUInt16LittleEndian(Data.AsSpan(GetOTOffset(language))) != 0;
|
||||
|
||||
private static int GetLanguageIndex(int language)
|
||||
{
|
||||
var lang = (LanguageID) language;
|
||||
if (lang is < LanguageID.Japanese or LanguageID.UNUSED_6 or > LanguageID.ChineseT)
|
||||
return (int) LanguageID.English; // fallback
|
||||
return lang < LanguageID.UNUSED_6 ? language - 1 : language - 2;
|
||||
}
|
||||
|
||||
public override int Location { get => MetLocation; set => MetLocation = (ushort)value; }
|
||||
|
||||
public override IReadOnlyList<int> Moves
|
||||
{
|
||||
get => new[] { Move1, Move2, Move3, Move4 };
|
||||
set
|
||||
{
|
||||
if (value.Count > 0) Move1 = value[0];
|
||||
if (value.Count > 1) Move2 = value[1];
|
||||
if (value.Count > 2) Move3 = value[2];
|
||||
if (value.Count > 3) Move4 = value[3];
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadOnlyList<int> Relearn
|
||||
{
|
||||
get => new[] { RelearnMove1, RelearnMove2, RelearnMove3, RelearnMove4 };
|
||||
set
|
||||
{
|
||||
if (value.Count > 0) RelearnMove1 = value[0];
|
||||
if (value.Count > 1) RelearnMove2 = value[1];
|
||||
if (value.Count > 2) RelearnMove3 = value[2];
|
||||
if (value.Count > 3) RelearnMove4 = value[3];
|
||||
}
|
||||
}
|
||||
|
||||
public override string OT_Name { get; set; } = string.Empty;
|
||||
public string Nickname => string.Empty;
|
||||
public bool IsNicknamed => false;
|
||||
public int Language => 2;
|
||||
|
||||
public string GetNickname(int language) => StringConverter8.GetString(Data.AsSpan(GetNicknameOffset(language), 0x1A));
|
||||
public void SetNickname(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetNicknameOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero);
|
||||
|
||||
public string GetOT(int language) => StringConverter8.GetString(Data.AsSpan(GetOTOffset(language), 0x1A));
|
||||
public void SetOT(int language, string value) => StringConverter8.SetString(Data.AsSpan(GetOTOffset(language), 0x1A), value.AsSpan(), 12, StringConverterOption.ClearZero);
|
||||
|
||||
private static int GetNicknameOffset(int language)
|
||||
{
|
||||
int index = GetLanguageIndex(language);
|
||||
return 0x28 + (index * 0x1C);
|
||||
}
|
||||
|
||||
private static int GetOTOffset(int language)
|
||||
{
|
||||
int index = GetLanguageIndex(language);
|
||||
return 0x124 + (index * 0x1C);
|
||||
}
|
||||
|
||||
public bool CanHandleOT(int language) => !GetHasOT(language);
|
||||
|
||||
public override GameVersion Version
|
||||
{
|
||||
get => OriginGame != 0 ? (GameVersion)OriginGame : GameVersion.PLA;
|
||||
set { }
|
||||
}
|
||||
|
||||
public override PKM ConvertToPKM(ITrainerInfo sav, EncounterCriteria criteria)
|
||||
{
|
||||
if (!IsPokémon)
|
||||
throw new ArgumentException(nameof(IsPokémon));
|
||||
|
||||
int currentLevel = Level > 0 ? Level : (1 + Util.Rand.Next(100));
|
||||
int metLevel = MetLevel > 0 ? MetLevel : currentLevel;
|
||||
var pi = PersonalTable.LA.GetFormEntry(Species, Form);
|
||||
var language = sav.Language;
|
||||
var OT = GetOT(language);
|
||||
bool hasOT = GetHasOT(language);
|
||||
|
||||
var pk = new PA8
|
||||
{
|
||||
EncryptionConstant = EncryptionConstant != 0 ? EncryptionConstant : Util.Rand32(),
|
||||
TID = TID,
|
||||
SID = SID,
|
||||
Species = Species,
|
||||
Form = Form,
|
||||
CurrentLevel = currentLevel,
|
||||
Ball = Ball != 0 ? Ball : 4, // Default is Pokeball
|
||||
Met_Level = metLevel,
|
||||
HeldItem = HeldItem,
|
||||
|
||||
EXP = Experience.GetEXP(currentLevel, pi.EXPGrowth),
|
||||
|
||||
Move1 = Move1,
|
||||
Move2 = Move2,
|
||||
Move3 = Move3,
|
||||
Move4 = Move4,
|
||||
RelearnMove1 = RelearnMove1,
|
||||
RelearnMove2 = RelearnMove2,
|
||||
RelearnMove3 = RelearnMove3,
|
||||
RelearnMove4 = RelearnMove4,
|
||||
|
||||
Version = OriginGame != 0 ? OriginGame : sav.Game,
|
||||
|
||||
OT_Name = OT.Length > 0 ? OT : sav.OT,
|
||||
OT_Gender = OTGender < 2 ? OTGender : sav.Gender,
|
||||
HT_Name = hasOT ? sav.OT : string.Empty,
|
||||
HT_Gender = hasOT ? sav.Gender : 0,
|
||||
HT_Language = hasOT ? language : 0,
|
||||
CurrentHandler = hasOT ? 1 : 0,
|
||||
OT_Friendship = pi.BaseFriendship,
|
||||
|
||||
OT_Intensity = OT_Intensity,
|
||||
OT_Memory = OT_Memory,
|
||||
OT_TextVar = OT_TextVar,
|
||||
OT_Feeling = OT_Feeling,
|
||||
FatefulEncounter = true,
|
||||
|
||||
EVs = EVs,
|
||||
|
||||
CanGigantamax = CanGigantamax,
|
||||
DynamaxLevel = DynamaxLevel,
|
||||
|
||||
Met_Location = MetLocation,
|
||||
Egg_Location = EggLocation,
|
||||
};
|
||||
pk.SetMaximumPPCurrent();
|
||||
|
||||
if ((sav.Generation > Generation && OriginGame == 0) || !CanBeReceivedByVersion(pk.Version))
|
||||
pk.Version = (int)GameVersion.PLA;
|
||||
|
||||
if (OTGender >= 2)
|
||||
{
|
||||
pk.TID = sav.TID;
|
||||
pk.SID = sav.SID;
|
||||
}
|
||||
|
||||
// Official code explicitly corrects for Meowstic
|
||||
if (pk.Species == (int)Core.Species.Meowstic)
|
||||
pk.Form = pk.Gender;
|
||||
|
||||
pk.MetDate = DateTime.Now;
|
||||
|
||||
var nickname_language = GetLanguage(language);
|
||||
pk.Language = nickname_language != 0 ? nickname_language : sav.Language;
|
||||
pk.IsNicknamed = GetIsNicknamed(language);
|
||||
pk.Nickname = pk.IsNicknamed ? GetNickname(language) : SpeciesName.GetSpeciesNameGeneration(Species, pk.Language, Generation);
|
||||
|
||||
for (var i = 0; i < RibbonBytesCount; i++)
|
||||
{
|
||||
var ribbon = GetRibbonAtIndex(i);
|
||||
if (ribbon != RibbonByteNone)
|
||||
pk.SetRibbon(ribbon);
|
||||
}
|
||||
|
||||
SetPINGA(pk, criteria);
|
||||
|
||||
if (IsEgg)
|
||||
SetEggMetData(pk);
|
||||
pk.CurrentFriendship = pk.IsEgg ? pi.HatchCycles : pi.BaseFriendship;
|
||||
|
||||
{
|
||||
pk.HeightScalar = PokeSizeUtil.GetRandomScalar();
|
||||
pk.WeightScalar = PokeSizeUtil.GetRandomScalar();
|
||||
pk.HeightScalarCopy = pk.HeightScalar;
|
||||
pk.ResetHeight();
|
||||
pk.ResetWeight();
|
||||
}
|
||||
|
||||
pk.ResetPartyStats();
|
||||
pk.RefreshChecksum();
|
||||
return pk;
|
||||
}
|
||||
|
||||
private void SetEggMetData(PKM pk)
|
||||
{
|
||||
pk.IsEgg = true;
|
||||
pk.EggMetDate = DateTime.Now;
|
||||
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(0, pk.Language, Generation);
|
||||
pk.IsNicknamed = true;
|
||||
}
|
||||
|
||||
private void SetPINGA(PKM pk, EncounterCriteria criteria)
|
||||
{
|
||||
var pi = PersonalTable.LA.GetFormEntry(Species, Form);
|
||||
pk.Nature = (int)criteria.GetNature(Nature == -1 ? Core.Nature.Random : (Nature)Nature);
|
||||
pk.StatNature = pk.Nature;
|
||||
pk.Gender = criteria.GetGender(Gender, pi);
|
||||
var av = GetAbilityIndex(criteria);
|
||||
pk.RefreshAbility(av);
|
||||
SetPID(pk);
|
||||
SetIVs(pk);
|
||||
}
|
||||
|
||||
private int GetAbilityIndex(EncounterCriteria criteria) => AbilityType switch
|
||||
{
|
||||
00 or 01 or 02 => AbilityType, // Fixed 0/1/2
|
||||
03 or 04 => criteria.GetAbilityFromNumber(Ability), // 0/1 or 0/1/H
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(AbilityType)),
|
||||
};
|
||||
|
||||
public override AbilityPermission Ability => AbilityType switch
|
||||
{
|
||||
0 => AbilityPermission.OnlyFirst,
|
||||
1 => AbilityPermission.OnlySecond,
|
||||
2 => AbilityPermission.OnlyHidden,
|
||||
3 => AbilityPermission.Any12,
|
||||
_ => AbilityPermission.Any12H,
|
||||
};
|
||||
|
||||
private uint GetPID(ITrainerID tr, byte type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
0 => GetAntishiny(tr), // Random, Never Shiny
|
||||
1 => Util.Rand32(), // Random, Any
|
||||
2 => (uint) (((tr.TID ^ tr.SID ^ (PID & 0xFFFF) ^ 1) << 16) | (PID & 0xFFFF)), // Fixed, Force Star
|
||||
3 => (uint) (((tr.TID ^ tr.SID ^ (PID & 0xFFFF) ^ 0) << 16) | (PID & 0xFFFF)), // Fixed, Force Square
|
||||
4 => PID, // Fixed, Force Value
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type)),
|
||||
};
|
||||
|
||||
static uint GetAntishiny(ITrainerID tr)
|
||||
{
|
||||
var pid = Util.Rand32();
|
||||
if (tr.IsShiny(pid, 8))
|
||||
return pid ^ 0x1000_0000;
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPID(PKM pk)
|
||||
{
|
||||
pk.PID = GetPID(pk, PIDTypeValue);
|
||||
}
|
||||
|
||||
private void SetIVs(PKM pk)
|
||||
{
|
||||
Span<int> finalIVs = stackalloc int[6];
|
||||
var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3);
|
||||
var rng = Util.Rand;
|
||||
if (ivflag == 0) // Random IVs
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
finalIVs[i] = IVs[i] > 31 ? rng.Next(32) : IVs[i];
|
||||
}
|
||||
else // 1/2/3 perfect IVs
|
||||
{
|
||||
int IVCount = ivflag - 0xFB;
|
||||
do { finalIVs[rng.Next(6)] = 31; }
|
||||
while (finalIVs.Count(31) < IVCount);
|
||||
for (int i = 0; i < 6; i++)
|
||||
finalIVs[i] = finalIVs[i] == 31 ? 31 : rng.Next(32);
|
||||
}
|
||||
pk.SetIVs(finalIVs);
|
||||
}
|
||||
|
||||
public override bool IsMatchExact(PKM pkm, DexLevel evo)
|
||||
{
|
||||
if (pkm.Egg_Location == 0) // Not Egg
|
||||
{
|
||||
if (OTGender < 2)
|
||||
{
|
||||
if (SID != pkm.SID) return false;
|
||||
if (TID != pkm.TID) return false;
|
||||
if (OTGender != pkm.OT_Gender) return false;
|
||||
}
|
||||
|
||||
if (!CanBeAnyLanguage() && !CanHaveLanguage(pkm.Language))
|
||||
return false;
|
||||
|
||||
var OT = GetOT(pkm.Language); // May not be guaranteed to work.
|
||||
if (!string.IsNullOrEmpty(OT) && OT != pkm.OT_Name) return false;
|
||||
if (OriginGame != 0 && OriginGame != pkm.Version) return false;
|
||||
if (EncryptionConstant != 0)
|
||||
{
|
||||
if (EncryptionConstant != pkm.EncryptionConstant)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Form != evo.Form && !FormInfo.IsFormChangeable(Species, Form, pkm.Form, pkm.Format))
|
||||
return false;
|
||||
|
||||
if (IsEgg)
|
||||
{
|
||||
if (EggLocation != pkm.Egg_Location) // traded
|
||||
{
|
||||
if (pkm.Egg_Location != Locations.LinkTrade6)
|
||||
return false;
|
||||
if (PIDType == Shiny.Random && pkm.IsShiny && pkm.ShinyXor > 1)
|
||||
return false; // shiny traded egg will always have xor0/1.
|
||||
}
|
||||
if (!PIDType.IsValid(pkm))
|
||||
{
|
||||
return false; // can't be traded away for unshiny
|
||||
}
|
||||
|
||||
if (pkm.IsEgg && !pkm.IsNative)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!PIDType.IsValid(pkm)) return false;
|
||||
if (EggLocation != pkm.Egg_Location) return false;
|
||||
if (MetLocation != pkm.Met_Location) return false;
|
||||
}
|
||||
|
||||
if (MetLevel != 0 && MetLevel != pkm.Met_Level) return false;
|
||||
if (OTGender < 2 && OTGender != pkm.OT_Gender) return false;
|
||||
if (Nature != -1 && pkm.Nature != Nature) return false;
|
||||
if (Gender != 3 && Gender != pkm.Gender) return false;
|
||||
|
||||
const int poke = (int)Core.Ball.LAPoke;
|
||||
var expectedBall = (Ball == 0 ? poke : Ball);
|
||||
if (expectedBall < poke) // Not even Cherish balls are safe! They get set to the proto-Poké ball.
|
||||
expectedBall = poke;
|
||||
if (expectedBall != pkm.Ball)
|
||||
return false;
|
||||
|
||||
if (pkm is IGigantamax g && g.CanGigantamax != CanGigantamax && !g.CanToggleGigantamax(pkm.Species, pkm.Form, Species, Form))
|
||||
return false;
|
||||
|
||||
if (pkm is not IDynamaxLevel dl || dl.DynamaxLevel < DynamaxLevel)
|
||||
return false;
|
||||
|
||||
// PID Types 0 and 1 do not use the fixed PID value.
|
||||
// Values 2,3 are specific shiny states, and 4 is fixed value.
|
||||
// 2,3,4 can change if it is a traded egg to ensure the same shiny state.
|
||||
var type = PIDTypeValue;
|
||||
if (type <= 1)
|
||||
return true;
|
||||
return pkm.PID == GetPID(pkm, type);
|
||||
}
|
||||
|
||||
protected override bool IsMatchDeferred(PKM pkm) => Species != pkm.Species;
|
||||
protected override bool IsMatchPartial(PKM pkm) => false; // no version compatibility checks yet.
|
||||
|
||||
#region Lazy Ribbon Implementation
|
||||
public bool RibbonEarth { get => this.GetRibbonIndex(Earth); set => this.SetRibbonIndex(Earth, value); }
|
||||
public bool RibbonNational { get => this.GetRibbonIndex(National); set => this.SetRibbonIndex(National, value); }
|
||||
public bool RibbonCountry { get => this.GetRibbonIndex(Country); set => this.SetRibbonIndex(Country, value); }
|
||||
public bool RibbonChampionBattle { get => this.GetRibbonIndex(ChampionBattle); set => this.SetRibbonIndex(ChampionBattle, value); }
|
||||
public bool RibbonChampionRegional { get => this.GetRibbonIndex(ChampionRegional); set => this.SetRibbonIndex(ChampionRegional, value); }
|
||||
public bool RibbonChampionNational { get => this.GetRibbonIndex(ChampionNational); set => this.SetRibbonIndex(ChampionNational, value); }
|
||||
public bool RibbonClassic { get => this.GetRibbonIndex(Classic); set => this.SetRibbonIndex(Classic, value); }
|
||||
public bool RibbonWishing { get => this.GetRibbonIndex(Wishing); set => this.SetRibbonIndex(Wishing, value); }
|
||||
public bool RibbonPremier { get => this.GetRibbonIndex(Premier); set => this.SetRibbonIndex(Premier, value); }
|
||||
public bool RibbonEvent { get => this.GetRibbonIndex(Event); set => this.SetRibbonIndex(Event, value); }
|
||||
public bool RibbonBirthday { get => this.GetRibbonIndex(Birthday); set => this.SetRibbonIndex(Birthday, value); }
|
||||
public bool RibbonSpecial { get => this.GetRibbonIndex(Special); set => this.SetRibbonIndex(Special, value); }
|
||||
public bool RibbonWorld { get => this.GetRibbonIndex(World); set => this.SetRibbonIndex(World, value); }
|
||||
public bool RibbonChampionWorld { get => this.GetRibbonIndex(ChampionWorld); set => this.SetRibbonIndex(ChampionWorld, value); }
|
||||
public bool RibbonSouvenir { get => this.GetRibbonIndex(Souvenir); set => this.SetRibbonIndex(Souvenir, value); }
|
||||
public bool RibbonChampionG3 { get => this.GetRibbonIndex(ChampionG3); set => this.SetRibbonIndex(ChampionG3, value); }
|
||||
public bool RibbonArtist { get => this.GetRibbonIndex(Artist); set => this.SetRibbonIndex(Artist, value); }
|
||||
public bool RibbonEffort { get => this.GetRibbonIndex(Effort); set => this.SetRibbonIndex(Effort, value); }
|
||||
public bool RibbonChampionSinnoh { get => this.GetRibbonIndex(ChampionSinnoh); set => this.SetRibbonIndex(ChampionSinnoh, value); }
|
||||
public bool RibbonAlert { get => this.GetRibbonIndex(Alert); set => this.SetRibbonIndex(Alert, value); }
|
||||
public bool RibbonShock { get => this.GetRibbonIndex(Shock); set => this.SetRibbonIndex(Shock, value); }
|
||||
public bool RibbonDowncast { get => this.GetRibbonIndex(Downcast); set => this.SetRibbonIndex(Downcast, value); }
|
||||
public bool RibbonCareless { get => this.GetRibbonIndex(Careless); set => this.SetRibbonIndex(Careless, value); }
|
||||
public bool RibbonRelax { get => this.GetRibbonIndex(Relax); set => this.SetRibbonIndex(Relax, value); }
|
||||
public bool RibbonSnooze { get => this.GetRibbonIndex(Snooze); set => this.SetRibbonIndex(Snooze, value); }
|
||||
public bool RibbonSmile { get => this.GetRibbonIndex(Smile); set => this.SetRibbonIndex(Smile, value); }
|
||||
public bool RibbonGorgeous { get => this.GetRibbonIndex(Gorgeous); set => this.SetRibbonIndex(Gorgeous, value); }
|
||||
public bool RibbonRoyal { get => this.GetRibbonIndex(Royal); set => this.SetRibbonIndex(Royal, value); }
|
||||
public bool RibbonGorgeousRoyal { get => this.GetRibbonIndex(GorgeousRoyal); set => this.SetRibbonIndex(GorgeousRoyal, value); }
|
||||
public bool RibbonFootprint { get => this.GetRibbonIndex(Footprint); set => this.SetRibbonIndex(Footprint, value); }
|
||||
public bool RibbonRecord { get => this.GetRibbonIndex(Record); set => this.SetRibbonIndex(Record, value); }
|
||||
public bool RibbonLegend { get => this.GetRibbonIndex(Legend); set => this.SetRibbonIndex(Legend, value); }
|
||||
public bool RibbonChampionKalos { get => this.GetRibbonIndex(ChampionKalos); set => this.SetRibbonIndex(ChampionKalos, value); }
|
||||
public bool RibbonChampionG6Hoenn { get => this.GetRibbonIndex(ChampionG6Hoenn); set => this.SetRibbonIndex(ChampionG6Hoenn, value); }
|
||||
public bool RibbonBestFriends { get => this.GetRibbonIndex(BestFriends); set => this.SetRibbonIndex(BestFriends, value); }
|
||||
public bool RibbonTraining { get => this.GetRibbonIndex(Training); set => this.SetRibbonIndex(Training, value); }
|
||||
public bool RibbonBattlerSkillful { get => this.GetRibbonIndex(BattlerSkillful); set => this.SetRibbonIndex(BattlerSkillful, value); }
|
||||
public bool RibbonBattlerExpert { get => this.GetRibbonIndex(BattlerExpert); set => this.SetRibbonIndex(BattlerExpert, value); }
|
||||
public bool RibbonContestStar { get => this.GetRibbonIndex(ContestStar); set => this.SetRibbonIndex(ContestStar, value); }
|
||||
public bool RibbonMasterCoolness { get => this.GetRibbonIndex(MasterCoolness); set => this.SetRibbonIndex(MasterCoolness, value); }
|
||||
public bool RibbonMasterBeauty { get => this.GetRibbonIndex(MasterBeauty); set => this.SetRibbonIndex(MasterBeauty, value); }
|
||||
public bool RibbonMasterCuteness { get => this.GetRibbonIndex(MasterCuteness); set => this.SetRibbonIndex(MasterCuteness, value); }
|
||||
public bool RibbonMasterCleverness { get => this.GetRibbonIndex(MasterCleverness); set => this.SetRibbonIndex(MasterCleverness, value); }
|
||||
public bool RibbonMasterToughness { get => this.GetRibbonIndex(MasterToughness); set => this.SetRibbonIndex(MasterToughness, value); }
|
||||
|
||||
public int RibbonCountMemoryContest { get => 0; set { } }
|
||||
public int RibbonCountMemoryBattle { get => 0; set { } }
|
||||
|
||||
public bool RibbonChampionAlola { get => this.GetRibbonIndex(ChampionAlola); set => this.SetRibbonIndex(ChampionAlola, value); }
|
||||
public bool RibbonBattleRoyale { get => this.GetRibbonIndex(BattleRoyale); set => this.SetRibbonIndex(BattleRoyale, value); }
|
||||
public bool RibbonBattleTreeGreat { get => this.GetRibbonIndex(BattleTreeGreat); set => this.SetRibbonIndex(BattleTreeGreat, value); }
|
||||
public bool RibbonBattleTreeMaster { get => this.GetRibbonIndex(BattleTreeMaster); set => this.SetRibbonIndex(BattleTreeMaster, value); }
|
||||
public bool RibbonChampionGalar { get => this.GetRibbonIndex(ChampionGalar); set => this.SetRibbonIndex(ChampionGalar, value); }
|
||||
public bool RibbonTowerMaster { get => this.GetRibbonIndex(TowerMaster); set => this.SetRibbonIndex(TowerMaster, value); }
|
||||
public bool RibbonMasterRank { get => this.GetRibbonIndex(MasterRank); set => this.SetRibbonIndex(MasterRank, value); }
|
||||
public bool RibbonMarkLunchtime { get => this.GetRibbonIndex(MarkLunchtime); set => this.SetRibbonIndex(MarkLunchtime, value); }
|
||||
public bool RibbonMarkSleepyTime { get => this.GetRibbonIndex(MarkSleepyTime); set => this.SetRibbonIndex(MarkSleepyTime, value); }
|
||||
public bool RibbonMarkDusk { get => this.GetRibbonIndex(MarkDusk); set => this.SetRibbonIndex(MarkDusk, value); }
|
||||
public bool RibbonMarkDawn { get => this.GetRibbonIndex(MarkDawn); set => this.SetRibbonIndex(MarkDawn, value); }
|
||||
public bool RibbonMarkCloudy { get => this.GetRibbonIndex(MarkCloudy); set => this.SetRibbonIndex(MarkCloudy, value); }
|
||||
public bool RibbonMarkRainy { get => this.GetRibbonIndex(MarkRainy); set => this.SetRibbonIndex(MarkRainy, value); }
|
||||
public bool RibbonMarkStormy { get => this.GetRibbonIndex(MarkStormy); set => this.SetRibbonIndex(MarkStormy, value); }
|
||||
public bool RibbonMarkSnowy { get => this.GetRibbonIndex(MarkSnowy); set => this.SetRibbonIndex(MarkSnowy, value); }
|
||||
public bool RibbonMarkBlizzard { get => this.GetRibbonIndex(MarkBlizzard); set => this.SetRibbonIndex(MarkBlizzard, value); }
|
||||
public bool RibbonMarkDry { get => this.GetRibbonIndex(MarkDry); set => this.SetRibbonIndex(MarkDry, value); }
|
||||
public bool RibbonMarkSandstorm { get => this.GetRibbonIndex(MarkSandstorm); set => this.SetRibbonIndex(MarkSandstorm, value); }
|
||||
public bool RibbonMarkMisty { get => this.GetRibbonIndex(MarkMisty); set => this.SetRibbonIndex(MarkMisty, value); }
|
||||
public bool RibbonMarkDestiny { get => this.GetRibbonIndex(MarkDestiny); set => this.SetRibbonIndex(MarkDestiny, value); }
|
||||
public bool RibbonMarkFishing { get => this.GetRibbonIndex(MarkFishing); set => this.SetRibbonIndex(MarkFishing, value); }
|
||||
public bool RibbonMarkCurry { get => this.GetRibbonIndex(MarkCurry); set => this.SetRibbonIndex(MarkCurry, value); }
|
||||
public bool RibbonMarkUncommon { get => this.GetRibbonIndex(MarkUncommon); set => this.SetRibbonIndex(MarkUncommon, value); }
|
||||
public bool RibbonMarkRare { get => this.GetRibbonIndex(MarkRare); set => this.SetRibbonIndex(MarkRare, value); }
|
||||
public bool RibbonMarkRowdy { get => this.GetRibbonIndex(MarkRowdy); set => this.SetRibbonIndex(MarkRowdy, value); }
|
||||
public bool RibbonMarkAbsentMinded { get => this.GetRibbonIndex(MarkAbsentMinded); set => this.SetRibbonIndex(MarkAbsentMinded, value); }
|
||||
public bool RibbonMarkJittery { get => this.GetRibbonIndex(MarkJittery); set => this.SetRibbonIndex(MarkJittery, value); }
|
||||
public bool RibbonMarkExcited { get => this.GetRibbonIndex(MarkExcited); set => this.SetRibbonIndex(MarkExcited, value); }
|
||||
public bool RibbonMarkCharismatic { get => this.GetRibbonIndex(MarkCharismatic); set => this.SetRibbonIndex(MarkCharismatic, value); }
|
||||
public bool RibbonMarkCalmness { get => this.GetRibbonIndex(MarkCalmness); set => this.SetRibbonIndex(MarkCalmness, value); }
|
||||
public bool RibbonMarkIntense { get => this.GetRibbonIndex(MarkIntense); set => this.SetRibbonIndex(MarkIntense, value); }
|
||||
public bool RibbonMarkZonedOut { get => this.GetRibbonIndex(MarkZonedOut); set => this.SetRibbonIndex(MarkZonedOut, value); }
|
||||
public bool RibbonMarkJoyful { get => this.GetRibbonIndex(MarkJoyful); set => this.SetRibbonIndex(MarkJoyful, value); }
|
||||
public bool RibbonMarkAngry { get => this.GetRibbonIndex(MarkAngry); set => this.SetRibbonIndex(MarkAngry, value); }
|
||||
public bool RibbonMarkSmiley { get => this.GetRibbonIndex(MarkSmiley); set => this.SetRibbonIndex(MarkSmiley, value); }
|
||||
public bool RibbonMarkTeary { get => this.GetRibbonIndex(MarkTeary); set => this.SetRibbonIndex(MarkTeary, value); }
|
||||
public bool RibbonMarkUpbeat { get => this.GetRibbonIndex(MarkUpbeat); set => this.SetRibbonIndex(MarkUpbeat, value); }
|
||||
public bool RibbonMarkPeeved { get => this.GetRibbonIndex(MarkPeeved); set => this.SetRibbonIndex(MarkPeeved, value); }
|
||||
public bool RibbonMarkIntellectual { get => this.GetRibbonIndex(MarkIntellectual); set => this.SetRibbonIndex(MarkIntellectual, value); }
|
||||
public bool RibbonMarkFerocious { get => this.GetRibbonIndex(MarkFerocious); set => this.SetRibbonIndex(MarkFerocious, value); }
|
||||
public bool RibbonMarkCrafty { get => this.GetRibbonIndex(MarkCrafty); set => this.SetRibbonIndex(MarkCrafty, value); }
|
||||
public bool RibbonMarkScowling { get => this.GetRibbonIndex(MarkScowling); set => this.SetRibbonIndex(MarkScowling, value); }
|
||||
public bool RibbonMarkKindly { get => this.GetRibbonIndex(MarkKindly); set => this.SetRibbonIndex(MarkKindly, value); }
|
||||
public bool RibbonMarkFlustered { get => this.GetRibbonIndex(MarkFlustered); set => this.SetRibbonIndex(MarkFlustered, value); }
|
||||
public bool RibbonMarkPumpedUp { get => this.GetRibbonIndex(MarkPumpedUp); set => this.SetRibbonIndex(MarkPumpedUp, value); }
|
||||
public bool RibbonMarkZeroEnergy { get => this.GetRibbonIndex(MarkZeroEnergy); set => this.SetRibbonIndex(MarkZeroEnergy, value); }
|
||||
public bool RibbonMarkPrideful { get => this.GetRibbonIndex(MarkPrideful); set => this.SetRibbonIndex(MarkPrideful, value); }
|
||||
public bool RibbonMarkUnsure { get => this.GetRibbonIndex(MarkUnsure); set => this.SetRibbonIndex(MarkUnsure, value); }
|
||||
public bool RibbonMarkHumble { get => this.GetRibbonIndex(MarkHumble); set => this.SetRibbonIndex(MarkHumble, value); }
|
||||
public bool RibbonMarkThorny { get => this.GetRibbonIndex(MarkThorny); set => this.SetRibbonIndex(MarkThorny, value); }
|
||||
public bool RibbonMarkVigor { get => this.GetRibbonIndex(MarkVigor); set => this.SetRibbonIndex(MarkVigor, value); }
|
||||
public bool RibbonMarkSlump { get => this.GetRibbonIndex(MarkSlump); set => this.SetRibbonIndex(MarkSlump, value); }
|
||||
public bool RibbonTwinklingStar { get => this.GetRibbonIndex(TwinklingStar); set => this.SetRibbonIndex(TwinklingStar, value); }
|
||||
public bool RibbonPioneer { get => this.GetRibbonIndex(Pioneer); set => this.SetRibbonIndex(Pioneer, value); }
|
||||
|
||||
public int GetRibbonByte(int index) => Array.FindIndex(Data, RibbonBytesOffset, RibbonBytesCount, z => z == index);
|
||||
public bool GetRibbon(int index) => GetRibbonByte(index) >= 0;
|
||||
|
||||
public void SetRibbon(int index, bool value = true)
|
||||
{
|
||||
if ((uint)index > (uint)MarkSlump)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (GetRibbon(index))
|
||||
return;
|
||||
var openIndex = Array.FindIndex(Data, RibbonBytesOffset, RibbonBytesCount, z => z != RibbonByteNone);
|
||||
if (openIndex < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
SetRibbonAtIndex(openIndex, (byte)index);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ofs = GetRibbonByte(index);
|
||||
if (ofs < 0)
|
||||
return;
|
||||
SetRibbonAtIndex(ofs, RibbonByteNone);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
866
PKHeX.Core/PKM/PA8.cs
Normal file
866
PKHeX.Core/PKM/PA8.cs
Normal file
|
@ -0,0 +1,866 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary> Generation 8 <see cref="PKM"/> format. </summary>
|
||||
public sealed class PA8 : PKM, ISanityChecksum,
|
||||
IRibbonSetEvent3, IRibbonSetEvent4, IRibbonSetCommon3, IRibbonSetCommon4, IRibbonSetCommon6, IRibbonSetCommon7, IRibbonSetCommon8, IRibbonSetMark8, IRibbonSetAffixed, IGanbaru, IAlpha, INoble, ITechRecord8, ISociability, IMoveShop8Mastery,
|
||||
IContestStats, IContestStatsMutable, IHyperTrain, IScaledSizeValue, IGigantamax, IFavorite, IDynamaxLevel, IRibbonIndex, IHandlerLanguage, IFormArgument, IHomeTrack, IBattleVersion, ITrainerMemories
|
||||
{
|
||||
private static readonly ushort[] Unused =
|
||||
{
|
||||
0x17, 0x1A, 0x1B, 0x23, 0x33,
|
||||
0x4C, 0x4D, 0x4E, 0x4F,
|
||||
0x52, 0x53,
|
||||
0xA0, 0xA1, 0xA2, 0xA3,
|
||||
0xAA, 0xAB,
|
||||
0xB4, 0xB5, 0xB6, 0xB7,
|
||||
0xD5,
|
||||
0xD6, 0xD7,
|
||||
0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xE9,
|
||||
0xF0, 0xF1,
|
||||
0xF3,
|
||||
0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
|
||||
0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x018, 0x109, 0x10A, 0x10B, 0x10C, 0x10D, 0x10E, 0x10F,
|
||||
0x12D, 0x13C,
|
||||
};
|
||||
|
||||
public override IReadOnlyList<ushort> ExtraBytes => Unused;
|
||||
public override PersonalInfo PersonalInfo => PersonalTable.LA.GetFormEntry(Species, Form);
|
||||
|
||||
public override int Format => 8;
|
||||
public PA8() : base(PokeCrypto.SIZE_8APARTY) => AffixedRibbon = -1; // 00 would make it show Kalos Champion :)
|
||||
public PA8(byte[] data) : base(DecryptParty(data)) { }
|
||||
|
||||
public override int SIZE_PARTY => PokeCrypto.SIZE_8APARTY;
|
||||
public override int SIZE_STORED => PokeCrypto.SIZE_8ASTORED;
|
||||
public override bool ChecksumValid => CalculateChecksum() == Checksum;
|
||||
public override PKM Clone() => new PA8((byte[])Data.Clone());
|
||||
|
||||
private static byte[] DecryptParty(byte[] data)
|
||||
{
|
||||
PokeCrypto.DecryptIfEncrypted8A(ref data);
|
||||
Array.Resize(ref data, PokeCrypto.SIZE_8APARTY);
|
||||
return data;
|
||||
}
|
||||
|
||||
private ushort CalculateChecksum()
|
||||
{
|
||||
ushort chk = 0;
|
||||
for (int i = 8; i < PokeCrypto.SIZE_8ASTORED; i += 2)
|
||||
chk += ReadUInt16LittleEndian(Data.AsSpan(i));
|
||||
return chk;
|
||||
}
|
||||
|
||||
// Simple Generated Attributes
|
||||
public ReadOnlySpan<bool> TechRecordPermitFlags => PersonalInfo.TMHM.AsSpan(PersonalInfoSWSH.CountTM);
|
||||
public ReadOnlySpan<int> TechRecordPermitIndexes => Legal.TMHM_SWSH.AsSpan(PersonalInfoSWSH.CountTM);
|
||||
public ReadOnlySpan<bool> MoveShopPermitFlags => PersonalInfo.SpecialTutors[0];
|
||||
public ReadOnlySpan<int> MoveShopPermitIndexes => Legal.MoveShop8_LA;
|
||||
|
||||
public override int CurrentFriendship
|
||||
{
|
||||
get => CurrentHandler == 0 ? OT_Friendship : HT_Friendship;
|
||||
set { if (CurrentHandler == 0) OT_Friendship = value; else HT_Friendship = value; }
|
||||
}
|
||||
|
||||
public override void RefreshChecksum() => Checksum = CalculateChecksum();
|
||||
public override bool Valid { get => Sanity == 0 && ChecksumValid; set { if (!value) return; Sanity = 0; RefreshChecksum(); } }
|
||||
|
||||
// Trash Bytes
|
||||
public override Span<byte> Nickname_Trash => Data.AsSpan(0x60, 26);
|
||||
public override Span<byte> HT_Trash => Data.AsSpan(0xB8, 26);
|
||||
public override Span<byte> OT_Trash => Data.AsSpan(0x110, 26);
|
||||
|
||||
// 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[0xB8] == 0 && Data[0xB8 + 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.EncryptArray8A(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 => ReadUInt32LittleEndian(Data.AsSpan(0x00)); set => WriteUInt32LittleEndian(Data.AsSpan(0x00), value); }
|
||||
public ushort Sanity { get => ReadUInt16LittleEndian(Data.AsSpan(0x04)); set => WriteUInt16LittleEndian(Data.AsSpan(0x04), value); }
|
||||
public ushort Checksum { get => ReadUInt16LittleEndian(Data.AsSpan(0x06)); set => WriteUInt16LittleEndian(Data.AsSpan(0x06), value); }
|
||||
|
||||
// Structure
|
||||
#region Block A
|
||||
public override int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x08)); set => WriteUInt16LittleEndian(Data.AsSpan(0x08), (ushort)value); }
|
||||
public override int HeldItem { get => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
|
||||
public override int TID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0C), (ushort)value); }
|
||||
public override int SID { get => ReadUInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0E), (ushort)value); }
|
||||
public override uint EXP { get => ReadUInt32LittleEndian(Data.AsSpan(0x10)); set => WriteUInt32LittleEndian(Data.AsSpan(0x10), value); }
|
||||
public override int Ability { get => ReadUInt16LittleEndian(Data.AsSpan(0x14)); set => WriteUInt16LittleEndian(Data.AsSpan(0x14), (ushort)value); }
|
||||
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)); }
|
||||
public bool IsAlpha { get => (Data[0x16] & 32) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~32) | ((value ? 1 : 0) << 5)); }
|
||||
public bool IsNoble { get => (Data[0x16] & 64) != 0; set => Data[0x16] = (byte)((Data[0x16] & ~64) | ((value ? 1 : 0) << 6)); }
|
||||
// 0x17 alignment unused
|
||||
public override int MarkValue { get => ReadUInt16LittleEndian(Data.AsSpan(0x18)); protected set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); }
|
||||
// 0x1A alignment unused
|
||||
// 0x1B alignment unused
|
||||
public override uint PID { get => ReadUInt32LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt32LittleEndian(Data.AsSpan(0x1C), value); }
|
||||
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 => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
|
||||
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; }
|
||||
|
||||
public int AlphaMove { get => ReadUInt16LittleEndian(Data.AsSpan(0x3E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x3E), (ushort)value); }
|
||||
|
||||
// 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 RibbonPioneer { get => FlagUtil.GetFlag(Data, 0x44, 2); set => FlagUtil.SetFlag(Data, 0x44, 2, value); }
|
||||
public bool RibbonTwinklingStar { 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.AsSpan();
|
||||
if ((ReadUInt16LittleEndian(d[0x3A..]) & 0xFFE0) != 0)
|
||||
return true;
|
||||
if (ReadUInt32LittleEndian(d[0x40..]) != 0)
|
||||
return true;
|
||||
return (d[0x44] & 3) != 0;
|
||||
}
|
||||
|
||||
public uint Sociability { get => ReadUInt32LittleEndian(Data.AsSpan(0x48)); set => WriteUInt32LittleEndian(Data.AsSpan(0x48), value); }
|
||||
|
||||
// 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; }
|
||||
public int HeightScalarCopy { get => Data[0x52]; set => Data[0x52] = (byte)value; }
|
||||
|
||||
// 0x53 unused
|
||||
|
||||
public override int Move1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x54)); set => WriteUInt16LittleEndian(Data.AsSpan(0x54), (ushort)value); }
|
||||
public override int Move2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x56)); set => WriteUInt16LittleEndian(Data.AsSpan(0x56), (ushort)value); }
|
||||
public override int Move3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x58)); set => WriteUInt16LittleEndian(Data.AsSpan(0x58), (ushort)value); }
|
||||
public override int Move4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), (ushort)value); }
|
||||
|
||||
public override int Move1_PP { get => Data[0x5C]; set => Data[0x5C] = (byte)value; }
|
||||
public override int Move2_PP { get => Data[0x5D]; set => Data[0x5D] = (byte)value; }
|
||||
public override int Move3_PP { get => Data[0x5E]; set => Data[0x5E] = (byte)value; }
|
||||
public override int Move4_PP { get => Data[0x5F]; set => Data[0x5F] = (byte)value; }
|
||||
#endregion
|
||||
#region Block B
|
||||
public override string Nickname
|
||||
{
|
||||
get => StringConverter8.GetString(Nickname_Trash);
|
||||
set => StringConverter8.SetString(Nickname_Trash, value.AsSpan(), 12, StringConverterOption.None);
|
||||
}
|
||||
|
||||
// 2 bytes for \0, automatically handled above
|
||||
|
||||
public override int Move1_PPUps { get => Data[0x86]; set => Data[0x86] = (byte)value; }
|
||||
public override int Move2_PPUps { get => Data[0x87]; set => Data[0x87] = (byte)value; }
|
||||
public override int Move3_PPUps { get => Data[0x88]; set => Data[0x88] = (byte)value; }
|
||||
public override int Move4_PPUps { get => Data[0x89]; set => Data[0x89] = (byte)value; }
|
||||
|
||||
public override int RelearnMove1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x8A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8A), (ushort)value); }
|
||||
public override int RelearnMove2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x8C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8C), (ushort)value); }
|
||||
public override int RelearnMove3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x8E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x8E), (ushort)value); }
|
||||
public override int RelearnMove4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x90)); set => WriteUInt16LittleEndian(Data.AsSpan(0x90), (ushort)value); }
|
||||
|
||||
public override int Stat_HPCurrent { get => ReadUInt16LittleEndian(Data.AsSpan(0x92)); set => WriteUInt16LittleEndian(Data.AsSpan(0x92), (ushort)value); }
|
||||
private uint IV32 { get => ReadUInt32LittleEndian(Data.AsSpan(0x94)); set => WriteUInt32LittleEndian(Data.AsSpan(0x94), value); }
|
||||
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[0x98]; set => Data[0x98] = value; }
|
||||
|
||||
public override int Status_Condition { get => ReadInt32LittleEndian(Data.AsSpan(0x9C)); set => WriteInt32LittleEndian(Data.AsSpan(0x9C), value); }
|
||||
public int UnkA0 { get => ReadInt32LittleEndian(Data.AsSpan(0xA0)); set => WriteInt32LittleEndian(Data.AsSpan(0xA0), value); }
|
||||
public int GV_HP { get => Data[0xA4]; set => Data[0xA4] = (byte)value; }
|
||||
public int GV_ATK { get => Data[0xA5]; set => Data[0xA5] = (byte)value; }
|
||||
public int GV_DEF { get => Data[0xA6]; set => Data[0xA6] = (byte)value; }
|
||||
public int GV_SPE { get => Data[0xA7]; set => Data[0xA7] = (byte)value; }
|
||||
public int GV_SPA { get => Data[0xA8]; set => Data[0xA8] = (byte)value; }
|
||||
public int GV_SPD { get => Data[0xA9]; set => Data[0xA9] = (byte)value; }
|
||||
|
||||
// 0xAA-0xAB unused
|
||||
|
||||
public float HeightAbsolute { get => ReadSingleLittleEndian(Data.AsSpan(0xAC)); set => WriteSingleLittleEndian(Data.AsSpan(0xAC), value); }
|
||||
public float WeightAbsolute { get => ReadSingleLittleEndian(Data.AsSpan(0xB0)); set => WriteSingleLittleEndian(Data.AsSpan(0xB0), value); }
|
||||
|
||||
// 0xB4-0xB7 unused
|
||||
|
||||
#endregion
|
||||
#region Block C
|
||||
public override string HT_Name
|
||||
{
|
||||
get => StringConverter8.GetString(HT_Trash);
|
||||
set => StringConverter8.SetString(HT_Trash, value.AsSpan(), 12, StringConverterOption.None);
|
||||
}
|
||||
|
||||
public override int HT_Gender { get => Data[0xD2]; set => Data[0xD2] = (byte)value; }
|
||||
public int HT_Language { get => Data[0xD3]; set => Data[0xD3] = (byte)value; }
|
||||
public override int CurrentHandler { get => Data[0xD4]; set => Data[0xD4] = (byte)value; }
|
||||
// 0xD5 unused (alignment)
|
||||
public int HT_TrainerID { get => ReadUInt16LittleEndian(Data.AsSpan(0xD6)); set => WriteUInt16LittleEndian(Data.AsSpan(0xD6), (ushort)value); } // unused?
|
||||
public override int HT_Friendship { get => Data[0xD8]; set => Data[0xD8] = (byte)value; }
|
||||
public int HT_Intensity { get => Data[0xD9]; set => Data[0xD9] = (byte)value; }
|
||||
public int HT_Memory { get => Data[0xDA]; set => Data[0xDA] = (byte)value; }
|
||||
public int HT_Feeling { get => Data[0xDB]; set => Data[0xDB] = (byte)value; }
|
||||
public int HT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0xDC)); set => WriteUInt16LittleEndian(Data.AsSpan(0xDC), (ushort)value); }
|
||||
|
||||
// 0xDE-0xEB unused
|
||||
|
||||
public override byte Fullness { get => Data[0xEC]; set => Data[0xEC] = value; }
|
||||
public override byte Enjoyment { get => Data[0xED]; set => Data[0xED] = value; }
|
||||
public override int Version { get => Data[0xEE]; set => Data[0xEE] = (byte)value; }
|
||||
public int BattleVersion { get => Data[0xEF]; set => Data[0xEF] = (byte)value; }
|
||||
// public override int Region { get => Data[0xF0]; set => Data[0xF0] = (byte)value; }
|
||||
// public override int ConsoleRegion { get => Data[0xF1]; set => Data[0xF1] = (byte)value; }
|
||||
public override int Language { get => Data[0xF2]; set => Data[0xF2] = (byte)value; }
|
||||
public int UnkF3 { get => Data[0xF3]; set => Data[0xF3] = (byte)value; }
|
||||
public uint FormArgument { get => ReadUInt32LittleEndian(Data.AsSpan(0xF4)); set => WriteUInt32LittleEndian(Data.AsSpan(0xF4), value); }
|
||||
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[0xF8]; set => Data[0xF8] = (byte)value; } // selected ribbon
|
||||
// remainder unused
|
||||
#endregion
|
||||
#region Block D
|
||||
public override string OT_Name
|
||||
{
|
||||
get => StringConverter8.GetString(OT_Trash);
|
||||
set => StringConverter8.SetString(OT_Trash, value.AsSpan(), 12, StringConverterOption.None);
|
||||
}
|
||||
|
||||
public override int OT_Friendship { get => Data[0x12A]; set => Data[0x12A] = (byte)value; }
|
||||
public int OT_Intensity { get => Data[0x12B]; set => Data[0x12B] = (byte)value; }
|
||||
public int OT_Memory { get => Data[0x12C]; set => Data[0x12C] = (byte)value; }
|
||||
// 0x12D unused align
|
||||
public int OT_TextVar { get => ReadUInt16LittleEndian(Data.AsSpan(0x12E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x12E), (ushort)value); }
|
||||
public int OT_Feeling { get => Data[0x130]; set => Data[0x130] = (byte)value; }
|
||||
public override int Egg_Year { get => Data[0x131]; set => Data[0x131] = (byte)value; }
|
||||
public override int Egg_Month { get => Data[0x132]; set => Data[0x132] = (byte)value; }
|
||||
public override int Egg_Day { get => Data[0x133]; set => Data[0x133] = (byte)value; }
|
||||
public override int Met_Year { get => Data[0x134]; set => Data[0x134] = (byte)value; }
|
||||
public override int Met_Month { get => Data[0x135]; set => Data[0x135] = (byte)value; }
|
||||
public override int Met_Day { get => Data[0x136]; set => Data[0x136] = (byte)value; }
|
||||
public override int Ball { get => Data[0x137]; set => Data[0x137] = (byte)value; }
|
||||
public override int Egg_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0x138)); set => WriteUInt16LittleEndian(Data.AsSpan(0x138), (ushort)value); }
|
||||
public override int Met_Location { get => ReadUInt16LittleEndian(Data.AsSpan(0x13A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x13A), (ushort)value); }
|
||||
// 0x13C unused align
|
||||
public override int Met_Level { get => Data[0x13D] & ~0x80; set => Data[0x13D] = (byte)((Data[0x13D] & 0x80) | value); }
|
||||
public override int OT_Gender { get => Data[0x13D] >> 7; set => Data[0x13D] = (byte)((Data[0x13D] & ~0x80) | (value << 7)); }
|
||||
public int HyperTrainFlags { get => Data[0x13E]; set => Data[0x13E] = (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, 0x13F + 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, 0x13F + ofs, index & 7, value);
|
||||
}
|
||||
|
||||
public bool GetMoveRecordFlagAny() => Array.FindIndex(Data, 0x13F, 14, z => z != 0) >= 0;
|
||||
|
||||
// Why did you mis-align this field, GameFreak?
|
||||
public ulong Tracker
|
||||
{
|
||||
get => ReadUInt64LittleEndian(Data.AsSpan(0x14D));
|
||||
set => WriteUInt64LittleEndian(Data.AsSpan(0x14D), value);
|
||||
}
|
||||
|
||||
public bool GetPurchasedRecordFlag(int index)
|
||||
{
|
||||
if ((uint)index > 63) // 8 bytes, 8 bits
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
int ofs = index >> 3;
|
||||
return FlagUtil.GetFlag(Data, 0x155 + ofs, index & 7);
|
||||
}
|
||||
|
||||
public void SetPurchasedRecordFlag(int index, bool value)
|
||||
{
|
||||
if ((uint)index > 63) // 8 bytes, 8 bits
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
int ofs = index >> 3;
|
||||
FlagUtil.SetFlag(Data, 0x155 + ofs, index & 7, value);
|
||||
}
|
||||
|
||||
public bool GetPurchasedRecordFlagAny() => Array.FindIndex(Data, 0x155, 8, z => z != 0) >= 0;
|
||||
|
||||
public bool GetMasteredRecordFlag(int index)
|
||||
{
|
||||
if ((uint)index > 63) // 8 bytes, 8 bits
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
int ofs = index >> 3;
|
||||
return FlagUtil.GetFlag(Data, 0x15D + ofs, index & 7);
|
||||
}
|
||||
|
||||
public void SetMasteredRecordFlag(int index, bool value)
|
||||
{
|
||||
if ((uint)index > 63) // 8 bytes, 8 bits
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
int ofs = index >> 3;
|
||||
FlagUtil.SetFlag(Data, 0x15D + ofs, index & 7, value);
|
||||
}
|
||||
|
||||
public bool GetMasteredRecordFlagAny() => Array.FindIndex(Data, 0x15D, 8, z => z != 0) >= 0;
|
||||
|
||||
#endregion
|
||||
#region Battle Stats
|
||||
public override int Stat_Level { get => Data[0x168]; set => Data[0x168] = (byte)value; }
|
||||
// 0x149 unused alignment
|
||||
public override int Stat_HPMax { get => ReadUInt16LittleEndian(Data.AsSpan(0x16A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16A), (ushort)value); }
|
||||
public override int Stat_ATK { get => ReadUInt16LittleEndian(Data.AsSpan(0x16C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16C), (ushort)value); }
|
||||
public override int Stat_DEF { get => ReadUInt16LittleEndian(Data.AsSpan(0x16E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x16E), (ushort)value); }
|
||||
public override int Stat_SPE { get => ReadUInt16LittleEndian(Data.AsSpan(0x170)); set => WriteUInt16LittleEndian(Data.AsSpan(0x170), (ushort)value); }
|
||||
public override int Stat_SPA { get => ReadUInt16LittleEndian(Data.AsSpan(0x172)); set => WriteUInt16LittleEndian(Data.AsSpan(0x172), (ushort)value); }
|
||||
public override int Stat_SPD { get => ReadUInt16LittleEndian(Data.AsSpan(0x174)); set => WriteUInt16LittleEndian(Data.AsSpan(0x174), (ushort)value); }
|
||||
#endregion
|
||||
|
||||
public override ushort[] GetStats(PersonalInfo p) => CalculateStatsArceus(p);
|
||||
|
||||
public ushort[] CalculateStatsArceus(PersonalInfo p)
|
||||
{
|
||||
int level = CurrentLevel;
|
||||
int nature = StatNature;
|
||||
|
||||
return new[]
|
||||
{
|
||||
(ushort)(GetGanbaruStat(p.HP, HT_HP ? 31 : IV_HP, GV_HP, level) + GetStatHp(p.HP, level)),
|
||||
(ushort)(GetGanbaruStat(p.ATK, HT_ATK ? 31 : IV_ATK, GV_ATK, level) + GetStat(p.ATK, level, nature, 0)),
|
||||
(ushort)(GetGanbaruStat(p.DEF, HT_DEF ? 31 : IV_DEF, GV_DEF, level) + GetStat(p.DEF, level, nature, 1)),
|
||||
(ushort)(GetGanbaruStat(p.SPE, HT_SPE ? 31 : IV_SPE, GV_SPE, level) + GetStat(p.SPE, level, nature, 4)),
|
||||
(ushort)(GetGanbaruStat(p.SPA, HT_SPA ? 31 : IV_SPA, GV_SPA, level) + GetStat(p.SPA, level, nature, 2)),
|
||||
(ushort)(GetGanbaruStat(p.SPD, HT_SPD ? 31 : IV_SPD, GV_SPD, level) + GetStat(p.SPD, level, nature, 3)),
|
||||
};
|
||||
}
|
||||
|
||||
public static int GetGanbaruStat(int baseStat, int iv, int gv, int level)
|
||||
{
|
||||
int mul = GanbaruExtensions.GetGanbaruMultiplier(gv, iv);
|
||||
double step1 = Math.Abs(Math.Sqrt((float)baseStat)) * mul; // The game does abs after sqrt; should be before. It's fine because baseStat is never negative.
|
||||
var result = ((float)step1 + level) / 2.5f;
|
||||
return (int)Math.Round(result, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
public static int GetStatHp(int baseStat, int level)
|
||||
{
|
||||
return (int)((((level / 100.0f) + 1.0f) * baseStat) + level);
|
||||
}
|
||||
|
||||
public static int GetStat(int baseStat, int level, int nature, int statIndex)
|
||||
{
|
||||
var initial = (int)((((level / 50.0f) + 1.0f) * baseStat) / 1.5f);
|
||||
return AmplifyStat(nature, statIndex, initial);
|
||||
}
|
||||
|
||||
private static int AmplifyStat(int nature, int index, int initial) => GetNatureAmp(nature, index) switch
|
||||
{
|
||||
1 => 110 * initial / 100, // 110%
|
||||
-1 => 90 * initial / 100, // 90%
|
||||
_ => initial,
|
||||
};
|
||||
|
||||
private static sbyte GetNatureAmp(int nature, int index)
|
||||
{
|
||||
if ((uint)nature >= 25)
|
||||
return -1;
|
||||
return NatureAmpTable[(5 * nature) + index];
|
||||
}
|
||||
|
||||
private static readonly sbyte[] NatureAmpTable =
|
||||
{
|
||||
0, 0, 0, 0, 0, // Hardy
|
||||
1,-1, 0, 0, 0, // Lonely
|
||||
1, 0, 0, 0,-1, // Brave
|
||||
1, 0,-1, 0, 0, // Adamant
|
||||
1, 0, 0,-1, 0, // Naughty
|
||||
-1, 1, 0, 0, 0, // Bold
|
||||
0, 0, 0, 0, 0, // Docile
|
||||
0, 1, 0, 0,-1, // Relaxed
|
||||
0, 1,-1, 0, 0, // Impish
|
||||
0, 1, 0,-1, 0, // Lax
|
||||
-1, 0, 0, 0, 1, // Timid
|
||||
0,-1, 0, 0, 1, // Hasty
|
||||
0, 0, 0, 0, 0, // Serious
|
||||
0, 0,-1, 0, 1, // Jolly
|
||||
0, 0, 0,-1, 1, // Naive
|
||||
-1, 0, 1, 0, 0, // Modest
|
||||
0,-1, 1, 0, 0, // Mild
|
||||
0, 0, 1, 0,-1, // Quiet
|
||||
0, 0, 0, 0, 0, // Bashful
|
||||
0, 0, 1,-1, 0, // Rash
|
||||
-1, 0, 0, 1, 0, // Calm
|
||||
0,-1, 0, 1, 0, // Gentle
|
||||
0, 0, 0, 1,-1, // Sassy
|
||||
0, 0,-1, 1, 0, // Careful
|
||||
0, 0, 0, 0, 0, // Quirky
|
||||
};
|
||||
|
||||
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 => SetMarkings(value);
|
||||
}
|
||||
|
||||
public override void SetMarkings(ReadOnlySpan<int> value)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
public void Trade(ITrainerInfo tr, int Day = 29, int Month = 1, int Year = 2022)
|
||||
{
|
||||
if (IsEgg)
|
||||
{
|
||||
// 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 (LA)
|
||||
{
|
||||
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_TextVar = HT_Memory = HT_Intensity = HT_Feeling = 0;
|
||||
OT_TextVar = OT_Memory = OT_Intensity = OT_Feeling = 0;
|
||||
|
||||
// Clear Handler
|
||||
if (!IsTradedEgg)
|
||||
{
|
||||
HT_Friendship = HT_Language = HT_Gender = 0;
|
||||
HT_Trash.Clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsUntraded)
|
||||
HT_Language = HT_Friendship = HT_TextVar = HT_Memory = HT_Intensity = HT_Feeling = HT_Gender = 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 = PersonalInfo.BaseFriendship;
|
||||
HT_Name = tr.OT;
|
||||
}
|
||||
CurrentHandler = 1;
|
||||
HT_Gender = tr.Gender;
|
||||
HT_Language = tr.Language;
|
||||
}
|
||||
|
||||
// Maximums
|
||||
public override int MaxMoveID => Legal.MaxMoveID_8a;
|
||||
public override int MaxSpeciesID => Legal.MaxSpeciesID_8a;
|
||||
public override int MaxAbilityID => Legal.MaxAbilityID_8a;
|
||||
public override int MaxItemID => Legal.MaxItemID_8a;
|
||||
public override int MaxBallID => Legal.MaxBallID_8a;
|
||||
public override int MaxGameID => Legal.MaxGameID_8a;
|
||||
|
||||
// Casts are as per the game code; they may seem redundant but every bit of precision matters?
|
||||
// This still doesn't precisely match :( -- just use a tolerance check when updating.
|
||||
// If anyone can figure out how to get all precision to match, feel free to update :)
|
||||
public float HeightRatio => GetHeightRatio(HeightScalar);
|
||||
public float WeightRatio => GetWeightRatio(WeightScalar);
|
||||
|
||||
public float CalcHeightAbsolute => GetHeightAbsolute(PersonalInfo, HeightScalar);
|
||||
public float CalcWeightAbsolute => GetWeightAbsolute(PersonalInfo, HeightScalar, WeightScalar);
|
||||
|
||||
public void ResetHeight()
|
||||
{
|
||||
var current = HeightAbsolute;
|
||||
var updated = CalcHeightAbsolute;
|
||||
if (Math.Abs(current - updated) > 0.0001f)
|
||||
HeightAbsolute = updated;
|
||||
}
|
||||
|
||||
public void ResetWeight()
|
||||
{
|
||||
var current = WeightAbsolute;
|
||||
var updated = CalcWeightAbsolute;
|
||||
if (Math.Abs(current - updated) > 0.0001f)
|
||||
WeightAbsolute = updated;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
private static float GetHeightRatio(int heightScalar)
|
||||
{
|
||||
// +/- 20% (down from +/- 40% in LGP/E)
|
||||
float result = (byte)heightScalar / 255f;
|
||||
result *= 0.4f;
|
||||
result += 0.8f;
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
private static float GetWeightRatio(int weightScalar)
|
||||
{
|
||||
// +/- 20%
|
||||
float result = (byte)weightScalar / 255f;
|
||||
result *= 0.4f;
|
||||
result += 0.8f;
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public static float GetHeightAbsolute(PersonalInfo p, int heightScalar)
|
||||
{
|
||||
float HeightRatio = GetHeightRatio(heightScalar);
|
||||
return HeightRatio * p.Height;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public static float GetWeightAbsolute(PersonalInfo p, int heightScalar, int weightScalar)
|
||||
{
|
||||
float HeightRatio = GetHeightRatio(heightScalar);
|
||||
float WeightRatio = GetWeightRatio(weightScalar);
|
||||
|
||||
float weight = WeightRatio * p.Weight;
|
||||
return HeightRatio * weight;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public static byte GetHeightScalar(float height, int avgHeight)
|
||||
{
|
||||
// height is already *100
|
||||
float biasH = avgHeight * -0.8f;
|
||||
float biasL = avgHeight * 0.4f;
|
||||
float numerator = biasH + height;
|
||||
float result = numerator / biasL;
|
||||
result *= 255f;
|
||||
int value = (int)result;
|
||||
int unsigned = value & ~(value >> 31);
|
||||
return (byte)Math.Min(255, unsigned);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)]
|
||||
public static byte GetWeightScalar(float height, float weight, int avgHeight, int avgWeight)
|
||||
{
|
||||
// height is already *100
|
||||
// weight is already *10
|
||||
float heightRatio = height / avgHeight;
|
||||
float weightComponent = heightRatio * weight;
|
||||
float top = avgWeight * -0.8f;
|
||||
top += weightComponent;
|
||||
float bot = avgWeight * 0.4f;
|
||||
float result = top / bot;
|
||||
result *= 255f;
|
||||
int value = (int)result;
|
||||
int unsigned = value & ~(value >> 31);
|
||||
return (byte)Math.Min(255, unsigned);
|
||||
}
|
||||
|
||||
public int GetRandomAlphaMove()
|
||||
{
|
||||
var index = MoveShopPermitFlags.IndexOf(true);
|
||||
if (index == -1)
|
||||
return 0;
|
||||
return MoveShopPermitIndexes[index];
|
||||
}
|
||||
|
||||
public void SetMasteryFlags()
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
SetMasteryFlagMove(GetMove(i));
|
||||
}
|
||||
|
||||
public void SetMasteryFlagMove(int move)
|
||||
{
|
||||
var moves = MoveShopPermitIndexes;
|
||||
int flagIndex = moves.IndexOf(move);
|
||||
if (flagIndex == -1)
|
||||
return;
|
||||
if (MoveShopPermitFlags[flagIndex])
|
||||
SetMasteredRecordFlag(flagIndex, true);
|
||||
}
|
||||
}
|
9
PKHeX.Core/PKM/Shared/IAlpha.cs
Normal file
9
PKHeX.Core/PKM/Shared/IAlpha.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Interface that exposes an indication if the Pokémon is an alpha Pokémon.
|
||||
/// </summary>
|
||||
public interface IAlpha
|
||||
{
|
||||
bool IsAlpha { get; set; }
|
||||
}
|
109
PKHeX.Core/PKM/Shared/IGanbaru.cs
Normal file
109
PKHeX.Core/PKM/Shared/IGanbaru.cs
Normal file
|
@ -0,0 +1,109 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public interface IGanbaru
|
||||
{
|
||||
int GV_HP { get; set; }
|
||||
int GV_ATK { get; set; }
|
||||
int GV_DEF { get; set; }
|
||||
int GV_SPE { get; set; }
|
||||
int GV_SPA { get; set; }
|
||||
int GV_SPD { get; set; }
|
||||
}
|
||||
|
||||
public static class GanbaruExtensions
|
||||
{
|
||||
public const int TrueMax = 10;
|
||||
|
||||
private static readonly ushort[] GanbaruMultiplier = { 0, 2, 3, 4, 7, 8, 9, 14, 15, 16, 25 };
|
||||
|
||||
public static int GetMax(this IGanbaru _, PKM pk, int index) => GetMaxGanbaru(pk, index);
|
||||
|
||||
public static int GetMaxGanbaru(this PKM pk, int index)
|
||||
{
|
||||
var iv = pk.GetIV(index);
|
||||
return GetMaxGanbaru(iv);
|
||||
}
|
||||
|
||||
private static int GetMaxGanbaru(int iv)
|
||||
{
|
||||
var bias = GetBias(iv);
|
||||
return TrueMax - bias;
|
||||
}
|
||||
|
||||
public static int GetBias(int iv) => iv switch
|
||||
{
|
||||
>= 31 => 3,
|
||||
>= 26 => 2,
|
||||
>= 20 => 1,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
public static void SetSuggestedGanbaruValues(this IGanbaru g, PKM pk)
|
||||
{
|
||||
g.GV_HP = GetMaxGanbaru(pk.IV_HP);
|
||||
g.GV_ATK = pk.IV_ATK == 0 ? 0 : GetMaxGanbaru(pk.IV_ATK);
|
||||
g.GV_DEF = GetMaxGanbaru(pk.IV_DEF);
|
||||
g.GV_SPE = pk.IV_SPE == 0 ? 0 : GetMaxGanbaru(pk.IV_SPE);
|
||||
g.GV_SPA = GetMaxGanbaru(pk.IV_SPA);
|
||||
g.GV_SPD = GetMaxGanbaru(pk.IV_SPD);
|
||||
}
|
||||
|
||||
public static bool IsGanbaruValuesMax(this IGanbaru g, PKM pk)
|
||||
{
|
||||
var result = true;
|
||||
result &= g.GV_HP == GetMaxGanbaru(pk.IV_HP);
|
||||
result &= g.GV_ATK >= (pk.IV_ATK == 0 ? 0 : GetMaxGanbaru(pk.IV_ATK));
|
||||
result &= g.GV_DEF == GetMaxGanbaru(pk.IV_DEF);
|
||||
result &= g.GV_SPE >= (pk.IV_SPE == 0 ? 0 : GetMaxGanbaru(pk.IV_SPE));
|
||||
result &= g.GV_SPA == GetMaxGanbaru(pk.IV_SPA);
|
||||
result &= g.GV_SPD == GetMaxGanbaru(pk.IV_SPD);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void ClearGanbaruValues(this IGanbaru g)
|
||||
{
|
||||
g.GV_HP = 0;
|
||||
g.GV_ATK = 0;
|
||||
g.GV_DEF = 0;
|
||||
g.GV_SPE = 0;
|
||||
g.GV_SPA = 0;
|
||||
g.GV_SPD = 0;
|
||||
}
|
||||
|
||||
public static int GetGanbaruMultiplier(int gv, int iv) => GanbaruMultiplier[Math.Min(gv + GetBias(iv), 10)];
|
||||
|
||||
/// <summary>
|
||||
/// Sets one of the <see cref="IAwakened"/> values based on its index within the array.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to modify.</param>
|
||||
/// <param name="index">Index to set to</param>
|
||||
/// <param name="value">Value to set</param>
|
||||
public static int SetGV(this IGanbaru pk, int index, int value) => index switch
|
||||
{
|
||||
0 => pk.GV_HP = value,
|
||||
1 => pk.GV_ATK = value,
|
||||
2 => pk.GV_DEF = value,
|
||||
3 => pk.GV_SPE = value,
|
||||
4 => pk.GV_SPA = value,
|
||||
5 => pk.GV_SPD = value,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index)),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Sets one of the <see cref="IAwakened"/> values based on its index within the array.
|
||||
/// </summary>
|
||||
/// <param name="pk">Pokémon to check.</param>
|
||||
/// <param name="index">Index to get</param>
|
||||
public static int GetGV(this IGanbaru pk, int index) => index switch
|
||||
{
|
||||
0 => pk.GV_HP,
|
||||
1 => pk.GV_ATK,
|
||||
2 => pk.GV_DEF,
|
||||
3 => pk.GV_SPE,
|
||||
4 => pk.GV_SPA,
|
||||
5 => pk.GV_SPD,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index)),
|
||||
};
|
||||
}
|
19
PKHeX.Core/PKM/Shared/IMoveShop8.cs
Normal file
19
PKHeX.Core/PKM/Shared/IMoveShop8.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public interface IMoveShop8
|
||||
{
|
||||
ReadOnlySpan<bool> MoveShopPermitFlags { get; }
|
||||
ReadOnlySpan<int> MoveShopPermitIndexes { get; }
|
||||
bool GetPurchasedRecordFlag(int index);
|
||||
void SetPurchasedRecordFlag(int index, bool value);
|
||||
bool GetPurchasedRecordFlagAny();
|
||||
}
|
||||
|
||||
public interface IMoveShop8Mastery : IMoveShop8
|
||||
{
|
||||
bool GetMasteredRecordFlag(int index);
|
||||
void SetMasteredRecordFlag(int index, bool value);
|
||||
bool GetMasteredRecordFlagAny();
|
||||
}
|
9
PKHeX.Core/PKM/Shared/INoble.cs
Normal file
9
PKHeX.Core/PKM/Shared/INoble.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Interface that exposes an indication if the Pokémon is a Noble Pokémon.
|
||||
/// </summary>
|
||||
public interface INoble
|
||||
{
|
||||
bool IsNoble { get; set; }
|
||||
}
|
12
PKHeX.Core/PKM/Shared/ITechRecord8.cs
Normal file
12
PKHeX.Core/PKM/Shared/ITechRecord8.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
public interface ITechRecord8
|
||||
{
|
||||
ReadOnlySpan<bool> TechRecordPermitFlags { get; }
|
||||
ReadOnlySpan<int> TechRecordPermitIndexes { get; }
|
||||
bool GetMoveRecordFlag(int index);
|
||||
void SetMoveRecordFlag(int index, bool state = true);
|
||||
bool GetMoveRecordFlagAny();
|
||||
}
|
|
@ -9,7 +9,7 @@ namespace PKHeX.Core
|
|||
/// </summary>
|
||||
public static class PKX
|
||||
{
|
||||
internal static readonly PersonalTable Personal = PersonalTable.SWSH;
|
||||
internal static readonly PersonalTable Personal = PersonalTable.LA;
|
||||
public const int Generation = 8;
|
||||
|
||||
private static readonly HashSet<int> Sizes = new()
|
||||
|
@ -22,6 +22,7 @@ namespace PKHeX.Core
|
|||
PokeCrypto.SIZE_5PARTY,
|
||||
PokeCrypto.SIZE_6STORED, PokeCrypto.SIZE_6PARTY,
|
||||
PokeCrypto.SIZE_8STORED, PokeCrypto.SIZE_8PARTY,
|
||||
PokeCrypto.SIZE_8ASTORED, PokeCrypto.SIZE_8APARTY,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -203,6 +204,7 @@ namespace PKHeX.Core
|
|||
|
||||
internal const string ExtensionPB7 = "pb7";
|
||||
internal const string ExtensionPB8 = "pb8";
|
||||
internal const string ExtensionPA8 = "pa8";
|
||||
|
||||
/// <summary>
|
||||
/// Gets an array of valid <see cref="PKM"/> file extensions.
|
||||
|
@ -227,6 +229,8 @@ namespace PKHeX.Core
|
|||
result.Add(ExtensionPB7); // let's go
|
||||
if (maxGeneration >= 8)
|
||||
result.Add(ExtensionPB8); // Brilliant Diamond & Shining Pearl
|
||||
if (maxGeneration >= 8)
|
||||
result.Add(ExtensionPA8); // Legends: Arceus
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
|
|
@ -45,6 +45,10 @@ namespace PKHeX.Core
|
|||
internal const int SIZE_8PARTY = SIZE_8STORED + 0x10; // 0x158
|
||||
internal const int SIZE_8BLOCK = 80; // 0x50
|
||||
|
||||
internal const int SIZE_8ASTORED = 8 + (4 * SIZE_8ABLOCK); // 0x168
|
||||
internal const int SIZE_8APARTY = SIZE_8ASTORED + 0x10; // 0x178
|
||||
internal const int SIZE_8ABLOCK = 88; // 0x58
|
||||
|
||||
/// <summary>
|
||||
/// Positions for shuffling.
|
||||
/// </summary>
|
||||
|
@ -133,6 +137,21 @@ namespace PKHeX.Core
|
|||
return ShuffleArray(ekm, sv, SIZE_8BLOCK);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a Gen8 pkm byte array.
|
||||
/// </summary>
|
||||
/// <param name="ekm">Encrypted Pokémon data.</param>
|
||||
/// <returns>Decrypted Pokémon data.</returns>
|
||||
/// <returns>Encrypted Pokémon data.</returns>
|
||||
public static byte[] DecryptArray8A(Span<byte> ekm)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(ekm);
|
||||
uint sv = pv >> 13 & 31;
|
||||
|
||||
CryptPKM(ekm, pv, SIZE_8ABLOCK);
|
||||
return ShuffleArray(ekm, sv, SIZE_8ABLOCK);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a Gen8 pkm byte array.
|
||||
/// </summary>
|
||||
|
@ -147,6 +166,20 @@ namespace PKHeX.Core
|
|||
return ekm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a Gen8 pkm byte array.
|
||||
/// </summary>
|
||||
/// <param name="pkm">Decrypted Pokémon data.</param>
|
||||
public static byte[] EncryptArray8A(Span<byte> pkm)
|
||||
{
|
||||
uint pv = ReadUInt32LittleEndian(pkm);
|
||||
uint sv = pv >> 13 & 31;
|
||||
|
||||
byte[] ekm = ShuffleArray(pkm, blockPositionInvert[sv], SIZE_8ABLOCK);
|
||||
CryptPKM(ekm, pv, SIZE_8ABLOCK);
|
||||
return ekm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a 232 byte + party stat byte array.
|
||||
/// </summary>
|
||||
|
@ -390,8 +423,19 @@ namespace PKHeX.Core
|
|||
public static void DecryptIfEncrypted8(ref byte[] pkm)
|
||||
{
|
||||
var span = pkm.AsSpan();
|
||||
if (ReadUInt16LittleEndian(span[0x70..]) != 0 || ReadUInt16LittleEndian(span[0xC0..]) != 0)
|
||||
if (ReadUInt16LittleEndian(span[0x70..]) != 0 || ReadUInt16LittleEndian(span[0x110..]) != 0)
|
||||
pkm = DecryptArray8(span);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts the input <see cref="pkm"/> data into a new array if it is encrypted, and updates the reference.
|
||||
/// </summary>
|
||||
/// <remarks>Generation 8 Format encryption check</remarks>
|
||||
public static void DecryptIfEncrypted8A(ref byte[] pkm)
|
||||
{
|
||||
var span = pkm.AsSpan();
|
||||
if (ReadUInt16LittleEndian(span[0x78..]) != 0 || ReadUInt16LittleEndian(span[0x128..]) != 0)
|
||||
pkm = DecryptArray8A(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
113
PKHeX.Core/PersonalInfo/PersonalInfoLA.cs
Normal file
113
PKHeX.Core/PersonalInfo/PersonalInfoLA.cs
Normal file
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using static System.Buffers.Binary.BinaryPrimitives;
|
||||
|
||||
namespace PKHeX.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="PersonalInfo"/> class with values from the <see cref="GameVersion.BDSP"/> games.
|
||||
/// </summary>
|
||||
public sealed class PersonalInfoLA : PersonalInfo
|
||||
{
|
||||
public const int SIZE = 0xB0;
|
||||
|
||||
public PersonalInfoLA(byte[] data) : base(data)
|
||||
{
|
||||
// TM/TR and Special Tutors are inaccessible; dummy data.
|
||||
|
||||
// 0xA8-0xAF are armor type tutors, one bit for each type
|
||||
var moveShop = new bool[Legal.MoveShop8_LA.Length];
|
||||
for (int i = 0; i < moveShop.Length; i++)
|
||||
moveShop[i] = FlagUtil.GetFlag(Data, 0xA8 + (i >> 3), i);
|
||||
SpecialTutors = new[]
|
||||
{
|
||||
moveShop,
|
||||
};
|
||||
}
|
||||
|
||||
public override byte[] Write()
|
||||
{
|
||||
for (int i = 0; i < SpecialTutors[0].Length; i++)
|
||||
FlagUtil.SetFlag(Data, 0xA8 + (i >> 3), i, SpecialTutors[0][i]);
|
||||
return Data;
|
||||
}
|
||||
|
||||
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 => ReadUInt16LittleEndian(Data.AsSpan(0x0A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x0A), (ushort)value); }
|
||||
public override int EV_HP { get => EVYield >> 0 & 0x3; set => EVYield = (EVYield & ~(0x3 << 0)) | (value & 0x3) << 0; }
|
||||
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 => ReadInt16LittleEndian(Data.AsSpan(0x0C)); set => WriteInt16LittleEndian(Data.AsSpan(0x0C), (short)value); }
|
||||
public int Item2 { get => ReadInt16LittleEndian(Data.AsSpan(0x0E)); set => WriteInt16LittleEndian(Data.AsSpan(0x0E), (short)value); }
|
||||
public int Item3 { get => ReadInt16LittleEndian(Data.AsSpan(0x10)); set => WriteInt16LittleEndian(Data.AsSpan(0x10), (short)value); }
|
||||
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 => ReadUInt16LittleEndian(Data.AsSpan(0x18)); set => WriteUInt16LittleEndian(Data.AsSpan(0x18), (ushort)value); }
|
||||
public int Ability2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x1A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1A), (ushort)value); }
|
||||
public int AbilityH { get => ReadUInt16LittleEndian(Data.AsSpan(0x1C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1C), (ushort)value); }
|
||||
public override int EscapeRate { get => 0; set { } } // moved?
|
||||
protected internal override int FormStatsIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); }
|
||||
public override int FormSprite { get => ReadUInt16LittleEndian(Data.AsSpan(0x1E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x1E), (ushort)value); } // ???
|
||||
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 => ((Data[0x21] >> 7) & 1) == 1; set => Data[0x21] = (byte)((Data[0x21] & ~0x80) | (value ? 0x80 : 0)); }
|
||||
public override int BaseEXP { get => ReadUInt16LittleEndian(Data.AsSpan(0x22)); set => WriteUInt16LittleEndian(Data.AsSpan(0x22), (ushort)value); }
|
||||
public override int Height { get => ReadUInt16LittleEndian(Data.AsSpan(0x24)); set => WriteUInt16LittleEndian(Data.AsSpan(0x24), (ushort)value); }
|
||||
public override int Weight { get => ReadUInt16LittleEndian(Data.AsSpan(0x26)); set => WriteUInt16LittleEndian(Data.AsSpan(0x26), (ushort)value); }
|
||||
|
||||
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;
|
||||
|
||||
public int HatchSpecies { get => ReadUInt16LittleEndian(Data.AsSpan(0x56)); set => WriteUInt16LittleEndian(Data.AsSpan(0x56), (ushort)value); }
|
||||
public int HatchFormIndex { get => ReadUInt16LittleEndian(Data.AsSpan(0x58)); set => WriteUInt16LittleEndian(Data.AsSpan(0x58), (ushort)value); } // local region base form
|
||||
public ushort RegionalFlags { get => ReadUInt16LittleEndian(Data.AsSpan(0x5A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5A), value); }
|
||||
public bool IsRegionalForm { get => (RegionalFlags & 1) == 1; set => RegionalFlags = (ushort)((RegionalFlags & 0xFFFE) | (value ? 1 : 0)); }
|
||||
public int Species { get => ReadUInt16LittleEndian(Data.AsSpan(0x5C)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5C), (ushort)value); }
|
||||
public int Form { get => ReadUInt16LittleEndian(Data.AsSpan(0x5E)); set => WriteUInt16LittleEndian(Data.AsSpan(0x5E), (ushort)value); }
|
||||
public int DexIndexHisui { get => ReadUInt16LittleEndian(Data.AsSpan(0x60)); set => WriteUInt16LittleEndian(Data.AsSpan(0x60), (ushort)value); }
|
||||
public int DexIndexLocal1 { get => ReadUInt16LittleEndian(Data.AsSpan(0x62)); set => WriteUInt16LittleEndian(Data.AsSpan(0x62), (ushort)value); }
|
||||
public int DexIndexLocal2 { get => ReadUInt16LittleEndian(Data.AsSpan(0x64)); set => WriteUInt16LittleEndian(Data.AsSpan(0x64), (ushort)value); }
|
||||
public int DexIndexLocal3 { get => ReadUInt16LittleEndian(Data.AsSpan(0x66)); set => WriteUInt16LittleEndian(Data.AsSpan(0x66), (ushort)value); }
|
||||
public int DexIndexLocal4 { get => ReadUInt16LittleEndian(Data.AsSpan(0x68)); set => WriteUInt16LittleEndian(Data.AsSpan(0x68), (ushort)value); }
|
||||
public int DexIndexLocal5 { get => ReadUInt16LittleEndian(Data.AsSpan(0x6A)); set => WriteUInt16LittleEndian(Data.AsSpan(0x6A), (ushort)value); }
|
||||
}
|
Loading…
Reference in a new issue