Minor refactoring

Change Ability array to IReadOnlyList, add method to check ability index in personal data
Suppress some message warnings
Change EvolutionChain short-circuit for VC to jump from gen6 directly down to gen2. There aren't any notradeback 1 situations, so a notradeback1 will always start with g=1, so no need for the other if-continue.

Simplify pk5 conversion
This commit is contained in:
Kurt 2020-09-06 10:53:13 -07:00
parent 39e5c9000d
commit cf9e5ec37f
34 changed files with 345 additions and 265 deletions

View file

@ -230,7 +230,13 @@ namespace PKHeX.Core
if (tmp != ModifyResult.Modified)
result = tmp;
}
catch (Exception ex) { Debug.WriteLine(MsgBEModifyFail + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue); }
#pragma warning disable CA1031 // Do not catch general exception types
// Swallow any error because this can be malformed user input.
catch (Exception ex)
#pragma warning restore CA1031 // Do not catch general exception types
{
Debug.WriteLine(MsgBEModifyFail + " " + ex.Message, cmd.PropertyName, cmd.PropertyValue);
}
}
return result;
}

View file

@ -79,8 +79,7 @@ namespace PKHeX.Core
{
if (abil < 0)
return;
var abilities = pk.PersonalInfo.Abilities;
int index = Array.IndexOf(abilities, abil);
var index = pk.PersonalInfo.GetAbilityIndex(abil);
index = Math.Max(0, index);
pk.SetAbilityIndex(index);
}

View file

@ -17,8 +17,7 @@ namespace PKHeX.Core
GameInfo.FilteredSources = new FilteredGameDataSource(sav, GameInfo.Sources, hax);
// Update Legality Analysis strings
LegalityAnalysis.MoveStrings = str.movelist;
LegalityAnalysis.SpeciesStrings = str.specieslist;
LegalityAnalysis.ChangeLocalizationStrings(str.movelist, str.specieslist);
// Update Legality Strings
Task.Run(() =>

View file

@ -3,7 +3,9 @@
/// <summary>
/// <see cref="GameVersion"/> analogues used by Colosseum/XD instead of the main-series values.
/// </summary>
#pragma warning disable CA1027 // Mark enums with FlagsAttribute
public enum GCVersion : byte
#pragma warning restore CA1027 // Mark enums with FlagsAttribute
{
None = 0,
FR = 1,

View file

@ -4,6 +4,7 @@
/// Gender a <see cref="PKM"/> can have
/// </summary>
/// <remarks><see cref="Random"/> provided to function for Encounter template values</remarks>
#pragma warning disable CA1027 // Mark enums with FlagsAttribute
public enum Gender : byte
{
Male = 0,

View file

@ -33,8 +33,14 @@ namespace PKHeX.Core
private static readonly TransferVerifier Transfer = new TransferVerifier();
private static readonly MarkVerifier Mark = new MarkVerifier();
public static string[] MoveStrings { internal get; set; } = Util.GetMovesList(GameLanguage.DefaultLanguage);
public static string[] SpeciesStrings { internal get; set; } = Util.GetSpeciesList(GameLanguage.DefaultLanguage);
internal static IEnumerable<string> GetMoveNames(IEnumerable<int> moves) => moves.Select(m => (uint)m >= MoveStrings.Length ? L_AError : MoveStrings[m]);
internal static IReadOnlyList<string> MoveStrings = Util.GetMovesList(GameLanguage.DefaultLanguage);
internal static IReadOnlyList<string> SpeciesStrings = Util.GetSpeciesList(GameLanguage.DefaultLanguage);
internal static IEnumerable<string> GetMoveNames(IEnumerable<int> moves) => moves.Select(m => (uint)m >= MoveStrings.Count ? L_AError : MoveStrings[m]);
public static void ChangeLocalizationStrings(IReadOnlyList<string> moves, IReadOnlyList<string> species)
{
SpeciesStrings = species;
MoveStrings = moves;
}
}
}

View file

@ -89,9 +89,9 @@
return num >> 1;
var abils = pkPersonalInfo.Abilities;
if (num == -1 && abils.Length > 2 && abils[2] == Ability) // hidden allowed
if (abils.Count > 2 && abils[2] == Ability && num == -1) // hidden allowed
return 2;
if (abils[0] == Ability)
if (abils.Count > 0 && abils[0] == Ability)
return 0;
return 1;
}
@ -102,7 +102,7 @@
return type;
var abils = pkPersonalInfo.Abilities;
if (type == 4 && abils.Length > 2 && abils[2] == Ability) // hidden allowed
if (type == 4 && abils.Count > 2 && abils[2] == Ability) // hidden allowed
return 2;
if (abils[0] == Ability)
return 0;

View file

@ -8,6 +8,7 @@ namespace PKHeX.Core
/// <remarks>
/// Different from <see cref="EncounterType"/>, this corresponds to the method that the <see cref="IEncounterable"/> may be encountered.</remarks>
[Flags]
#pragma warning disable RCS1191 // Declare enum value as combination of names.
public enum SlotType : byte
{
/// <summary>

View file

@ -52,13 +52,8 @@ namespace PKHeX.Core
bool noxfrDecremented = true;
for (int g = GensEvoChains.Length - 1; g >= mingen; g--)
{
if (pkGen <= 2)
{
if (3 <= g && g <= 6)
continue;
if (g == 2 && pkm.Gen1_NotTradeback)
continue;
}
if (pkGen <= 2 && g == 6)
g = 2;
if (g <= 4 && pkm.Format > 2 && pkm.Format > g && !pkm.HasOriginalMetLocation && lvl > pkm.Met_Level)
{

View file

@ -261,11 +261,11 @@ namespace PKHeX.Core
return GameData.GetLearnsets(ver)[index];
}
public static IEnumerable<int> GetMovesLevelUp(PKM pkm, int species, int minlvlG1, int minlvlG2, int lvl, int form, GameVersion version, bool MoveReminder, int Generation)
public static IEnumerable<int> GetMovesLevelUp(PKM pkm, int species, int minlvlG1, int minlvlG2, int lvl, int form, GameVersion version, bool MoveReminder, int generation)
{
if (pkm.IsMovesetRestricted(Generation))
if (pkm.IsMovesetRestricted(generation))
version = (GameVersion)pkm.Version;
return Generation switch
return generation switch
{
1 => GetMovesLevelUp1(species, form, lvl, minlvlG1, version),
2 => GetMovesLevelUp2(species, form, lvl, minlvlG2, pkm.Korean, pkm.LearnMovesNew2Disallowed(), version),

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core
@ -29,15 +28,16 @@ namespace PKHeX.Core
private CheckResult VerifyAbility(LegalityAnalysis data)
{
var pkm = data.pkm;
var abilities = pkm.PersonalInfo.Abilities;
var pi = data.PersonalInfo;
// Check ability is possible (within bounds)
int ability = pkm.Ability;
int abilval = Array.IndexOf(abilities, ability);
int abilval = pi.GetAbilityIndex(ability);
if (abilval < 0)
return GetInvalid(LAbilityUnexpected);
var enc = data.EncounterMatch;
var abilities = data.PersonalInfo.Abilities;
if (enc is MysteryGift g && g.Format >= 4)
return VerifyAbilityMG(data, g, abilities);
@ -51,7 +51,7 @@ namespace PKHeX.Core
// Check AbilityNumber points to ability
int an = num >> 1;
if (an >= abilities.Length || abilities[an] != ability)
if (an >= abilities.Count || abilities[an] != ability)
return GetInvalid(LAbilityMismatchFlag);
return VerifyAbility(data, abilities, abilval);

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -16,12 +15,16 @@ namespace PKHeX.Core
/// <param name="lines">Array of strings that are tab separated with Property Name, \t, and Display Name.</param>
public static void ResetDictionary(IEnumerable<string> lines)
{
// Don't clear existing keys on reset; only update.
// A language will have the same keys (hopefully), only with differing values.
foreach (var line in lines)
{
string[] split = line.Split('\t');
if (split.Length != 2)
var index = line.IndexOf('\t');
if (index < 0)
continue;
RibbonNames[split[0]] = split[1];
var name = line.Substring(0, index);
var text = line.Substring(index + 1);
RibbonNames[name] = text;
}
}
@ -32,8 +35,10 @@ namespace PKHeX.Core
/// <returns>Ribbon display name</returns>
public static string GetName(string propertyName)
{
// Throw an exception with the requested property name as the message, rather than an ambiguous "key not present" message.
// We should ALWAYS have the key present as the input arguments are not user-defined, rather, they are from PKM property names.
if (!RibbonNames.TryGetValue(propertyName, out string value))
throw new ArgumentException(propertyName);
throw new KeyNotFoundException(propertyName);
return value;
}
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
@ -260,7 +259,11 @@ namespace PKHeX.Core
public override int Stat_SPE { get => BitConverter.ToUInt16(Data, 0x96); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x96); }
public override int Stat_SPA { get => BitConverter.ToUInt16(Data, 0x98); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x98); }
public override int Stat_SPD { get => BitConverter.ToUInt16(Data, 0x9A); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x9A); }
public byte[] HeldMailData { get => Data.Skip(0x9C).Take(0x38).ToArray(); set => value.CopyTo(Data, 0x9C); }
public byte[] GetHeldMailData() => Data.Slice(0x9C, 0x38);
public void SetHeldMailData(byte[] value) => value.CopyTo(Data, 0x9C);
public void ClearHeldMailData() => Array.Clear(Data, 0x9C, 0x38);
#endregion
// Generated Attributes
@ -324,171 +327,133 @@ namespace PKHeX.Core
SID = SID,
EXP = EXP,
PID = PID,
Ability = Ability
Ability = Ability,
AbilityNumber = 1 << CalculateAbilityIndex(),
Markings = Markings,
Language = Math.Max((int)LanguageID.Japanese, Language), // Hacked or Bad IngameTrade (Japanese B/W)
CNT_Cool = CNT_Cool,
CNT_Beauty = CNT_Beauty,
CNT_Cute = CNT_Cute,
CNT_Smart = CNT_Smart,
CNT_Tough = CNT_Tough,
CNT_Sheen = CNT_Sheen,
// Cap EVs
EV_HP = Math.Min(EV_HP, 252),
EV_ATK = Math.Min(EV_ATK, 252),
EV_DEF = Math.Min(EV_DEF, 252),
EV_SPA = Math.Min(EV_SPA, 252),
EV_SPD = Math.Min(EV_SPD, 252),
EV_SPE = Math.Min(EV_SPE, 252),
Move1 = Move1,
Move2 = Move2,
Move3 = Move3,
Move4 = Move4,
Move1_PPUps = Move1_PPUps,
Move2_PPUps = Move2_PPUps,
Move3_PPUps = Move3_PPUps,
Move4_PPUps = Move4_PPUps,
IV_HP = IV_HP,
IV_ATK = IV_ATK,
IV_DEF = IV_DEF,
IV_SPA = IV_SPA,
IV_SPD = IV_SPD,
IV_SPE = IV_SPE,
IsEgg = IsEgg,
IsNicknamed = IsNicknamed,
FatefulEncounter = FatefulEncounter,
Gender = Gender,
AltForm = AltForm,
Nature = Nature,
Version = Version,
OT_Name = OT_Name,
// Dates are kept upon transfer
MetDate = MetDate,
EggMetDate = EggMetDate,
// Locations are kept upon transfer
Met_Location = Met_Location,
Egg_Location = Egg_Location,
PKRS_Strain = PKRS_Strain,
PKRS_Days = PKRS_Days,
Ball = Ball,
// OT Gender & Encounter Level
Met_Level = Met_Level,
OT_Gender = OT_Gender,
EncounterType = EncounterType,
// Fill the Ribbon Counter Bytes
RibbonCountMemoryContest = CountContestRibbons(),
RibbonCountMemoryBattle = CountBattleRibbons(),
// Copy Ribbons to their new locations.
RibbonChampionG3Hoenn = RibbonChampionG3Hoenn,
RibbonChampionSinnoh = RibbonChampionSinnoh,
RibbonEffort = RibbonEffort,
RibbonAlert = RibbonAlert,
RibbonShock = RibbonShock,
RibbonDowncast = RibbonDowncast,
RibbonCareless = RibbonCareless,
RibbonRelax = RibbonRelax,
RibbonSnooze = RibbonSnooze,
RibbonSmile = RibbonSmile,
RibbonGorgeous = RibbonGorgeous,
RibbonRoyal = RibbonRoyal,
RibbonGorgeousRoyal = RibbonGorgeousRoyal,
RibbonArtist = RibbonArtist,
RibbonFootprint = RibbonFootprint,
RibbonRecord = RibbonRecord,
RibbonLegend = RibbonLegend,
RibbonCountry = RibbonCountry,
RibbonNational = RibbonNational,
RibbonEarth = RibbonEarth,
RibbonWorld = RibbonWorld,
RibbonClassic = RibbonClassic,
RibbonPremier = RibbonPremier,
RibbonEvent = RibbonEvent,
RibbonBirthday = RibbonBirthday,
RibbonSpecial = RibbonSpecial,
RibbonSouvenir = RibbonSouvenir,
RibbonWishing = RibbonWishing,
RibbonChampionBattle = RibbonChampionBattle,
RibbonChampionRegional = RibbonChampionRegional,
RibbonChampionNational = RibbonChampionNational,
RibbonChampionWorld = RibbonChampionWorld,
// Write the Memories, Friendship, and Origin!
CurrentHandler = 1,
HT_Name = PKMConverter.OT_Name,
HT_Gender = PKMConverter.OT_Gender,
HT_Intensity = 1,
HT_Memory = 4,
HT_Feeling = Memories.GetRandomFeeling(4),
};
int[] abilities = PersonalInfo.Abilities;
int abilval = Array.IndexOf(abilities, Ability);
if (abilval >= 0 && abilities[abilval] == abilities[2] && HiddenAbility)
abilval = 2; // hidden ability shared with a regular ability
if (abilval >= 0)
{
pk6.AbilityNumber = 1 << abilval;
}
else // Fallback (shouldn't happen)
{
if (HiddenAbility) pk6.AbilityNumber = 4; // Hidden, else G5 or G3/4 correlation.
else pk6.AbilityNumber = Gen5 ? 1 << (int)(PID >> 16 & 1) : 1 << (int)(PID & 1);
}
pk6.Markings = Markings;
pk6.Language = Math.Max((int)LanguageID.Japanese, Language); // Hacked or Bad IngameTrade (Japanese B/W)
pk6.CNT_Cool = CNT_Cool;
pk6.CNT_Beauty = CNT_Beauty;
pk6.CNT_Cute = CNT_Cute;
pk6.CNT_Smart = CNT_Smart;
pk6.CNT_Tough = CNT_Tough;
pk6.CNT_Sheen = CNT_Sheen;
// Cap EVs
pk6.EV_HP = EV_HP > 252 ? 252 : EV_HP;
pk6.EV_ATK = EV_ATK > 252 ? 252 : EV_ATK;
pk6.EV_DEF = EV_DEF > 252 ? 252 : EV_DEF;
pk6.EV_SPA = EV_SPA > 252 ? 252 : EV_SPA;
pk6.EV_SPD = EV_SPD > 252 ? 252 : EV_SPD;
pk6.EV_SPE = EV_SPE > 252 ? 252 : EV_SPE;
pk6.Move1 = Move1;
pk6.Move2 = Move2;
pk6.Move3 = Move3;
pk6.Move4 = Move4;
pk6.Move1_PPUps = Move1_PPUps;
pk6.Move2_PPUps = Move2_PPUps;
pk6.Move3_PPUps = Move3_PPUps;
pk6.Move4_PPUps = Move4_PPUps;
// Fix PP
pk6.HealPP();
pk6.IV_HP = IV_HP;
pk6.IV_ATK = IV_ATK;
pk6.IV_DEF = IV_DEF;
pk6.IV_SPA = IV_SPA;
pk6.IV_SPD = IV_SPD;
pk6.IV_SPE = IV_SPE;
pk6.IsEgg = IsEgg;
pk6.IsNicknamed = IsNicknamed;
pk6.FatefulEncounter = FatefulEncounter;
pk6.Gender = Gender;
pk6.AltForm = AltForm;
pk6.Nature = Nature;
// Apply trash bytes for species name of current app language -- default to PKM's language if no match
int curLang = SpeciesName.GetSpeciesNameLanguage(Species, Nickname, Format);
pk6.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, curLang < 0 ? Language : curLang, pk6.Format);
if (IsNicknamed)
pk6.Nickname = Nickname;
pk6.Version = Version;
pk6.OT_Name = OT_Name;
// Dates are kept upon transfer
pk6.MetDate = MetDate;
pk6.EggMetDate = EggMetDate;
// Locations are kept upon transfer
pk6.Met_Location = Met_Location;
pk6.Egg_Location = Egg_Location;
pk6.PKRS_Strain = PKRS_Strain;
pk6.PKRS_Days = PKRS_Days;
pk6.Ball = Ball;
// OT Gender & Encounter Level
pk6.Met_Level = Met_Level;
pk6.OT_Gender = OT_Gender;
pk6.EncounterType = EncounterType;
// Ribbon Decomposer (Contest & Battle)
byte contestribbons = 0;
byte battleribbons = 0;
// Contest Ribbon Counter
for (int i = 0; i < 8; i++) // Sinnoh 3, Hoenn 1
{
if ((Data[0x60] >> i & 1) == 1) contestribbons++;
if (((Data[0x61] >> i) & 1) == 1) contestribbons++;
if (((Data[0x3C] >> i) & 1) == 1) contestribbons++;
if (((Data[0x3D] >> i) & 1) == 1) contestribbons++;
}
for (int i = 0; i < 4; i++) // Sinnoh 4, Hoenn 2
{
if (((Data[0x62] >> i) & 1) == 1) contestribbons++;
if (((Data[0x3E] >> i) & 1) == 1) contestribbons++;
}
// Battle Ribbon Counter
if (RibbonWinning) battleribbons++;
if (RibbonVictory) battleribbons++;
for (int i = 1; i < 7; i++) // Sinnoh Battle Ribbons
if (((Data[0x24] >> i) & 1) == 1) battleribbons++;
// Fill the Ribbon Counter Bytes
pk6.RibbonCountMemoryContest = contestribbons;
pk6.RibbonCountMemoryBattle = battleribbons;
// Copy Ribbons to their new locations.
pk6.RibbonChampionG3Hoenn = RibbonChampionG3Hoenn;
pk6.RibbonChampionSinnoh = RibbonChampionSinnoh;
pk6.RibbonEffort = RibbonEffort;
pk6.RibbonAlert = RibbonAlert;
pk6.RibbonShock = RibbonShock;
pk6.RibbonDowncast = RibbonDowncast;
pk6.RibbonCareless = RibbonCareless;
pk6.RibbonRelax = RibbonRelax;
pk6.RibbonSnooze = RibbonSnooze;
pk6.RibbonSmile = RibbonSmile;
pk6.RibbonGorgeous = RibbonGorgeous;
pk6.RibbonRoyal = RibbonRoyal;
pk6.RibbonGorgeousRoyal = RibbonGorgeousRoyal;
pk6.RibbonArtist = RibbonArtist;
pk6.RibbonFootprint = RibbonFootprint;
pk6.RibbonRecord = RibbonRecord;
pk6.RibbonLegend = RibbonLegend;
pk6.RibbonCountry = RibbonCountry;
pk6.RibbonNational = RibbonNational;
pk6.RibbonEarth = RibbonEarth;
pk6.RibbonWorld = RibbonWorld;
pk6.RibbonClassic = RibbonClassic;
pk6.RibbonPremier = RibbonPremier;
pk6.RibbonEvent = RibbonEvent;
pk6.RibbonBirthday = RibbonBirthday;
pk6.RibbonSpecial = RibbonSpecial;
pk6.RibbonSouvenir = RibbonSouvenir;
pk6.RibbonWishing = RibbonWishing;
pk6.RibbonChampionBattle = RibbonChampionBattle;
pk6.RibbonChampionRegional = RibbonChampionRegional;
pk6.RibbonChampionNational = RibbonChampionNational;
pk6.RibbonChampionWorld = RibbonChampionWorld;
// Write Transfer Location - location is dependent on 3DS system that transfers.
PKMConverter.SetConsoleRegionData3DS(pk6);
PKMConverter.SetFirstCountryRegion(pk6);
// Write the Memories, Friendship, and Origin!
pk6.CurrentHandler = 1;
pk6.HT_Name = PKMConverter.OT_Name;
pk6.HT_Gender = PKMConverter.OT_Gender;
pk6.HT_Intensity = 1;
pk6.HT_Memory = 4;
pk6.HT_Feeling = Memories.GetRandomFeeling(pk6.HT_Memory);
// Apply trash bytes for species name of current app language -- default to PKM's language if no match
int curLang = SpeciesName.GetSpeciesNameLanguage(Species, Nickname, 5);
if (curLang < 0)
curLang = Language;
pk6.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, curLang, 6);
if (IsNicknamed)
pk6.Nickname = Nickname;
// When transferred, friendship gets reset.
pk6.OT_Friendship = pk6.HT_Friendship = PersonalInfo.BaseFriendship;
@ -500,6 +465,9 @@ namespace PKHeX.Core
// HMs are not deleted 5->6, transfer away (but fix if blank spots?)
pk6.FixMoves();
// Fix PP
pk6.HealPP();
// Fix Name Strings
pk6.Nickname = StringConverter345.TransferGlyphs56(pk6.Nickname);
pk6.OT_Name = StringConverter345.TransferGlyphs56(pk6.OT_Name);
@ -509,5 +477,55 @@ namespace PKHeX.Core
return pk6; // Done!
}
private byte CountBattleRibbons()
{
byte count = 0;
if (RibbonWinning) count++;
if (RibbonVictory) count++;
for (int i = 1; i < 7; i++) // Sinnoh Battle Ribbons
{
if (((Data[0x24] >> i) & 1) == 1)
count++;
}
return count;
}
private byte CountContestRibbons()
{
byte count = 0;
for (int i = 0; i < 8; i++) // Sinnoh 3, Hoenn 1
{
if ((Data[0x60] >> i & 1) == 1) count++;
if (((Data[0x61] >> i) & 1) == 1) count++;
if (((Data[0x3C] >> i) & 1) == 1) count++;
if (((Data[0x3D] >> i) & 1) == 1) count++;
}
for (int i = 0; i < 4; i++) // Sinnoh 4, Hoenn 2
{
if (((Data[0x62] >> i) & 1) == 1) count++;
if (((Data[0x3E] >> i) & 1) == 1) count++;
}
return count;
}
private int CalculateAbilityIndex()
{
var pi = (PersonalInfoB2W2) PersonalInfo;
if (HiddenAbility)
return 2;
if (pi.Ability1 == Ability)
return 0;
if (pi.Ability2 == Ability)
return 1;
// reset ability, invalid
var pid = PID;
if (Gen5)
pid >>= 16;
return (int)(pid & 1);
}
}
}

View file

@ -470,7 +470,7 @@ namespace PKHeX.Core
return -1;
if (Version == (int) GameVersion.CXD)
return Array.IndexOf(PersonalInfo.Abilities, Ability);
return PersonalInfo.GetAbilityIndex(Ability); // Can mismatch; not tied to PID
return (int)((Gen5 ? PID >> 16 : PID) & 1);
}
}
@ -734,8 +734,8 @@ namespace PKHeX.Core
public void RefreshAbility(int n)
{
AbilityNumber = 1 << n;
int[] abilities = PersonalInfo.Abilities;
if ((uint)n < abilities.Length)
var abilities = PersonalInfo.Abilities;
if ((uint)n < abilities.Count)
Ability = abilities[n];
if (this is PK5 pk5)
pk5.HiddenAbility = n == 2;

View file

@ -45,7 +45,7 @@ namespace PKHeX.Core
}
// this is a hack; depends on currently loaded SaveFile's Game ID
private static bool IsGG() => GameVersion.GG.Contains(PKMConverter.Game);
private static bool IsGG() => PKMConverter.Game == (int)GameVersion.GP || PKMConverter.Game == (int)GameVersion.GE;
public static bool IsTotemForm(int species, int form, int generation)
{

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -130,7 +131,7 @@ namespace PKHeX.Core
/// <summary>
/// Held Items the entry can be randomly encountered with.
/// </summary>
public abstract int[] Items { get; set; }
public abstract IReadOnlyList<int> Items { get; set; }
/// <summary>
/// Gender Ratio value determining if the entry is a fixed gender or bi-gendered.
@ -155,7 +156,14 @@ namespace PKHeX.Core
/// <summary>
/// Full list of <see cref="PKM.Ability"/> values the entry can have.
/// </summary>
public abstract int [] Abilities { get; set; }
public abstract IReadOnlyList<int> Abilities { get; set; }
/// <summary>
/// Gets the ability index without creating an array and looking through it.
/// </summary>
/// <param name="abilityID">Ability ID</param>
/// <returns>Ability Index</returns>
public abstract int GetAbilityIndex(int abilityID);
/// <summary>
/// Escape factor used for fleeing the Safari Zone or calling for help in SOS Battles.

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -53,6 +54,7 @@ namespace PKHeX.Core
public int Ability1 { get => Data[0x18]; set => Data[0x18] = (byte)value; }
public int Ability2 { get => Data[0x19]; set => Data[0x19] = (byte)value; }
public int AbilityH { get => Data[0x1A]; set => Data[0x1A] = (byte)value; }
public override int EscapeRate { get => Data[0x1B]; set => Data[0x1B] = (byte)value; }
protected internal override int FormStatsIndex { get => BitConverter.ToUInt16(Data, 0x1C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1C); }
public override int FormeSprite { get => BitConverter.ToUInt16(Data, 0x1E); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x1E); }
@ -65,30 +67,32 @@ namespace PKHeX.Core
public override int Height { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); }
public override int Weight { get => BitConverter.ToUInt16(Data, 0x26); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x26); }
public override int[] Items
public override IReadOnlyList<int> Items
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Length != 3) return;
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override int[] Abilities
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Length != 3) return;
if (value.Count != 3) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
AbilityH = (byte)value[2];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : abilityID == AbilityH ? 2 : -1;
public bool HasHiddenAbility => AbilityH != Ability1;
}
}

View file

@ -1,4 +1,7 @@
namespace PKHeX.Core
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary>
/// <see cref="PersonalInfo"/> class with values from Generation 1 games.
@ -46,10 +49,11 @@
public override int EV_SPD { get => EV_SPC; set { } }
// Future game values, unused
public override int[] Items { get => new[] { 0, 0 }; set { } }
public override IReadOnlyList<int> Items { get => Array.Empty<int>(); set { } }
public override int EggGroup1 { get => 0; set { } }
public override int EggGroup2 { get => 0; set { } }
public override int[] Abilities { get => new[] { 0, 0 }; set { } }
public override IReadOnlyList<int> Abilities { get => Array.Empty<int>(); set { } }
public override int GetAbilityIndex(int abilityID) => -1;
public override int Gender { get; set; }
public override int HatchCycles { get => 0; set { } }
public override int BaseFriendship { get => 0; set { } }

View file

@ -1,4 +1,7 @@
namespace PKHeX.Core
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary>
/// <see cref="PersonalInfo"/> class with values from Generation 2 games.
@ -37,12 +40,12 @@
public override int EggGroup1 { get => Data[0x17] & 0xF; set => Data[0x17] = (byte)((Data[0x17] & 0xF0) | value); }
public override int EggGroup2 { get => Data[0x17] >> 4; set => Data[0x17] = (byte)((Data[0x17] & 0x0F) | value << 4); }
public override int[] Items
public override IReadOnlyList<int> Items
{
get => new[] { Item1, Item2 };
set
{
if (value.Length != 2) return;
if (value.Count != 2) return;
Item1 = value[0];
Item2 = value[1];
}
@ -57,7 +60,8 @@
public override int EV_SPD { get => SPD; set { } }
// Future game values, unused
public override int[] Abilities { get => new[] { 0, 0 }; set { } }
public override IReadOnlyList<int> Abilities { get => Array.Empty<int>(); set { } }
public override int GetAbilityIndex(int abilityID) => -1;
public override int BaseFriendship { get => 70; set { } }
public override int EscapeRate { get => 0; set { } }
public override int Color { get => 0; set { } }

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -46,28 +47,30 @@ namespace PKHeX.Core
public override int Color { get => Data[0x19] & 0x7F; set => Data[0x19] = (byte)((Data[0x19] & 0x80) | value); }
public bool NoFlip { get => Data[0x19] >> 7 == 1; set => Data[0x19] = (byte)(Color | (value ? 0x80 : 0)); }
public override int[] Items
public override IReadOnlyList<int> Items
{
get => new[] { Item1, Item2 };
set
{
if (value.Length != 2) return;
if (value.Count != 2) return;
Item1 = value[0];
Item2 = value[1];
}
}
public override int[] Abilities
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2 };
set
{
if (value.Length != 2) return;
if (value.Count != 2) return;
Ability1 = (byte)value[0];
Ability2 = (byte)value[1];
}
}
public override int GetAbilityIndex(int abilityID) => abilityID == Ability1 ? 0 : abilityID == Ability2 ? 1 : -1;
public bool HasSecondAbility => Ability1 != Ability2;
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -88,30 +89,32 @@ namespace PKHeX.Core
public override int Height { get => BitConverter.ToUInt16(Data, 0x24); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x24); }
public override int Weight { get => BitConverter.ToUInt16(Data, 0x26); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x26); }
public override int[] Items
public override IReadOnlyList<int> Items
{
get => new[] { Item1, Item2, Item3 };
set
{
if (value.Length != 3) return;
if (value.Count != 3) return;
Item1 = value[0];
Item2 = value[1];
Item3 = value[2];
}
}
public override int[] Abilities
public override IReadOnlyList<int> Abilities
{
get => new[] { Ability1, Ability2, AbilityH };
set
{
if (value.Length != 3) return;
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 Species { get => BitConverter.ToUInt16(Data, 0x4C); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x4C); }
public int BaseSpecies { get => BitConverter.ToUInt16(Data, 0x56); set => BitConverter.GetBytes((ushort)value).CopyTo(Data, 0x56); }

View file

@ -259,17 +259,6 @@ namespace PKHeX.Core
}
}
/// <summary>
/// Gets the abilities possible for a given <see cref="PKM.Species"/> and <see cref="PKM.AltForm"/>.
/// </summary>
/// <param name="species"><see cref="PKM.Species"/></param>
/// <param name="forme"><see cref="PKM.AltForm"/></param>
/// <returns>Array of possible abilities</returns>
public int[] GetAbilities(int species, int forme)
{
return GetFormeEntry(species, forme).Abilities;
}
/// <summary>
/// Gets the <see cref="PersonalInfo"/> entry index for a given <see cref="PKM.Species"/> and <see cref="PKM.AltForm"/>.
/// </summary>

View file

@ -100,10 +100,13 @@ namespace PKHeX.Core
// 2 bytes for alarm clock time setting
public byte[] PoketchDotArtistData
public byte[] GetPoketchDotArtistData() => General.Slice(PoketchStart + 0x2A, 120);
public void SetPoketchDotArtistData(byte[] value)
{
get => General.Slice(PoketchStart + 0x2A, 120);
set => SetData(General, value, PoketchStart + 0x2A);
if (value.Length != 120)
throw new ArgumentException($"Expected {120} bytes.", nameof(value.Length));
SetData(General, value, PoketchStart + 0x2A);
}
// map marking stuff is at the end, unimportant
@ -127,25 +130,25 @@ namespace PKHeX.Core
SetData(General, tree.Data, OFS_HONEY + (HONEY_SIZE * index));
}
public int[] MunchlaxTrees
public int[] GetMunchlaxTrees() => CalculateMunchlaxTrees(TID, SID);
private static int[] CalculateMunchlaxTrees(int tid, int sid)
{
get
{
int A = (TID >> 8) % 21;
int B = (TID & 0x00FF) % 21;
int C = (SID >> 8) % 21;
int D = (SID & 0x00FF) % 21;
int A = (tid >> 8) % 21;
int B = (tid & 0x00FF) % 21;
int C = (sid >> 8) % 21;
int D = (sid & 0x00FF) % 21;
if (A == B) B = (B + 1) % 21;
if (A == C) C = (C + 1) % 21;
if (B == C) C = (C + 1) % 21;
if (A == D) D = (D + 1) % 21;
if (B == D) D = (D + 1) % 21;
if (C == D) D = (D + 1) % 21;
if (A == B) B = (B + 1) % 21;
if (A == C) C = (C + 1) % 21;
if (B == C) C = (C + 1) % 21;
if (A == D) D = (D + 1) % 21;
if (B == D) D = (D + 1) % 21;
if (C == D) D = (D + 1) % 21;
return new[] { A, B, C, D };
}
return new[] {A, B, C, D};
}
#endregion
public int OFS_PoffinCase { get; protected set; }

View file

@ -22,6 +22,18 @@ namespace PKHeX.Core
set => Data[Offset + 1] = (byte) ((Data[Offset + 1] & 0xEF) | (value & 1) << 4);
}
public bool BattledToday
{
get => ((Data[Offset + 1] >> 5) & 1) == 1;
set => Data[Offset + 1] = (byte)((Data[Offset + 1] & 0xDF) | (value ? 1 : 0) << 5);
}
public int RegistryStatus
{
get => (Data[Offset + 1] >> 6) & 3;
set => Data[Offset + 1] = (byte)((Data[Offset + 1] & 0x3F) | (value & 3) << 6);
}
public string OT_Name
{
get => StringConverter3.GetString3(Data, Offset + 2, 7, Japanese);
@ -36,10 +48,15 @@ namespace PKHeX.Core
public int OT_Class => Data[Offset + 9] % 5;
public int Language { get => Data[Offset + 0x0D]; set => Data[Offset + 0x0D] = (byte)value; }
public int _E { get => Data[Offset + 0x0E]; set => Data[Offset + 0x0E] = (byte)value; }
public int _F { get => Data[Offset + 0x0F]; set => Data[Offset + 0x0F] = (byte)value; }
public int _10 { get => Data[Offset + 0x10]; set => Data[Offset + 0x10] = (byte)value; }
public int _11 { get => Data[Offset + 0x11]; set => Data[Offset + 0x11] = (byte)value; }
public ushort SecretBasesReceived
{
get => BitConverter.ToUInt16(Data, Offset + 0x0E);
set => BitConverter.GetBytes(value).CopyTo(Data, Offset + 0x0E);
}
public byte TimesEntered { get => Data[Offset + 0x10]; set => Data[Offset + 0x10] = value; }
public int Unused11 { get => Data[Offset + 0x11]; set => Data[Offset + 0x11] = (byte)value; } // alignment padding
public byte[] Decorations
{

View file

@ -4,6 +4,7 @@
/// Ball Capsule Seals used in Generation 4 save files.
/// </summary>
/// <remarks>80 bytes, one for each seal.</remarks>
#pragma warning disable CA1027 // Mark enums with FlagsAttribute
public enum Seal4
{
HeartA,

View file

@ -1,5 +1,6 @@
namespace PKHeX.Core
{
#pragma warning disable CA1027 // Mark enums with FlagsAttribute
public enum TrainerSprite6
{
Calem = 00,

View file

@ -1,5 +1,6 @@
namespace PKHeX.Core
{
#pragma warning disable CA1027 // Mark enums with FlagsAttribute
public enum EventVarType
{
/// <summary>

View file

@ -32,7 +32,7 @@ namespace PKHeX.Core
}
}
public override void CopyTo(PK5 pk5) => pk5.HeldMailData = Data;
public override void CopyTo(PK5 pk5) => pk5.SetHeldMailData(Data);
public override ushort AuthorTID { get => BitConverter.ToUInt16(Data, 0); set => BitConverter.GetBytes(value).CopyTo(Data, 0); }
public override ushort AuthorSID { get => BitConverter.ToUInt16(Data, 2); set => BitConverter.GetBytes(value).CopyTo(Data, 2); }
public override byte AuthorGender { get => Data[4]; set => Data[4] = value; }

View file

@ -379,19 +379,20 @@ namespace PKHeX.WinForms.Controls
private void LoadAbility4(PKM pk)
{
int[] abils = pk.PersonalInfo.Abilities;
var index = GetAbilityIndex4(pk, abils);
var index = GetAbilityIndex4(pk);
CB_Ability.SelectedIndex = Math.Min(CB_Ability.Items.Count - 1, index);
}
private static int GetAbilityIndex4(PKM pk, int[] abils)
private static int GetAbilityIndex4(PKM pk)
{
int abilityIndex = Array.IndexOf(abils, pk.Ability);
var pi = pk.PersonalInfo;
int abilityIndex = pi.GetAbilityIndex(pk.Ability);
if (abilityIndex < 0)
return 0;
if (abilityIndex >= 2)
return 2;
var abils = pi.Abilities;
if (abils[0] == abils[1])
return pk.PIDAbility;
return abilityIndex;

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using PKHeX.Core;
@ -69,13 +70,21 @@ namespace PKHeX.WinForms
row.Cells[r++].Value = p.SPD.ToString("000");
row.Cells[r].Style.BackColor = ImageUtil.ColorBaseStat(p.SPE);
row.Cells[r++].Value = p.SPE.ToString("000");
row.Cells[r++].Value = abilities[p.Abilities[0]];
row.Cells[r++].Value = abilities[p.Abilities[1]];
row.Cells[r].Value = abilities[p.Abilities.Length <= 2 ? 0 : p.Abilities[2]];
var abils = p.Abilities;
row.Cells[r++].Value = GetAbility(abils, 0);
row.Cells[r++].Value = GetAbility(abils, 1);
row.Cells[r].Value = GetAbility(abils, 2);
row.Height = SpriteUtil.Spriter.Height + 1;
DGV.Rows.Add(row);
}
private string GetAbility(IReadOnlyList<int> abilityIDs, int index)
{
if ((uint)index > abilityIDs.Count)
return abilities[0];
return abilities[abilityIDs[index]];
}
private static bool GetIsNative(PersonalInfo personalInfo, int s)
{
return personalInfo switch

View file

@ -22,7 +22,7 @@ namespace PKHeX.WinForms
Table = HoneyTree.TablePt;
// Get Munchlax tree for this savegame in screen
MunchlaxTrees = SAV.MunchlaxTrees;
MunchlaxTrees = SAV.GetMunchlaxTrees();
const string sep = "- ";
L_Tree0.Text = string.Join(Environment.NewLine, MunchlaxTrees.Select(z => sep + CB_TreeList.Items[z]));

View file

@ -224,7 +224,7 @@ namespace PKHeX.WinForms
CLB_Poketch.Items.Add(title, val);
}
DotArtistByte = s.PoketchDotArtistData;
DotArtistByte = s.GetPoketchDotArtistData();
ColorTable = new byte[] { 248, 168, 88, 8 };
SetPictureBoxFromFlags(DotArtistByte);
string tip = "Guide about D&D ImageFile Format";
@ -244,7 +244,7 @@ namespace PKHeX.WinForms
var b = CLB_Poketch.GetItemChecked(i);
s.SetPoketchAppUnlocked((PoketchApp)i, b);
}
s.PoketchDotArtistData = DotArtistByte;
s.SetPoketchDotArtistData(DotArtistByte);
}
private void SetPictureBoxFromFlags(byte[] inp)

View file

@ -433,7 +433,7 @@ namespace PKHeX.WinForms
int newabil = Convert.ToInt16(MT_AbilNo.Text) >> 1;
int species = WinFormsUtil.GetIndex(CB_Species);
int formnum = CB_Form.SelectedIndex;
int[] abils = PersonalTable.AO.GetAbilities(species, formnum);
var abils = PersonalTable.AO.GetFormeEntry(species, formnum).Abilities;
CB_Ability.DataSource = GameInfo.FilteredSources.GetAbilityList(abils, 6);
CB_Ability.SelectedIndex = newabil < 3 ? newabil : 0;

View file

@ -95,7 +95,7 @@ namespace PKHeX.WinForms
case SAV5 sav5:
m = new Mail5[p.Count + 20];
for (int i = 0; i < p.Count; i++)
m[i] = new Mail5(((PK5)p[i]).HeldMailData);
m[i] = new Mail5(((PK5)p[i]).GetHeldMailData());
for (int i = p.Count, j = 0; i < m.Length; i++, j++)
{
int ofs = SAV5.GetMailOffset(j);