2018-11-28 06:05:36 +00:00
|
|
|
using System;
|
|
|
|
|
2018-11-14 03:14:23 +00:00
|
|
|
namespace PKHeX.Core
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// Beluga specific Dex manipulator, slightly modified from Gen7.
|
|
|
|
/// </summary>
|
2020-09-07 20:51:13 +00:00
|
|
|
public sealed class Zukan7b : Zukan7
|
2018-11-14 03:14:23 +00:00
|
|
|
{
|
PKHeX.Core Nullable cleanup (#2401)
* Handle some nullable cases
Refactor MysteryGift into a second abstract class (backed by a byte array, or fake data)
Make some classes have explicit constructors instead of { } initialization
* Handle bits more obviously without null
* Make SaveFile.BAK explicitly readonly again
* merge constructor methods to have readonly fields
* Inline some properties
* More nullable handling
* Rearrange box actions
define straightforward classes to not have any null properties
* Make extrabyte reference array immutable
* Move tooltip creation to designer
* Rearrange some logic to reduce nesting
* Cache generated fonts
* Split mystery gift album purpose
* Handle more tooltips
* Disallow null setters
* Don't capture RNG object, only type enum
* Unify learnset objects
Now have readonly properties which are never null
don't new() empty learnsets (>800 Learnset objects no longer created,
total of 2400 objects since we also new() a move & level array)
optimize g1/2 reader for early abort case
* Access rewrite
Initialize blocks in a separate object, and get via that object
removes a couple hundred "might be null" warnings since blocks are now readonly getters
some block references have been relocated, but interfaces should expose all that's needed
put HoF6 controls in a groupbox, and disable
* Readonly personal data
* IVs non nullable for mystery gift
* Explicitly initialize forced encounter moves
* Make shadow objects readonly & non-null
Put murkrow fix in binary data resource, instead of on startup
* Assign dex form fetch on constructor
Fixes legality parsing edge cases
also handle cxd parse for valid; exit before exception is thrown in FrameGenerator
* Remove unnecessary null checks
* Keep empty value until init
SetPouch sets the value to an actual one during load, but whatever
* Readonly team lock data
* Readonly locks
Put locked encounters at bottom (favor unlocked)
* Mail readonly data / offset
Rearrange some call flow and pass defaults
Add fake classes for SaveDataEditor mocking
Always party size, no need to check twice in stat editor
use a fake save file as initial data for savedata editor, and for
gamedata (wow i found a usage)
constrain eventwork editor to struct variable types (uint, int, etc),
thus preventing null assignment errors
2019-10-17 01:47:31 +00:00
|
|
|
public Zukan7b(SAV7b sav, int dex, int langflag) : base(sav, dex, langflag)
|
2018-11-14 03:14:23 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void SetDex(PKM pkm)
|
|
|
|
{
|
2020-12-11 04:42:30 +00:00
|
|
|
if (!TryGetSizeEntryIndex(pkm.Species, pkm.Form, out _))
|
2018-11-14 03:14:23 +00:00
|
|
|
return;
|
2018-11-28 06:05:36 +00:00
|
|
|
SetSizeData((PB7)pkm);
|
2018-11-14 03:14:23 +00:00
|
|
|
base.SetDex(pkm);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void SetDex(int species, int bit, int form, int gender, bool shiny, int lang)
|
|
|
|
{
|
|
|
|
if (IsBuddy(species, form))
|
|
|
|
form = 0;
|
|
|
|
base.SetDex(species, bit, form, gender, shiny, lang);
|
|
|
|
}
|
|
|
|
|
2019-12-09 01:39:19 +00:00
|
|
|
private static bool IsBuddy(int species, int form) => (species == (int)Species.Pikachu && form == 8) || (species == (int)Species.Eevee && form == 1);
|
2018-11-14 03:14:23 +00:00
|
|
|
|
2018-11-30 05:40:20 +00:00
|
|
|
public const int DefaultEntryValue = 0x7F;
|
|
|
|
|
|
|
|
public bool GetSizeData(DexSizeType group, int species, int form, out int height, out int weight)
|
|
|
|
{
|
|
|
|
height = weight = DefaultEntryValue;
|
|
|
|
if (TryGetSizeEntryIndex(species, form, out var index))
|
|
|
|
return GetSizeData(group, index, out height, out weight);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool GetSizeData(DexSizeType group, int index, out int height, out int weight)
|
|
|
|
{
|
|
|
|
var ofs = GetDexSizeOffset(group, index);
|
|
|
|
height = BitConverter.ToUInt16(SAV.Data, ofs) >> 1;
|
|
|
|
weight = BitConverter.ToUInt16(SAV.Data, ofs + 2);
|
|
|
|
return !IsEntryUnset(height, weight);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static bool IsEntryUnset(int height, int weight) => height == DefaultEntryValue && weight == DefaultEntryValue;
|
|
|
|
|
2018-11-28 06:05:36 +00:00
|
|
|
private void SetSizeData(PB7 pkm)
|
2018-11-14 03:14:23 +00:00
|
|
|
{
|
|
|
|
int species = pkm.Species;
|
2020-12-11 04:42:30 +00:00
|
|
|
int form = pkm.Form;
|
2018-11-30 05:40:20 +00:00
|
|
|
if (!TryGetSizeEntryIndex(species, form, out int index))
|
2018-11-14 03:14:23 +00:00
|
|
|
return;
|
|
|
|
|
2018-11-28 06:05:36 +00:00
|
|
|
if (Math.Round(pkm.HeightAbsolute) < pkm.PersonalInfo.Height) // possible minimum height
|
|
|
|
{
|
2018-11-30 05:40:20 +00:00
|
|
|
int ofs = GetDexSizeOffset(DexSizeType.MinHeight, index);
|
2018-11-28 06:05:36 +00:00
|
|
|
var minHeight = BitConverter.ToUInt16(SAV.Data, ofs) >> 1;
|
|
|
|
var calcHeight = PB7.GetHeightAbsolute(pkm.PersonalInfo, minHeight);
|
|
|
|
if (Math.Round(pkm.HeightAbsolute) < Math.Round(calcHeight) || BitConverter.ToUInt32(SAV.Data, ofs) == 0x007F00FE) // unset
|
2018-11-30 05:40:20 +00:00
|
|
|
SetSizeData(pkm, DexSizeType.MinHeight);
|
2018-11-28 06:05:36 +00:00
|
|
|
}
|
|
|
|
else if (Math.Round(pkm.HeightAbsolute) > pkm.PersonalInfo.Height) // possible maximum height
|
|
|
|
{
|
2018-11-30 05:40:20 +00:00
|
|
|
int ofs = GetDexSizeOffset(DexSizeType.MaxHeight, index);
|
2018-11-28 06:05:36 +00:00
|
|
|
var maxHeight = BitConverter.ToUInt16(SAV.Data, ofs) >> 1;
|
|
|
|
var calcHeight = PB7.GetHeightAbsolute(pkm.PersonalInfo, maxHeight);
|
|
|
|
if (Math.Round(pkm.HeightAbsolute) > Math.Round(calcHeight) || BitConverter.ToUInt32(SAV.Data, ofs) == 0x007F00FE) // unset
|
2018-11-30 05:40:20 +00:00
|
|
|
SetSizeData(pkm, DexSizeType.MaxHeight);
|
2018-11-28 06:05:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Math.Round(pkm.WeightAbsolute) < pkm.PersonalInfo.Weight) // possible minimum weight
|
|
|
|
{
|
2018-11-30 05:40:20 +00:00
|
|
|
int ofs = GetDexSizeOffset(DexSizeType.MinWeight, index);
|
2018-11-28 06:05:36 +00:00
|
|
|
var minWeight = BitConverter.ToUInt16(SAV.Data, ofs + 2);
|
|
|
|
var minHeight = BitConverter.ToUInt16(SAV.Data, ofs) >> 1;
|
|
|
|
var calcWeight = PB7.GetWeightAbsolute(pkm.PersonalInfo, minHeight, minWeight);
|
|
|
|
if (Math.Round(pkm.WeightAbsolute) < Math.Round(calcWeight) || BitConverter.ToUInt32(SAV.Data, ofs) == 0x007F00FE) // unset
|
2018-11-30 05:40:20 +00:00
|
|
|
SetSizeData(pkm, DexSizeType.MinWeight);
|
2018-11-28 06:05:36 +00:00
|
|
|
}
|
|
|
|
else if (Math.Round(pkm.WeightAbsolute) > pkm.PersonalInfo.Weight) // possible maximum weight
|
|
|
|
{
|
2018-11-30 05:40:20 +00:00
|
|
|
int ofs = GetDexSizeOffset(DexSizeType.MaxWeight, index);
|
2018-11-28 06:05:36 +00:00
|
|
|
var maxWeight = BitConverter.ToUInt16(SAV.Data, ofs + 2);
|
|
|
|
var maxHeight = BitConverter.ToUInt16(SAV.Data, ofs) >> 1;
|
|
|
|
var calcWeight = PB7.GetWeightAbsolute(pkm.PersonalInfo, maxHeight, maxWeight);
|
|
|
|
if (Math.Round(pkm.WeightAbsolute) > Math.Round(calcWeight) || BitConverter.ToUInt32(SAV.Data, ofs) == 0x007F00FE) // unset
|
2018-11-30 05:40:20 +00:00
|
|
|
SetSizeData(pkm, DexSizeType.MaxWeight);
|
2018-11-28 06:05:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-08 01:40:09 +00:00
|
|
|
private static int GetDexSizeOffset(DexSizeType group, int index) => 0x3978 + (index * 6) + ((int)group * 0x45C); // blockofs + 0xF78 + ([186*6]*n) + x*6
|
2018-11-28 06:05:36 +00:00
|
|
|
|
2018-11-30 05:40:20 +00:00
|
|
|
private void SetSizeData(PB7 pkm, DexSizeType group)
|
2018-11-28 06:05:36 +00:00
|
|
|
{
|
|
|
|
var tree = EvolutionTree.GetEvolutionTree(pkm, 7);
|
|
|
|
int species = pkm.Species;
|
2020-12-11 04:42:30 +00:00
|
|
|
int form = pkm.Form;
|
2018-11-28 06:05:36 +00:00
|
|
|
|
2018-11-30 05:40:20 +00:00
|
|
|
int height = pkm.HeightScalar;
|
|
|
|
int weight = pkm.WeightScalar;
|
|
|
|
|
2018-11-28 06:05:36 +00:00
|
|
|
// update for all species in potential lineage
|
|
|
|
var allspec = tree.GetEvolutionsAndPreEvolutions(species, form);
|
2020-11-17 22:23:15 +00:00
|
|
|
foreach (var sf in allspec)
|
|
|
|
{
|
|
|
|
var s = sf & 0x7FF;
|
|
|
|
var f = sf >> 11;
|
|
|
|
SetSizeData(group, s, f, height, weight);
|
|
|
|
}
|
2018-11-30 05:40:20 +00:00
|
|
|
}
|
2018-11-28 06:05:36 +00:00
|
|
|
|
2018-11-30 05:40:20 +00:00
|
|
|
public void SetSizeData(DexSizeType group, int species, int form, int height, int weight)
|
|
|
|
{
|
|
|
|
if (TryGetSizeEntryIndex(species, form, out var index))
|
|
|
|
SetSizeData(group, index, height, weight);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SetSizeData(DexSizeType group, int index, int height, int weight)
|
|
|
|
{
|
|
|
|
var ofs = GetDexSizeOffset(group, index);
|
|
|
|
BitConverter.GetBytes((ushort)(height << 1)).CopyTo(SAV.Data, ofs);
|
|
|
|
BitConverter.GetBytes((ushort)(weight)).CopyTo(SAV.Data, ofs + 2);
|
2018-11-14 03:14:23 +00:00
|
|
|
}
|
|
|
|
|
2018-11-30 05:40:20 +00:00
|
|
|
public static bool TryGetSizeEntryIndex(int species, int form, out int index)
|
2018-11-14 03:14:23 +00:00
|
|
|
{
|
|
|
|
index = -1;
|
|
|
|
if (form == 0 && species <= 151)
|
|
|
|
{
|
|
|
|
index = species - 1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < SizeDexInfoTable.Length; i += 3)
|
|
|
|
{
|
|
|
|
if (SizeDexInfoTable[i] != species)
|
|
|
|
continue;
|
|
|
|
if (SizeDexInfoTable[i + 1] != form)
|
|
|
|
continue;
|
|
|
|
index = SizeDexInfoTable[i + 2];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-11-30 05:40:20 +00:00
|
|
|
private static readonly ushort[] SizeDexInfoTable =
|
2018-11-14 03:14:23 +00:00
|
|
|
{
|
|
|
|
// spec, form, index
|
|
|
|
808, 0, 151,
|
|
|
|
809, 0, 152,
|
|
|
|
|
|
|
|
003, 1, 153,
|
|
|
|
006, 1, 154,
|
|
|
|
006, 2, 155,
|
|
|
|
009, 1, 156,
|
|
|
|
015, 1, 157,
|
|
|
|
018, 1, 158,
|
|
|
|
019, 1, 159,
|
|
|
|
020, 1, 160,
|
|
|
|
026, 1, 161,
|
|
|
|
027, 1, 162,
|
|
|
|
028, 1, 163,
|
|
|
|
037, 1, 164,
|
|
|
|
038, 1, 165,
|
|
|
|
050, 1, 166,
|
|
|
|
051, 1, 167,
|
|
|
|
052, 1, 168,
|
|
|
|
053, 1, 169,
|
|
|
|
065, 1, 170,
|
|
|
|
074, 1, 171,
|
|
|
|
075, 1, 172,
|
|
|
|
076, 1, 173,
|
|
|
|
080, 1, 174,
|
|
|
|
088, 1, 175,
|
|
|
|
089, 1, 176,
|
|
|
|
094, 1, 177,
|
|
|
|
103, 1, 178,
|
|
|
|
105, 1, 179,
|
|
|
|
115, 1, 180,
|
|
|
|
127, 1, 181,
|
|
|
|
130, 1, 182,
|
|
|
|
142, 1, 183,
|
|
|
|
150, 1, 184,
|
|
|
|
150, 2, 185,
|
|
|
|
};
|
|
|
|
|
|
|
|
protected override bool GetSaneFormsToIterate(int species, out int formStart, out int formEnd, int formIn)
|
|
|
|
{
|
|
|
|
switch (species)
|
|
|
|
{
|
2020-12-25 18:58:33 +00:00
|
|
|
// Totems with Alolan Forms
|
|
|
|
case 020 or 105: // Raticate or Marowak
|
2018-11-14 03:14:23 +00:00
|
|
|
formStart = 0;
|
|
|
|
formEnd = 1;
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
2019-03-16 19:01:21 +00:00
|
|
|
int count = DexFormUtil.GetDexFormCountGG(species);
|
2018-11-14 03:14:23 +00:00
|
|
|
formStart = formEnd = 0;
|
|
|
|
return count < formIn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-30 05:40:20 +00:00
|
|
|
|
|
|
|
public enum DexSizeType
|
|
|
|
{
|
|
|
|
MinHeight,
|
|
|
|
MaxHeight,
|
|
|
|
MinWeight,
|
|
|
|
MaxWeight,
|
|
|
|
}
|
2018-11-14 03:14:23 +00:00
|
|
|
}
|