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
This commit is contained in:
Kurt 2019-10-16 18:47:31 -07:00 committed by GitHub
parent a70769dc76
commit 02420d3e93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
340 changed files with 4945 additions and 4367 deletions

View file

@ -79,7 +79,7 @@ namespace PKHeX.Core
/// <param name="propertyName">Property Name to fetch the type for</param>
/// <param name="typeIndex">Type index (within <see cref="Types"/>. Leave empty (0) for a nonspecific format.</param>
/// <returns>Short name of the property's type.</returns>
public static string GetPropertyType(string propertyName, int typeIndex = 0)
public static string? GetPropertyType(string propertyName, int typeIndex = 0)
{
if (CustomProperties.Contains(propertyName))
return "Custom";
@ -164,6 +164,8 @@ namespace PKHeX.Core
return false;
try
{
if (pi == null)
continue;
if (pi.IsValueEqual(obj, cmd.PropertyValue) == cmd.Evaluator)
continue;
}
@ -248,7 +250,7 @@ namespace PKHeX.Core
if (cmd.PropertyValue == CONST_SUGGEST)
return SetSuggestedPKMProperty(cmd.PropertyName, info);
if (cmd.PropertyValue == CONST_RAND && cmd.PropertyName == nameof(PKM.Moves))
return SetMoves(pk, pk.GetMoveSet(true, info.Legality));
return SetMoves(pk, pk.GetMoveSet(info.Legality, true));
if (SetComplexProperty(pk, cmd))
return ModifyResult.Modified;
@ -321,7 +323,7 @@ namespace PKHeX.Core
if (cmd.PropertyName != IdentifierContains)
return false;
bool result = pk.Identifier.Contains(cmd.PropertyValue);
bool result = pk.Identifier?.Contains(cmd.PropertyValue) ?? false;
return result == cmd.Evaluator;
}
@ -451,7 +453,7 @@ namespace PKHeX.Core
else if (cmd.PropertyName == nameof(PKM.PID) && cmd.PropertyValue == CONST_SHINY)
pk.SetShiny();
else if (cmd.PropertyName == nameof(PKM.Species) && cmd.PropertyValue == "0")
pk.Data = new byte[pk.Data.Length];
Array.Clear(pk.Data, 0, pk.Data.Length);
else if (cmd.PropertyName.StartsWith("IV") && cmd.PropertyValue == CONST_RAND)
SetRandomIVs(pk, cmd);
else if (cmd.PropertyName == nameof(PKM.IsNicknamed) && string.Equals(cmd.PropertyValue, "false", StringComparison.OrdinalIgnoreCase))

View file

@ -10,11 +10,11 @@ namespace PKHeX.Core
internal PKM pkm { get; }
internal PKMInfo(PKM pk) { pkm = pk; }
private LegalityAnalysis la;
internal LegalityAnalysis Legality => la ?? (la = new LegalityAnalysis(pkm));
private LegalityAnalysis? la;
internal LegalityAnalysis Legality => la ??= new LegalityAnalysis(pkm);
public bool Legal => Legality.Valid;
internal IReadOnlyList<int> SuggestedRelearn => Legality.GetSuggestedRelearn();
internal EncounterStatic SuggestedEncounter => Legality.GetSuggestedMetInfo();
internal EncounterStatic? SuggestedEncounter => Legality.GetSuggestedMetInfo();
}
}

View file

@ -20,6 +20,12 @@ namespace PKHeX.Core
public string PropertyValue { get; private set; }
public bool Evaluator { get; private set; }
public StringInstruction(string name, string value)
{
PropertyName = name;
PropertyValue = value;
}
public void SetScreenedValue(string[] arr)
{
int index = Array.IndexOf(arr, PropertyValue);
@ -72,7 +78,7 @@ namespace PKHeX.Core
let eval = line[0] == Require
let split = line.Substring(1).Split(SplitInstruction)
where split.Length == 2 && !string.IsNullOrWhiteSpace(split[0])
select new StringInstruction { PropertyName = split[0], PropertyValue = split[1], Evaluator = eval };
select new StringInstruction(split[0], split[1]) { Evaluator = eval };
}
public static IEnumerable<StringInstruction> GetInstructions(IEnumerable<string> lines)
@ -81,7 +87,7 @@ namespace PKHeX.Core
return from line in raw
select line.Split(SplitInstruction) into split
where split.Length == 2
select new StringInstruction { PropertyName = split[0], PropertyValue = split[1] };
select new StringInstruction(split[0], split[1]);
}
/// <summary>

View file

@ -13,23 +13,26 @@ namespace PKHeX.Core
private const string SetSeparator = ";";
public StringInstructionSet(IList<StringInstruction> filters, IList<StringInstruction> instructions)
{
Filters = filters;
Instructions = instructions;
}
public StringInstructionSet(ICollection<string> set)
{
Filters = StringInstruction.GetFilters(set).ToList();
Instructions = StringInstruction.GetInstructions(set).ToList();
}
public static IEnumerable<StringInstructionSet> GetBatchSets(IList<string> lines)
{
int start = 0;
while (start < lines.Count)
{
var list = lines.Skip(start).TakeWhile(_ => !lines[start++].StartsWith(SetSeparator)).ToList();
yield return GetBatchSet(list);
yield return new StringInstructionSet(list);
}
}
private static StringInstructionSet GetBatchSet(ICollection<string> set)
{
return new StringInstructionSet
{
Filters = StringInstruction.GetFilters(set).ToList(),
Instructions = StringInstruction.GetInstructions(set).ToList(),
};
}
}
}

View file

@ -597,6 +597,18 @@ namespace PKHeX.Core
pb.ResetCP();
}
/// <summary>
/// Gets a moveset for the provided <see cref="PKM"/> data.
/// </summary>
/// <param name="pkm">PKM to generate for</param>
/// <param name="random">Full movepool &amp; shuffling</param>
/// <returns>4 moves</returns>
public static int[] GetMoveSet(this PKM pkm, bool random = false)
{
var la = new LegalityAnalysis(pkm);
return pkm.GetMoveSet(la, random);
}
/// <summary>
/// Gets a moveset for the provided <see cref="PKM"/> data.
/// </summary>
@ -604,10 +616,8 @@ namespace PKHeX.Core
/// <param name="random">Full movepool &amp; shuffling</param>
/// <param name="la">Precomputed optional</param>
/// <returns>4 moves</returns>
public static int[] GetMoveSet(this PKM pkm, bool random = false, LegalityAnalysis la = null)
public static int[] GetMoveSet(this PKM pkm, LegalityAnalysis la, bool random = false)
{
if (la == null)
la = new LegalityAnalysis(pkm);
int[] m = la.GetSuggestedMoves(tm: random, tutor: random, reminder: random);
if (m == null)
return pkm.Moves;

View file

@ -85,7 +85,7 @@ namespace PKHeX.Core
return true; // no mods necessary
// Required HP type doesn't match IVs. Make currently-flawless IVs flawed.
int[] best = GetSuggestedHiddenPowerIVs(hpVal, IVs);
int[]? best = GetSuggestedHiddenPowerIVs(hpVal, IVs);
if (best == null)
return false; // can't force hidden power?
@ -95,12 +95,12 @@ namespace PKHeX.Core
return true;
}
private static int[] GetSuggestedHiddenPowerIVs(int hpVal, int[] IVs)
private static int[]? GetSuggestedHiddenPowerIVs(int hpVal, int[] IVs)
{
var flawless = IVs.Select((v, i) => v == 31 ? i : -1).Where(v => v != -1).ToArray();
var permutations = GetPermutations(flawless, flawless.Length);
int flawedCount = 0;
int[] best = null;
int[]? best = null;
foreach (var permute in permutations)
{
var ivs = (int[])IVs.Clone();

View file

@ -13,7 +13,7 @@ namespace PKHeX.Core
private readonly ushort[] Stats;
protected readonly PKM pkm; // protected for children generating extra properties
public string Position => pkm.Identifier;
public string? Position => pkm.Identifier;
public string Nickname => pkm.Nickname;
public string Species => Get(Strings.specieslist, pkm.Species);
public string Nature => Get(Strings.natures, pkm.Nature);
@ -124,6 +124,6 @@ namespace PKHeX.Core
/// <param name="arr">Array of strings</param>
/// <param name="val">Index to fetch</param>
/// <returns>Null if array is null</returns>
private static string Get(IReadOnlyList<string> arr, int val) => (uint)val < arr?.Count ? arr[val] : null;
private static string Get(IReadOnlyList<string> arr, int val) => (uint)val < arr?.Count ? arr[val] : string.Empty;
}
}

View file

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using PKHeX.Core.Searching;
namespace PKHeX.Core
{
public abstract class BoxManipBase : IBoxManip
{
protected BoxManipBase(BoxManipType type, Func<SaveFile, bool> usable)
{
Type = type;
Usable = usable;
}
public BoxManipType Type { get; }
public Func<SaveFile, bool> Usable { get; }
public abstract string GetPrompt(bool all);
public abstract string GetFail(bool all);
public abstract string GetSuccess(bool all);
public abstract int Execute(SaveFile SAV, BoxManipParam param);
public static readonly IReadOnlyList<BoxManipBase> SortCommon = new List<BoxManipBase>
{
new BoxManipSort(BoxManipType.SortSpecies, PKMSorting.OrderBySpecies),
new BoxManipSort(BoxManipType.SortSpeciesReverse, PKMSorting.OrderByDescendingSpecies),
new BoxManipSort(BoxManipType.SortLevel, PKMSorting.OrderByLevel),
new BoxManipSort(BoxManipType.SortLevelReverse, PKMSorting.OrderByDescendingLevel),
new BoxManipSort(BoxManipType.SortDate, PKMSorting.OrderByDateObtained, s => s.Generation >= 4),
new BoxManipSort(BoxManipType.SortName, list => list.OrderBySpeciesName(GameInfo.Strings.Species)),
new BoxManipSort(BoxManipType.SortFavorite, list => list.OrderByCustom(pk => pk is PB7 pb7 && pb7.Favorite), s => s is SAV7b),
new BoxManipSortComplex(BoxManipType.SortParty, (list, sav) => list.OrderByCustom(pk => ((SAV7b)sav).Blocks.Storage.GetPartyIndex(pk.Box - 1, pk.Slot - 1)), s => s is SAV7b),
new BoxManipSort(BoxManipType.SortShiny, list => list.OrderByCustom(pk => !pk.IsShiny)),
new BoxManipSort(BoxManipType.SortRandom, list => list.OrderByCustom(_ => Util.Rand32())),
};
public static readonly IReadOnlyList<BoxManipBase> SortAdvanced = new List<BoxManipBase>
{
new BoxManipSort(BoxManipType.SortUsage, PKMSorting.OrderByUsage, s => s.Generation >= 3),
new BoxManipSort(BoxManipType.SortPotential, list => list.OrderByCustom(pk => (pk.MaxIV * 6) - pk.IVTotal)),
new BoxManipSort(BoxManipType.SortTraining, list => list.OrderByCustom(pk => (pk.MaxEV * 6) - pk.EVTotal)),
new BoxManipSortComplex(BoxManipType.SortOwner, (list, sav) => list.OrderByOwnership(sav)),
new BoxManipSort(BoxManipType.SortType, list => list.OrderByCustom(pk => pk.PersonalInfo.Type1, pk => pk.PersonalInfo.Type2)),
new BoxManipSort(BoxManipType.SortVersion, list => list.OrderByCustom(pk => pk.GenNumber, pk => pk.Version, pk => pk.Met_Location), s => s.Generation >= 3),
new BoxManipSort(BoxManipType.SortBST, list => list.OrderByCustom(pk => pk.PersonalInfo.BST)),
new BoxManipSort(BoxManipType.SortCP, list => list.OrderByCustom(pk => pk is PB7 pb7 ? pb7.Stat_CP : 0), s => s is SAV7b),
new BoxManipSort(BoxManipType.SortLegal, list => list.OrderByCustom(pk => !new LegalityAnalysis(pk).Valid)),
new BoxManipSort(BoxManipType.SortEncounterType, list => list.OrderByCustom(pk => new LegalityAnalysis(pk).Info?.EncounterMatch.GetEncounterTypeName() ?? string.Empty)),
};
public static readonly IReadOnlyList<BoxManipBase> ClearCommon = new List<BoxManipBase>
{
new BoxManipClear(BoxManipType.DeleteAll, _ => true),
new BoxManipClear(BoxManipType.DeleteEggs, pk => pk.IsEgg, s => s.Generation >= 2),
new BoxManipClearComplex(BoxManipType.DeletePastGen, (pk, sav) => pk.GenNumber != sav.Generation, s => s.Generation >= 4),
new BoxManipClearComplex(BoxManipType.DeleteForeign, (pk, sav) => !sav.IsOriginalHandler(pk, pk.Format > 2)),
new BoxManipClear(BoxManipType.DeleteUntrained, pk => pk.EVTotal == 0),
new BoxManipClear(BoxManipType.DeleteItemless, pk => pk.HeldItem == 0),
new BoxManipClear(BoxManipType.DeleteIllegal, pk => !new LegalityAnalysis(pk).Valid),
new BoxManipClearDuplicate<string>(BoxManipType.DeleteClones, pk => SearchUtil.GetCloneDetectMethod(CloneDetectionMethod.HashDetails)(pk)),
};
public static readonly IReadOnlyList<BoxManipModify> ModifyCommon = new List<BoxManipModify>
{
new BoxManipModify(BoxManipType.ModifyHatchEggs, pk => pk.ForceHatchPKM(), s => s.Generation >= 2),
new BoxManipModify(BoxManipType.ModifyMaxFriendship, pk => pk.MaximizeFriendship()),
new BoxManipModify(BoxManipType.ModifyMaxLevel, pk => pk.MaximizeLevel()),
new BoxManipModify(BoxManipType.ModifyResetMoves, pk => pk.SetMoves(pk.GetMoveSet()), s => s.Generation >= 3),
new BoxManipModify(BoxManipType.ModifyRandomMoves, pk => pk.SetMoves(pk.GetMoveSet(true))),
new BoxManipModify(BoxManipType.ModifyHyperTrain,pk => pk.SetSuggestedHyperTrainingData(), s => s.Generation >= 7),
new BoxManipModify(BoxManipType.ModifyRemoveNicknames, pk => pk.SetDefaultNickname()),
new BoxManipModify(BoxManipType.ModifyRemoveItem, pk => pk.HeldItem = 0, s => s.Generation >= 2),
};
}
}

View file

@ -1,78 +1,21 @@
using System;
using System.Collections.Generic;
using PKHeX.Core.Searching;
namespace PKHeX.Core
{
public class BoxManipClear : IBoxManip
public sealed class BoxManipClear : BoxManipBase
{
public BoxManipType Type { get; protected set; }
public Func<SaveFile, bool> Usable { get; set; }
private readonly Func<PKM, bool> Criteria;
public BoxManipClear(BoxManipType type, Func<PKM, bool> criteria) : this(type, criteria, _ => true) { }
public BoxManipClear(BoxManipType type, Func<PKM, bool> criteria, Func<SaveFile, bool> usable) : base(type, usable) => Criteria = criteria;
public string GetPrompt(bool all) => all ? MessageStrings.MsgSaveBoxClearAll : MessageStrings.MsgSaveBoxClearCurrent;
public string GetFail(bool all) => all ? MessageStrings.MsgSaveBoxClearAllFailBattle : MessageStrings.MsgSaveBoxClearCurrentFailBattle;
public string GetSuccess(bool all) => all ? MessageStrings.MsgSaveBoxClearAllSuccess : MessageStrings.MsgSaveBoxClearCurrentSuccess;
protected Func<PKM, bool> CriteriaSimple { private get; set; }
protected Func<PKM, SaveFile, bool> CriteriaSAV { private get; set; }
public virtual int Execute(SaveFile SAV, BoxManipParam param)
{
bool Method(PKM p) => param.Reverse ^ (CriteriaSAV?.Invoke(p, SAV) ?? CriteriaSimple?.Invoke(p) ?? true);
return SAV.ClearBoxes(param.Start, param.Stop, Method);
}
protected BoxManipClear() { }
private BoxManipClear(BoxManipType type, Func<PKM, bool> criteria, Func<SaveFile, bool> usable = null)
{
Type = type;
CriteriaSimple = criteria;
Usable = usable;
}
private BoxManipClear(BoxManipType type, Func<PKM, SaveFile, bool> criteria, Func<SaveFile, bool> usable = null)
{
Type = type;
CriteriaSAV = criteria;
Usable = usable;
}
public static readonly IReadOnlyList<BoxManipClear> Common = new List<BoxManipClear>
{
new BoxManipClear(BoxManipType.DeleteAll, _ => true),
new BoxManipClear(BoxManipType.DeleteEggs, pk => pk.IsEgg, s => s.Generation >= 2),
new BoxManipClear(BoxManipType.DeletePastGen, (pk, sav) => pk.GenNumber != sav.Generation, s => s.Generation >= 4),
new BoxManipClear(BoxManipType.DeleteForeign, (pk, sav) => !sav.IsOriginalHandler(pk, pk.Format > 2)),
new BoxManipClear(BoxManipType.DeleteUntrained, pk => pk.EVTotal == 0),
new BoxManipClear(BoxManipType.DeleteItemless, pk => pk.HeldItem == 0),
new BoxManipClear(BoxManipType.DeleteIllegal, pk => !new LegalityAnalysis(pk).Valid),
new BoxManipClearDuplicate<string>(BoxManipType.DeleteClones, pk => SearchUtil.GetCloneDetectMethod(CloneDetectionMethod.HashDetails)(pk)),
};
}
public sealed class BoxManipClearDuplicate<T> : BoxManipClear
{
private readonly HashSet<T> HashSet = new HashSet<T>();
public override string GetPrompt(bool all) => all ? MessageStrings.MsgSaveBoxClearAll : MessageStrings.MsgSaveBoxClearCurrent;
public override string GetFail(bool all) => all ? MessageStrings.MsgSaveBoxClearAllFailBattle : MessageStrings.MsgSaveBoxClearCurrentFailBattle;
public override string GetSuccess(bool all) => all ? MessageStrings.MsgSaveBoxClearAllSuccess : MessageStrings.MsgSaveBoxClearCurrentSuccess;
public override int Execute(SaveFile SAV, BoxManipParam param)
{
HashSet.Clear();
return base.Execute(SAV, param);
}
public BoxManipClearDuplicate(BoxManipType type, Func<PKM, T> criteria, Func<SaveFile, bool> usable = null)
{
Type = type;
Usable = usable;
CriteriaSimple = pk =>
{
var result = criteria(pk);
if (HashSet.Contains(result))
return true;
HashSet.Add(result);
return false;
};
bool Method(PKM p) => param.Reverse ^ Criteria(p);
return SAV.ClearBoxes(param.Start, param.Stop, Method);
}
}
}

View file

@ -0,0 +1,21 @@
using System;
namespace PKHeX.Core
{
public sealed class BoxManipClearComplex : BoxManipBase
{
private readonly Func<PKM, SaveFile, bool> Criteria;
public BoxManipClearComplex(BoxManipType type, Func<PKM, SaveFile, bool> criteria) : this(type, criteria, _ => true) { }
public BoxManipClearComplex(BoxManipType type, Func<PKM, SaveFile, bool> criteria, Func<SaveFile, bool> usable) : base(type, usable) => Criteria = criteria;
public override string GetPrompt(bool all) => all ? MessageStrings.MsgSaveBoxClearAll : MessageStrings.MsgSaveBoxClearCurrent;
public override string GetFail(bool all) => all ? MessageStrings.MsgSaveBoxClearAllFailBattle : MessageStrings.MsgSaveBoxClearCurrentFailBattle;
public override string GetSuccess(bool all) => all ? MessageStrings.MsgSaveBoxClearAllSuccess : MessageStrings.MsgSaveBoxClearCurrentSuccess;
public override int Execute(SaveFile SAV, BoxManipParam param)
{
bool Method(PKM p) => param.Reverse ^ Criteria(p, SAV);
return SAV.ClearBoxes(param.Start, param.Stop, Method);
}
}
}

View file

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
public sealed class BoxManipClearDuplicate<T> : BoxManipBase
{
private readonly HashSet<T> HashSet = new HashSet<T>();
private readonly Func<PKM, bool> Criteria;
public BoxManipClearDuplicate(BoxManipType type, Func<PKM, T> criteria) : this(type, criteria, _ => true) { }
public BoxManipClearDuplicate(BoxManipType type, Func<PKM, T> criteria, Func<SaveFile, bool> usable) : base(type, usable)
{
Criteria = pk =>
{
var result = criteria(pk);
if (HashSet.Contains(result))
return true;
HashSet.Add(result);
return false;
};
}
public override string GetPrompt(bool all) => all ? MessageStrings.MsgSaveBoxClearAll : MessageStrings.MsgSaveBoxClearCurrent;
public override string GetFail(bool all) => all ? MessageStrings.MsgSaveBoxClearAllFailBattle : MessageStrings.MsgSaveBoxClearCurrentFailBattle;
public override string GetSuccess(bool all) => all ? MessageStrings.MsgSaveBoxClearAllSuccess : MessageStrings.MsgSaveBoxClearCurrentSuccess;
public override int Execute(SaveFile SAV, BoxManipParam param)
{
HashSet.Clear();
bool Method(PKM p) => param.Reverse ^ Criteria(p);
return SAV.ClearBoxes(param.Start, param.Stop, Method);
}
}
}

View file

@ -1,50 +1,17 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
public sealed class BoxManipModify : IBoxManip
public sealed class BoxManipModify : BoxManipBase
{
public BoxManipType Type { get; }
public Func<SaveFile, bool> Usable { get; set; }
public string GetPrompt(bool all) => null;
public string GetFail(bool all) => null;
public string GetSuccess(bool all) => null;
private readonly Action<PKM> Action;
private readonly Action<PKM, SaveFile> ActionComplex;
public BoxManipModify(BoxManipType type, Action<PKM> action) : this(type, action, _ => true) { }
public BoxManipModify(BoxManipType type, Action<PKM> action, Func<SaveFile, bool> usable) : base(type, usable) => Action = action;
public int Execute(SaveFile SAV, BoxManipParam param)
{
var method = Action ?? (px => ActionComplex(px, SAV));
return SAV.ModifyBoxes(method, param.Start, param.Stop);
}
public override string GetPrompt(bool all) => string.Empty;
public override string GetFail(bool all) => string.Empty;
public override string GetSuccess(bool all) => string.Empty;
private BoxManipModify(BoxManipType type, Action<PKM> action, Func<SaveFile, bool> usable = null)
{
Type = type;
Action = action;
Usable = usable;
}
private BoxManipModify(BoxManipType type, Action<PKM, SaveFile> action, Func<SaveFile, bool> usable = null)
{
Type = type;
ActionComplex = action;
Usable = usable;
}
public static readonly IReadOnlyList<BoxManipModify> Common = new List<BoxManipModify>
{
new BoxManipModify(BoxManipType.ModifyHatchEggs, pk => pk.ForceHatchPKM(), s => s.Generation >= 2),
new BoxManipModify(BoxManipType.ModifyMaxFriendship, pk => pk.MaximizeFriendship()),
new BoxManipModify(BoxManipType.ModifyMaxLevel, pk => pk.MaximizeLevel()),
new BoxManipModify(BoxManipType.ModifyResetMoves, pk => pk.SetMoves(pk.GetMoveSet()), s => s.Generation >= 3),
new BoxManipModify(BoxManipType.ModifyRandomMoves, pk => pk.SetMoves(pk.GetMoveSet(true))),
new BoxManipModify(BoxManipType.ModifyHyperTrain,pk => pk.SetSuggestedHyperTrainingData(), s => s.Generation >= 7),
new BoxManipModify(BoxManipType.ModifyRemoveNicknames, pk => pk.SetDefaultNickname()),
new BoxManipModify(BoxManipType.ModifyRemoveItem, pk => pk.HeldItem = 0, s => s.Generation >= 2),
};
public override int Execute(SaveFile SAV, BoxManipParam param) => SAV.ModifyBoxes(Action, param.Start, param.Stop);
}
}

View file

@ -0,0 +1,17 @@
using System;
namespace PKHeX.Core
{
public sealed class BoxManipModifyComplex : BoxManipBase
{
private readonly Action<PKM, SaveFile> Action;
public BoxManipModifyComplex(BoxManipType type, Action<PKM, SaveFile> action) : this(type, action, _ => true) { }
public BoxManipModifyComplex(BoxManipType type, Action<PKM, SaveFile> action, Func<SaveFile, bool> usable) : base(type, usable) => Action = action;
public override string GetPrompt(bool all) => string.Empty;
public override string GetFail(bool all) => string.Empty;
public override string GetSuccess(bool all) => string.Empty;
public override int Execute(SaveFile SAV, BoxManipParam param) => SAV.ModifyBoxes(pk => Action(pk, SAV), param.Start, param.Stop);
}
}

View file

@ -3,64 +3,20 @@ using System.Collections.Generic;
namespace PKHeX.Core
{
public sealed class BoxManipSort : IBoxManip
public sealed class BoxManipSort : BoxManipBase
{
public BoxManipType Type { get; }
public Func<SaveFile, bool> Usable { get; set; }
private readonly Func<IEnumerable<PKM>, IEnumerable<PKM>> Sorter;
public BoxManipSort(BoxManipType type, Func<IEnumerable<PKM>, IEnumerable<PKM>> sorter) : this(type, sorter, _ => true) { }
public BoxManipSort(BoxManipType type, Func<IEnumerable<PKM>, IEnumerable<PKM>> sorter, Func<SaveFile, bool> usable) : base(type, usable) => Sorter = sorter;
public string GetPrompt(bool all) => all ? MessageStrings.MsgSaveBoxSortAll : MessageStrings.MsgSaveBoxSortCurrent;
public string GetFail(bool all) => all ? MessageStrings.MsgSaveBoxSortAllFailBattle: MessageStrings.MsgSaveBoxSortCurrentFailBattle;
public string GetSuccess(bool all) => all ? MessageStrings.MsgSaveBoxSortAllSuccess : MessageStrings.MsgSaveBoxSortCurrentSuccess;
public override string GetPrompt(bool all) => all ? MessageStrings.MsgSaveBoxSortAll : MessageStrings.MsgSaveBoxSortCurrent;
public override string GetFail(bool all) => all ? MessageStrings.MsgSaveBoxSortAllFailBattle: MessageStrings.MsgSaveBoxSortCurrentFailBattle;
public override string GetSuccess(bool all) => all ? MessageStrings.MsgSaveBoxSortAllSuccess : MessageStrings.MsgSaveBoxSortCurrentSuccess;
private readonly Func<IEnumerable<PKM>, IEnumerable<PKM>> SorterSimple;
private readonly Func<IEnumerable<PKM>, SaveFile, IEnumerable<PKM>> SorterComplex;
public int Execute(SaveFile SAV, BoxManipParam param)
public override int Execute(SaveFile SAV, BoxManipParam param)
{
IEnumerable<PKM> Method(IEnumerable<PKM> p) => SorterSimple != null ? SorterSimple(p) : SorterComplex(p, SAV);
IEnumerable<PKM> Method(IEnumerable<PKM> p) => Sorter(p);
return SAV.SortBoxes(param.Start, param.Stop, Method, param.Reverse);
}
private BoxManipSort(BoxManipType type, Func<IEnumerable<PKM>, IEnumerable<PKM>> sorter, Func<SaveFile, bool> usable = null)
{
Type = type;
SorterSimple = sorter;
Usable = usable;
}
private BoxManipSort(BoxManipType type, Func<IEnumerable<PKM>, SaveFile, IEnumerable<PKM>> sorter, Func<SaveFile, bool> usable = null)
{
Type = type;
SorterComplex = sorter;
Usable = usable;
}
public static readonly IReadOnlyList<BoxManipSort> Common = new List<BoxManipSort>
{
new BoxManipSort(BoxManipType.SortSpecies, PKMSorting.OrderBySpecies),
new BoxManipSort(BoxManipType.SortSpeciesReverse, PKMSorting.OrderByDescendingSpecies),
new BoxManipSort(BoxManipType.SortLevel, PKMSorting.OrderByLevel),
new BoxManipSort(BoxManipType.SortLevelReverse, PKMSorting.OrderByDescendingLevel),
new BoxManipSort(BoxManipType.SortDate, PKMSorting.OrderByDateObtained, s => s.Generation >= 4),
new BoxManipSort(BoxManipType.SortName, list => list.OrderBySpeciesName(GameInfo.Strings.Species)),
new BoxManipSort(BoxManipType.SortFavorite, list => list.OrderByCustom(pk => !(pk as PB7)?.Favorite), s => s is SAV7b),
new BoxManipSort(BoxManipType.SortParty, (list, sav) => list.OrderByCustom(pk => ((SAV7b)sav).Storage.GetPartyIndex(pk.Box - 1, pk.Slot - 1)), s => s is SAV7b),
new BoxManipSort(BoxManipType.SortShiny, list => list.OrderByCustom(pk => !pk.IsShiny)),
new BoxManipSort(BoxManipType.SortRandom, list => list.OrderByCustom(_ => Util.Rand32())),
};
public static readonly IReadOnlyList<BoxManipSort> Advanced = new List<BoxManipSort>
{
new BoxManipSort(BoxManipType.SortUsage, PKMSorting.OrderByUsage, s => s.Generation >= 3),
new BoxManipSort(BoxManipType.SortPotential, list => list.OrderByCustom(pk => (pk.MaxIV * 6) - pk.IVTotal)),
new BoxManipSort(BoxManipType.SortTraining, list => list.OrderByCustom(pk => (pk.MaxEV * 6) - pk.EVTotal)),
new BoxManipSort(BoxManipType.SortOwner, (list, sav) => list.OrderByOwnership(sav)),
new BoxManipSort(BoxManipType.SortType, list => list.OrderByCustom(pk => pk.PersonalInfo.Type1, pk => pk.PersonalInfo.Type2)),
new BoxManipSort(BoxManipType.SortVersion, list => list.OrderByCustom(pk => pk.GenNumber, pk => pk.Version, pk => pk.Met_Location), s => s.Generation >= 3),
new BoxManipSort(BoxManipType.SortBST, list => list.OrderByCustom(pk => pk.PersonalInfo.BST)),
new BoxManipSort(BoxManipType.SortCP, list => list.OrderByCustom(pk => (pk as PB7)?.Stat_CP), s => s is SAV7b),
new BoxManipSort(BoxManipType.SortLegal, list => list.OrderByCustom(pk => !new LegalityAnalysis(pk).Valid)),
new BoxManipSort(BoxManipType.SortEncounterType, list => list.OrderByCustom(pk => new LegalityAnalysis(pk).Info?.EncounterMatch.GetEncounterTypeName())),
};
}
}

View file

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
public sealed class BoxManipSortComplex : BoxManipBase
{
private readonly Func<IEnumerable<PKM>, SaveFile, IEnumerable<PKM>> Sorter;
public BoxManipSortComplex(BoxManipType type, Func<IEnumerable<PKM>, SaveFile, IEnumerable<PKM>> sorter) : this(type, sorter, _ => true) { }
public BoxManipSortComplex(BoxManipType type, Func<IEnumerable<PKM>, SaveFile, IEnumerable<PKM>> sorter, Func<SaveFile, bool> usable) : base(type, usable) => Sorter = sorter;
public override string GetPrompt(bool all) => all ? MessageStrings.MsgSaveBoxSortAll : MessageStrings.MsgSaveBoxSortCurrent;
public override string GetFail(bool all) => all ? MessageStrings.MsgSaveBoxSortAllFailBattle : MessageStrings.MsgSaveBoxSortCurrentFailBattle;
public override string GetSuccess(bool all) => all ? MessageStrings.MsgSaveBoxSortAllSuccess : MessageStrings.MsgSaveBoxSortCurrentSuccess;
public override int Execute(SaveFile SAV, BoxManipParam param)
{
IEnumerable<PKM> Method(IEnumerable<PKM> p) => Sorter(p, SAV);
return SAV.SortBoxes(param.Start, param.Stop, Method, param.Reverse);
}
}
}

View file

@ -10,10 +10,10 @@ namespace PKHeX.Core
/// </summary>
public static readonly IReadOnlyList<IBoxManip>[] ManipCategories =
{
BoxManipClear.Common,
BoxManipSort.Common,
BoxManipSort.Advanced,
BoxManipModify.Common,
BoxManipBase.ClearCommon,
BoxManipBase.SortCommon,
BoxManipBase.SortAdvanced,
BoxManipBase.ModifyCommon,
};
public static readonly string[] ManipCategoryNames =
@ -36,7 +36,7 @@ namespace PKHeX.Core
/// </summary>
/// <param name="type">Manipulation type.</param>
/// <returns>Category Name</returns>
public static string GetManipCategoryName(this BoxManipType type)
public static string? GetManipCategoryName(this BoxManipType type)
{
for (int i = 0; i < ManipCategories.Length; i++)
{
@ -51,7 +51,7 @@ namespace PKHeX.Core
/// </summary>
/// <param name="manip">Manipulation type.</param>
/// <returns>Category Name</returns>
public static string GetManipCategoryName(this IBoxManip manip)
public static string? GetManipCategoryName(this IBoxManip manip)
{
for (int i = 0; i < ManipCategories.Length; i++)
{

View file

@ -14,7 +14,7 @@
/// <returns>True if operation succeeded, false if no changes made.</returns>
public bool Execute(IBoxManip manip, int box, bool allBoxes, bool reverse = false)
{
bool usable = manip.Usable?.Invoke(SAV) ?? true;
bool usable = manip.Usable.Invoke(SAV);
if (!usable)
return false;

View file

@ -7,7 +7,7 @@ namespace PKHeX.Core
/// Event number storage for more complex logic events.
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class EventWork<T> : EventVar
public sealed class EventWork<T> : EventVar where T : struct
{
public T Value;
public readonly IList<EventWorkVal> Options = new List<EventWorkVal> { new EventWorkVal() };

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using static PKHeX.Core.MessageStrings;
@ -10,7 +11,7 @@ namespace PKHeX.Core
/// </summary>
public class EventBlockDiff
{
public string Message { get; private set; }
public string Message { get; protected set; } = string.Empty;
public readonly List<int> SetFlags = new List<int>();
public readonly List<int> ClearedFlags = new List<int>();
public readonly List<string> WorkDiff = new List<string>();
@ -24,6 +25,11 @@ namespace PKHeX.Core
return;
var s1 = SaveUtil.GetVariantSAV(f1);
var s2 = SaveUtil.GetVariantSAV(f2);
if (s1 == null || s2 == null || s1.GetType() != s2.GetType())
{
Message = MsgSaveDifferentTypes;
return;
}
Diff(s1, s2);
}
@ -83,7 +89,7 @@ namespace PKHeX.Core
public sealed class EventWorkDiff7b : EventBlockDiff
{
public readonly List<int> WorkChanged = new List<int>();
private SaveFile S1;
private SaveFile? S1;
public EventWorkDiff7b(string f1, string f2)
{
@ -91,6 +97,11 @@ namespace PKHeX.Core
return;
var s1 = SaveUtil.GetVariantSAV(f1);
var s2 = SaveUtil.GetVariantSAV(f2);
if (s1 == null || s2 == null || s1.GetType() != s2.GetType())
{
Message = MsgSaveDifferentTypes;
return;
}
Diff(s1, s2);
}
@ -101,14 +112,16 @@ namespace PKHeX.Core
if (!SanityCheckSaveInfo(s1, s2))
return;
EventWorkUtil.DiffSavesFlag(((SAV7b)s1).EventWork, ((SAV7b)s2).EventWork, SetFlags, ClearedFlags);
EventWorkUtil.DiffSavesWork(((SAV7b)s1).EventWork, ((SAV7b)s2).EventWork, WorkChanged, WorkDiff);
EventWorkUtil.DiffSavesFlag(((SAV7b)s1).Blocks.EventWork, ((SAV7b)s2).Blocks.EventWork, SetFlags, ClearedFlags);
EventWorkUtil.DiffSavesWork(((SAV7b)s1).Blocks.EventWork, ((SAV7b)s2).Blocks.EventWork, WorkChanged, WorkDiff);
S1 = s1;
}
public List<string> Summarize()
public IReadOnlyList<string> Summarize()
{
var ew = ((SAV7b)S1).EventWork;
if (S1 == null)
return Array.Empty<string>();
var ew = ((SAV7b)S1).Blocks.EventWork;
var fOn = SetFlags.Select(z => new { Type = ew.GetFlagType(z, out var subIndex), Index = subIndex, Raw = z })
.Select(z => $"{z.Raw:0000}\t{true }\t{z.Index:0000}\t{z.Type}").ToArray();

View file

@ -103,7 +103,7 @@ namespace PKHeX.Core
{
var b = before.GetWork(i);
var a = after.GetWork(i);
if (b.Equals(a))
if (b is null || b.Equals(a))
continue;
changed.Add(i);

View file

@ -7,7 +7,7 @@ namespace PKHeX.Core
/// Editor object that unpacks <see cref="EventWork{T}"/> into flags & work groups, and handles value get/set operations.
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class SplitEventEditor<T>
public sealed class SplitEventEditor<T> where T : struct
{
public readonly IList<EventVarGroup> Work;
public readonly IList<EventVarGroup> Flag;

View file

@ -1,4 +1,7 @@
namespace PKHeX.Core
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <summary>
/// Environment for editing a <see cref="SaveFile"/>
@ -8,13 +11,77 @@
{
public readonly SaveFile SAV;
public readonly SlotEditor<T> Slots;
public readonly IPKMView PKMEditor;
public IPKMView PKMEditor { get; set; }
public SaveDataEditor() : this(FakeSaveFile.Default) { }
public SaveDataEditor(SaveFile sav)
{
SAV = sav;
Slots = new SlotEditor<T>(sav);
PKMEditor = new FakePKMEditor(SAV.BlankPKM);
}
public SaveDataEditor(SaveFile sav, IPKMView editor)
{
SAV = sav;
Slots = new SlotEditor<T>(sav);
PKMEditor = editor;
}
}
/// <summary>
/// Fakes the <see cref="IPKMView"/> interface interactions.
/// </summary>
public sealed class FakePKMEditor : IPKMView
{
public FakePKMEditor(PKM template) => Data = template;
public PKM Data { get; private set; }
public bool Unicode => true;
public bool HaX => false;
public bool ChangingFields { get; set; }
public bool EditsComplete => true;
public PKM PreparePKM(bool click = true) => Data;
public void PopulateFields(PKM pk, bool focus = true, bool skipConversionCheck = false) => Data = pk;
}
public sealed class FakeSaveFile : SaveFile
{
public static readonly FakeSaveFile Default = new FakeSaveFile();
protected override string BAKText => "Fake Save File";
public override SaveFile Clone() => this;
public override string Filter => string.Empty;
public override string Extension => string.Empty;
public override bool ChecksumsValid => true;
public override string ChecksumInfo => string.Empty;
public override int Generation => PKX.Generation;
public override string GetString(byte[] data, int offset, int length) => string.Empty;
public override byte[] SetString(string value, int maxLength, int PadToSize = 0, ushort PadWith = 0) => Array.Empty<byte>();
public override PersonalTable Personal => PKX.Personal;
public override int MaxEV => 0;
public override IReadOnlyList<ushort> HeldItems => Legal.HeldItems_GG;
public override int GetBoxOffset(int box) => -1;
public override string GetBoxName(int box) => $"Box {box:00}";
public override void SetBoxName(int box, string value) { }
public override int OTLength => 5;
public override int NickLength => 5;
public override int MaxMoveID => 5;
public override int MaxSpeciesID => 1;
public override int MaxItemID => 5;
public override int MaxBallID => 5;
public override int MaxGameID => 5;
public override int MaxAbilityID => 0;
public override int BoxCount => 1;
public override int GetPartyOffset(int slot) => -1;
protected override void SetChecksums() { }
public override Type PKMType => typeof(PKM);
protected override PKM GetPKM(byte[] data) => BlankPKM;
protected override byte[] DecryptPKM(byte[] data) => data;
public override PKM BlankPKM => new PK7();
public override int SIZE_STORED => 0;
protected override int SIZE_PARTY => 0;
}
}

View file

@ -5,7 +5,7 @@ namespace PKHeX.Core
public interface IBoxManip
{
BoxManipType Type { get; }
Func<SaveFile, bool> Usable { get; set; }
Func<SaveFile, bool> Usable { get; }
string GetPrompt(bool all);
string GetFail(bool all);

View file

@ -119,7 +119,7 @@ namespace PKHeX.Core
{
var list = new List<SlotInfoMisc>
{
new SlotInfoMisc(0, sav.GTS) {Type = StorageSlotType.GTS},
new SlotInfoMisc(0, sav.AllBlocks[07].Offset) {Type = StorageSlotType.GTS},
new SlotInfoMisc(0, sav.GetFusedSlotOffset(0)) {Type = StorageSlotType.Fused}
};
if (sav is SAV7USUM)

View file

@ -24,9 +24,6 @@ namespace PKHeX.Core
public ISlotInfo Undo()
{
if (!CanUndo)
return null;
var change = UndoStack.Pop();
var revert = GetReversion(change.Info, SAV);
AddRedo(revert);
@ -36,9 +33,6 @@ namespace PKHeX.Core
public ISlotInfo Redo()
{
if (!CanRedo)
return null;
var change = RedoStack.Pop();
var revert = GetReversion(change.Info, SAV);
AddUndo(revert);

View file

@ -100,12 +100,16 @@
public void Undo()
{
if (!Changelog.CanUndo)
return;
var slot = Changelog.Undo();
NotifySlotChanged(slot, SlotTouchType.Set, slot.Read(SAV));
}
public void Redo()
{
if (!Changelog.CanRedo)
return;
var slot = Changelog.Redo();
NotifySlotChanged(slot, SlotTouchType.Set, slot.Read(SAV));
}

View file

@ -12,9 +12,9 @@ namespace PKHeX.Core
/// </summary>
public List<ISlotViewer<T>> Subscribers { get; } = new List<ISlotViewer<T>>();
public ISlotInfo Previous { get; private set; }
public ISlotInfo? Previous { get; private set; }
public SlotTouchType PreviousType { get; private set; } = SlotTouchType.None;
public PKM PreviousPKM { get; private set; }
public PKM? PreviousPKM { get; private set; }
/// <summary>
/// Notifies all <see cref="Subscribers"/> with the latest slot change details.
@ -33,13 +33,18 @@ namespace PKHeX.Core
private void ResetView(ISlotViewer<T> sub, ISlotInfo slot, SlotTouchType type, PKM pkm)
{
if (PreviousPKM != null)
if (Previous != null)
sub.NotifySlotOld(Previous);
if (!(slot is SlotInfoBox b) || sub.ViewIndex == b.Box)
sub.NotifySlotChanged(slot, type, pkm);
}
public void ResetView(ISlotViewer<T> sub) => ResetView(sub, Previous, PreviousType, PreviousPKM);
public void ResetView(ISlotViewer<T> sub)
{
if (Previous == null || PreviousPKM == null)
return;
ResetView(sub, Previous, PreviousType, PreviousPKM);
}
}
}

View file

@ -6,11 +6,17 @@ namespace PKHeX.Core
/// <typeparam name="T"></typeparam>
public sealed class SlotViewInfo<T>
{
public ISlotInfo Slot;
public ISlotViewer<T> View;
public readonly ISlotInfo Slot;
public readonly ISlotViewer<T> View;
public PKM ReadCurrent() => Slot.Read(View.SAV);
public bool CanWriteTo() => Slot.CanWriteTo(View.SAV);
public WriteBlockedMessage CanWriteTo(PKM pkm) => Slot.CanWriteTo(View.SAV, pkm);
public SlotViewInfo(ISlotInfo slot, ISlotViewer<T> view)
{
Slot = slot;
View = view;
}
}
}

View file

@ -257,7 +257,7 @@ namespace PKHeX.Core
return GetText(strings);
}
private string GetText(GameStrings strings = null)
private string GetText(GameStrings? strings = null)
{
if (Species <= 0 || Species > MAX_SPECIES)
return string.Empty;

View file

@ -30,7 +30,17 @@ namespace PKHeX.Core
LegalMoveDataSource = HaXMoveDataSource.Where(m => !Legal.Z_Moves.Contains(m.Value)).ToList();
VersionDataSource = GetVersionList(s);
InitializeMetSources();
MetGen2 = CreateGen2(s);
MetGen3 = CreateGen3(s);
MetGen3CXD = CreateGen3CXD(s);
MetGen4 = CreateGen4(s);
MetGen5 = CreateGen5(s);
MetGen6 = CreateGen6(s);
MetGen7 = CreateGen7(s);
MetGen7GG = CreateGen7GG(s);
MetGen8 = CreateGen8(s);
Memories = new MemoryStrings(s);
}
@ -46,17 +56,17 @@ namespace PKHeX.Core
public readonly IReadOnlyList<ComboItem> HaXMoveDataSource;
public readonly IReadOnlyList<ComboItem> EncounterTypeDataSource;
private IReadOnlyList<ComboItem> MetGen2 { get; set; }
private IReadOnlyList<ComboItem> MetGen3 { get; set; }
private IReadOnlyList<ComboItem> MetGen3CXD { get; set; }
private IReadOnlyList<ComboItem> MetGen4 { get; set; }
private IReadOnlyList<ComboItem> MetGen5 { get; set; }
private IReadOnlyList<ComboItem> MetGen6 { get; set; }
private IReadOnlyList<ComboItem> MetGen7 { get; set; }
private IReadOnlyList<ComboItem> MetGen7GG { get; set; }
private IReadOnlyList<ComboItem> MetGen8 { get; set; }
private readonly IReadOnlyList<ComboItem> MetGen2;
private readonly IReadOnlyList<ComboItem> MetGen3;
private readonly IReadOnlyList<ComboItem> MetGen3CXD;
private readonly IReadOnlyList<ComboItem> MetGen4;
private readonly IReadOnlyList<ComboItem> MetGen5;
private readonly IReadOnlyList<ComboItem> MetGen6;
private readonly IReadOnlyList<ComboItem> MetGen7;
private readonly IReadOnlyList<ComboItem> MetGen7GG;
private readonly IReadOnlyList<ComboItem> MetGen8;
private IReadOnlyList<ComboItem> GetVersionList(GameStrings s)
private static IReadOnlyList<ComboItem> GetVersionList(GameStrings s)
{
var list = s.gamelist;
var ver = Util.GetCBList(list,
@ -72,95 +82,102 @@ namespace PKHeX.Core
return ver;
}
private void InitializeMetSources()
private List<ComboItem> CreateGen2(GameStrings s)
{
var s = Source;
// Gen 2
{
var met_list = Util.GetCBList(s.metGSC_00000, Enumerable.Range(0, 0x5F).ToArray());
Util.AddCBWithOffset(met_list, s.metGSC_00000, 00000, 0x7E, 0x7F);
MetGen2 = met_list;
}
// Gen 3
{
var met_list = Util.GetCBList(s.metRSEFRLG_00000, Enumerable.Range(0, 213).ToArray());
Util.AddCBWithOffset(met_list, s.metRSEFRLG_00000, 00000, 253, 254, 255);
MetGen3 = met_list;
MetGen3CXD = Util.GetCBList(s.metCXD_00000, Enumerable.Range(0, s.metCXD_00000.Length).ToArray()).Where(c => c.Text.Length > 0).ToList();
}
// Gen 4
{
var met_list = Util.GetCBList(s.metHGSS_00000, 0);
Util.AddCBWithOffset(met_list, s.metHGSS_02000, 2000, Locations.Daycare4);
Util.AddCBWithOffset(met_list, s.metHGSS_02000, 2000, Locations.LinkTrade4);
Util.AddCBWithOffset(met_list, s.metHGSS_03000, 3000, Locations.Ranger4);
Util.AddCBWithOffset(met_list, s.metHGSS_00000, 0000, Legal.Met_HGSS_0);
Util.AddCBWithOffset(met_list, s.metHGSS_02000, 2000, Legal.Met_HGSS_2);
Util.AddCBWithOffset(met_list, s.metHGSS_03000, 3000, Legal.Met_HGSS_3);
MetGen4 = met_list;
}
// Gen 5
{
var met_list = Util.GetCBList(s.metBW2_00000, 0);
Util.AddCBWithOffset(met_list, s.metBW2_60000, 60001, Locations.Daycare5);
Util.AddCBWithOffset(met_list, s.metBW2_30000, 30001, Locations.LinkTrade5);
Util.AddCBWithOffset(met_list, s.metBW2_00000, 00000, Legal.Met_BW2_0);
Util.AddCBWithOffset(met_list, s.metBW2_30000, 30001, Legal.Met_BW2_3);
Util.AddCBWithOffset(met_list, s.metBW2_40000, 40001, Legal.Met_BW2_4);
Util.AddCBWithOffset(met_list, s.metBW2_60000, 60001, Legal.Met_BW2_6);
MetGen5 = met_list;
}
// Gen 6
{
var met_list = Util.GetCBList(s.metXY_00000, 0);
Util.AddCBWithOffset(met_list, s.metXY_60000, 60001, Locations.Daycare5);
Util.AddCBWithOffset(met_list, s.metXY_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(met_list, s.metXY_00000, 00000, Legal.Met_XY_0);
Util.AddCBWithOffset(met_list, s.metXY_30000, 30001, Legal.Met_XY_3);
Util.AddCBWithOffset(met_list, s.metXY_40000, 40001, Legal.Met_XY_4);
Util.AddCBWithOffset(met_list, s.metXY_60000, 60001, Legal.Met_XY_6);
MetGen6 = met_list;
}
// Gen 7
{
var met_list = Util.GetCBList(s.metSM_00000, 0);
Util.AddCBWithOffset(met_list, s.metSM_60000, 60001, Locations.Daycare5);
Util.AddCBWithOffset(met_list, s.metSM_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(met_list, s.metSM_00000, 00000, Legal.Met_SM_0);
Util.AddCBWithOffset(met_list, s.metSM_30000, 30001, Legal.Met_SM_3);
Util.AddCBWithOffset(met_list, s.metSM_40000, 40001, Legal.Met_SM_4);
Util.AddCBWithOffset(met_list, s.metSM_60000, 60001, Legal.Met_SM_6);
MetGen7 = met_list;
}
// Gen 7 GG
{
var met_list = Util.GetCBList(s.metGG_00000, 0);
Util.AddCBWithOffset(met_list, s.metGG_60000, 60001, 60002);
Util.AddCBWithOffset(met_list, s.metGG_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(met_list, s.metGG_00000, 00000, Legal.Met_GG_0);
Util.AddCBWithOffset(met_list, s.metGG_30000, 30001, Legal.Met_GG_3);
Util.AddCBWithOffset(met_list, s.metGG_40000, 40001, Legal.Met_GG_4);
Util.AddCBWithOffset(met_list, s.metGG_60000, 60001, Legal.Met_GG_6);
MetGen7GG = met_list;
}
// Gen 8
{
var met_list = Util.GetCBList(s.metSWSH_00000, 0);
Util.AddCBWithOffset(met_list, s.metSWSH_60000, 60001, 60002);
Util.AddCBWithOffset(met_list, s.metSWSH_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(met_list, s.metSWSH_00000, 00000, Legal.Met_SWSH_0);
Util.AddCBWithOffset(met_list, s.metSWSH_30000, 30001, Legal.Met_SWSH_3);
Util.AddCBWithOffset(met_list, s.metSWSH_40000, 40001, Legal.Met_SWSH_4);
Util.AddCBWithOffset(met_list, s.metSWSH_60000, 60001, Legal.Met_SWSH_6);
MetGen8 = met_list;
}
var met_list = Util.GetCBList(s.metGSC_00000, Enumerable.Range(0, 0x5F).ToArray());
Util.AddCBWithOffset(met_list, s.metGSC_00000, 00000, 0x7E, 0x7F);
return met_list;
}
public IReadOnlyList<ComboItem> GetItemDataSource(GameVersion game, int generation, int MaxItemID, IEnumerable<ushort> allowed = null, bool HaX = false)
private List<ComboItem> CreateGen3(GameStrings s)
{
var met_list = Util.GetCBList(s.metRSEFRLG_00000, Enumerable.Range(0, 213).ToArray());
Util.AddCBWithOffset(met_list, s.metRSEFRLG_00000, 00000, 253, 254, 255);
return met_list;
}
private static List<ComboItem> CreateGen3CXD(GameStrings s)
{
return Util.GetCBList(s.metCXD_00000, Enumerable.Range(0, s.metCXD_00000.Length).ToArray()).Where(c => c.Text.Length > 0).ToList();
}
private static List<ComboItem> CreateGen4(GameStrings s)
{
var met_list = Util.GetCBList(s.metHGSS_00000, 0);
Util.AddCBWithOffset(met_list, s.metHGSS_02000, 2000, Locations.Daycare4);
Util.AddCBWithOffset(met_list, s.metHGSS_02000, 2000, Locations.LinkTrade4);
Util.AddCBWithOffset(met_list, s.metHGSS_03000, 3000, Locations.Ranger4);
Util.AddCBWithOffset(met_list, s.metHGSS_00000, 0000, Legal.Met_HGSS_0);
Util.AddCBWithOffset(met_list, s.metHGSS_02000, 2000, Legal.Met_HGSS_2);
Util.AddCBWithOffset(met_list, s.metHGSS_03000, 3000, Legal.Met_HGSS_3);
return met_list;
}
private static List<ComboItem> CreateGen5(GameStrings s)
{
var met_list = Util.GetCBList(s.metBW2_00000, 0);
Util.AddCBWithOffset(met_list, s.metBW2_60000, 60001, Locations.Daycare5);
Util.AddCBWithOffset(met_list, s.metBW2_30000, 30001, Locations.LinkTrade5);
Util.AddCBWithOffset(met_list, s.metBW2_00000, 00000, Legal.Met_BW2_0);
Util.AddCBWithOffset(met_list, s.metBW2_30000, 30001, Legal.Met_BW2_3);
Util.AddCBWithOffset(met_list, s.metBW2_40000, 40001, Legal.Met_BW2_4);
Util.AddCBWithOffset(met_list, s.metBW2_60000, 60001, Legal.Met_BW2_6);
return met_list;
}
private static List<ComboItem> CreateGen6(GameStrings s)
{
var met_list = Util.GetCBList(s.metXY_00000, 0);
Util.AddCBWithOffset(met_list, s.metXY_60000, 60001, Locations.Daycare5);
Util.AddCBWithOffset(met_list, s.metXY_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(met_list, s.metXY_00000, 00000, Legal.Met_XY_0);
Util.AddCBWithOffset(met_list, s.metXY_30000, 30001, Legal.Met_XY_3);
Util.AddCBWithOffset(met_list, s.metXY_40000, 40001, Legal.Met_XY_4);
Util.AddCBWithOffset(met_list, s.metXY_60000, 60001, Legal.Met_XY_6);
return met_list;
}
private static List<ComboItem> CreateGen7(GameStrings s)
{
var met_list = Util.GetCBList(s.metSM_00000, 0);
Util.AddCBWithOffset(met_list, s.metSM_60000, 60001, Locations.Daycare5);
Util.AddCBWithOffset(met_list, s.metSM_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(met_list, s.metSM_00000, 00000, Legal.Met_SM_0);
Util.AddCBWithOffset(met_list, s.metSM_30000, 30001, Legal.Met_SM_3);
Util.AddCBWithOffset(met_list, s.metSM_40000, 40001, Legal.Met_SM_4);
Util.AddCBWithOffset(met_list, s.metSM_60000, 60001, Legal.Met_SM_6);
return met_list;
}
private static List<ComboItem> CreateGen7GG(GameStrings s)
{
var met_list = Util.GetCBList(s.metGG_00000, 0);
Util.AddCBWithOffset(met_list, s.metGG_60000, 60001, 60002);
Util.AddCBWithOffset(met_list, s.metGG_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(met_list, s.metGG_00000, 00000, Legal.Met_GG_0);
Util.AddCBWithOffset(met_list, s.metGG_30000, 30001, Legal.Met_GG_3);
Util.AddCBWithOffset(met_list, s.metGG_40000, 40001, Legal.Met_GG_4);
Util.AddCBWithOffset(met_list, s.metGG_60000, 60001, Legal.Met_GG_6);
return met_list;
}
private static List<ComboItem> CreateGen8(GameStrings s)
{
var met_list = Util.GetCBList(s.metSWSH_00000, 0);
Util.AddCBWithOffset(met_list, s.metSWSH_60000, 60001, 60002);
Util.AddCBWithOffset(met_list, s.metSWSH_30000, 30001, Locations.LinkTrade6);
Util.AddCBWithOffset(met_list, s.metSWSH_00000, 00000, Legal.Met_SWSH_0);
Util.AddCBWithOffset(met_list, s.metSWSH_30000, 30001, Legal.Met_SWSH_3);
Util.AddCBWithOffset(met_list, s.metSWSH_40000, 40001, Legal.Met_SWSH_4);
Util.AddCBWithOffset(met_list, s.metSWSH_60000, 60001, Legal.Met_SWSH_6);
return met_list;
}
public IReadOnlyList<ComboItem> GetItemDataSource(GameVersion game, int generation, int MaxItemID, IEnumerable<ushort>? allowed = null, bool HaX = false)
{
var items = Source.GetItemStrings(generation, game);
return Util.GetCBList(items, (allowed == null || HaX ? Enumerable.Range(0, MaxItemID) : allowed.Select(i => (int)i)).ToArray());
var range = (allowed == null || HaX ? Enumerable.Range(0, MaxItemID) : allowed.Select(i => (int) i)).ToArray();
return Util.GetCBList(items, range);
}
/// <summary>

View file

@ -22,22 +22,17 @@ namespace PKHeX.Core
public static GameStrings GetStrings(int index)
{
return Languages[index] ?? (Languages[index] = new GameStrings(GameLanguage.Language2Char(index)));
return Languages[index] ??= new GameStrings(GameLanguage.Language2Char(index));
}
public static GameStrings Strings
{
get => _strings;
set
{
_strings = value;
Sources = new GameDataSource(_strings);
FilteredSources = null;
}
set => Sources = new GameDataSource(_strings = value);
}
public static GameDataSource Sources { get; set; }
public static FilteredGameDataSource FilteredSources { get; set; }
public static GameDataSource Sources { get; private set; } = new GameDataSource(_strings);
public static FilteredGameDataSource FilteredSources { get; set; } = new FilteredGameDataSource(FakeSaveFile.Default, Sources, false);
public static string GetVersionName(GameVersion version)
{

View file

@ -22,7 +22,7 @@ namespace PKHeX.Core
/// </summary>
public IReadOnlyList<CheckResult> Results => Parse;
private IEncounterable EncounterOriginalGB;
private IEncounterable? EncounterOriginalGB;
/// <summary>
/// Matched encounter data for the <see cref="pkm"/>.
@ -52,7 +52,7 @@ namespace PKHeX.Core
/// <summary>
/// Contains various data reused for multiple checks.
/// </summary>
public LegalInfo Info { get; private set; }
public readonly LegalInfo Info;
/// <summary>
/// Creates a report message with optional verbosity for in-depth analysis.
@ -85,7 +85,7 @@ namespace PKHeX.Core
}
}
private int[] _allSuggestedMoves, _allSuggestedRelearnMoves;
private int[]? _allSuggestedMoves, _allSuggestedRelearnMoves;
public int[] AllSuggestedMovesAndRelearn => AllSuggestedMoves.Concat(AllSuggestedRelearnMoves).ToArray();
private string EncounterName
@ -97,7 +97,7 @@ namespace PKHeX.Core
}
}
private string EncounterLocation
private string? EncounterLocation
{
get
{
@ -111,15 +111,22 @@ namespace PKHeX.Core
/// </summary>
/// <param name="pk">Input data to check</param>
/// <param name="table"><see cref="SaveFile"/> specific personal data</param>
public LegalityAnalysis(PKM pk, PersonalTable table = null)
public LegalityAnalysis(PKM pk, PersonalTable? table = null)
{
pkm = pk;
PersonalInfo = table?.GetFormeEntry(pkm.Species, pkm.AltForm) ?? pkm.PersonalInfo;
if (pkm.Format <= 2) // prior to storing GameVersion
pkm.TradebackStatus = GBRestrictions.GetTradebackStatusInitial(pkm);
#if SUPPRESS
try
#endif
{
PersonalInfo = table?.GetFormeEntry(pkm.Species, pkm.AltForm) ?? pkm.PersonalInfo;
ParseLegality();
Info = EncounterFinder.FindVerifiedEncounter(pkm);
if (!pkm.IsOriginValid)
AddLine(Severity.Invalid, LEncConditionBadSpecies, CheckIdentifier.GameOrigin);
GetParseMethod()();
if (Parse.Count == 0)
return;
@ -134,6 +141,7 @@ namespace PKHeX.Core
#if SUPPRESS
catch (Exception e)
{
Info = new LegalInfo(pkm);
System.Diagnostics.Debug.WriteLine(e.Message);
Valid = false;
AddLine(Severity.Invalid, L_AError, CheckIdentifier.Misc);
@ -143,33 +151,33 @@ namespace PKHeX.Core
Parsed = true;
}
private void ParseLegality()
private Action GetParseMethod()
{
if (!pkm.IsOriginValid)
AddLine(Severity.Invalid, LEncConditionBadSpecies, CheckIdentifier.GameOrigin);
if (pkm.Format <= 2) // prior to storing GameVersion
{
ParsePK1();
return;
}
switch (pkm.GenNumber)
{
case 3: ParsePK3(); return;
case 4: ParsePK4(); return;
case 5: ParsePK5(); return;
case 6: ParsePK6(); return;
return ParsePK1;
case 1: case 2:
case 7: ParsePK7(); return;
int gen = pkm.GenNumber;
if (gen <= 0)
gen = pkm.Format;
return gen switch
{
3 => ParsePK3,
4 => ParsePK4,
5 => ParsePK5,
6 => ParsePK6,
case 8: ParsePK8(); return;
}
1 => ParsePK7,
2 => ParsePK7,
7 => ParsePK7,
8 => (Action)ParsePK8,
_ => throw new Exception()
};
}
private void ParsePK1()
{
pkm.TradebackStatus = GBRestrictions.GetTradebackStatusInitial(pkm);
UpdateInfo();
if (pkm.TradebackStatus == TradebackType.Any && Info.Generation != pkm.Format)
pkm.TradebackStatus = TradebackType.WasTradeback; // Example: GSC Pokemon with only possible encounters in RBY, like the legendary birds
@ -267,7 +275,6 @@ namespace PKHeX.Core
private void UpdateInfo()
{
Info = EncounterFinder.FindVerifiedEncounter(pkm);
Parse.AddRange(Info.Parse);
}
@ -385,7 +392,11 @@ namespace PKHeX.Core
lines.Add(string.Format(L_F0_1, "Location", loc));
if (pkm.VC)
lines.Add(string.Format(L_F0_1, nameof(GameVersion), Info.Game));
var pidiv = Info.PIDIV ?? MethodFinder.Analyze(pkm);
if (!Info.PIDParsed)
Info.PIDIV = MethodFinder.Analyze(pkm);
var pidiv = Info.PIDIV;
if (pidiv != null)
{
if (!pidiv.NoSeed)
@ -456,6 +467,6 @@ namespace PKHeX.Core
/// <summary>
/// Gets an object containing met data properties that might be legal.
/// </summary>
public EncounterStatic GetSuggestedMetInfo() => EncounterSuggestion.GetSuggestedMetInfo(pkm);
public EncounterStatic? GetSuggestedMetInfo() => EncounterSuggestion.GetSuggestedMetInfo(pkm);
}
}

View file

@ -10,7 +10,7 @@ namespace PKHeX.Core
public abstract class EncounterArea
{
public int Location;
public EncounterSlot[] Slots;
public EncounterSlot[] Slots = Array.Empty<EncounterSlot>();
/// <summary>
/// Gets the encounter areas for species with same level range and same slot type at same location

View file

@ -27,7 +27,7 @@ namespace PKHeX.Core
protected override IEnumerable<EncounterSlot> GetFilteredSlots(PKM pkm, IEnumerable<EncounterSlot> slots, int minLevel)
{
EncounterSlot slotMax = null;
EncounterSlot? slotMax = null;
foreach (EncounterSlot s in slots)
{
if (Legal.WildForms.Contains(pkm.Species) && s.Form != pkm.AltForm)

View file

@ -11,7 +11,7 @@ namespace PKHeX.Core
{
protected override IEnumerable<EncounterSlot> GetFilteredSlots(PKM pkm, IEnumerable<EncounterSlot> slots, int minLevel)
{
EncounterSlot slotMax = null;
EncounterSlot? slotMax = null;
void CachePressureSlot(EncounterSlot s)
{
if (slotMax == null || s.LevelMax > slotMax.LevelMax)

View file

@ -21,7 +21,7 @@ namespace PKHeX.Core
yield break;
}
EncounterSlot slotMax = null;
EncounterSlot? slotMax = null;
void CachePressureSlot(EncounterSlot s)
{
if (slotMax != null && s.LevelMax > slotMax.LevelMax)

View file

@ -30,26 +30,19 @@ namespace PKHeX.Core
internal static TreesArea[] GetArray(byte[][] entries) => entries.Select(z => new TreesArea(z)).ToArray();
public int Location { get; private set; }
private TreeEncounterAvailable[] TrainerModerateEncounterTree { get; set; }
private TreeEncounterAvailable[] TrainerLowEncounterTree { get; set; }
private int[] ValidTreeIndex { get; set; }
private int[] InvalidTreeIndex { get; set; }
private TreeCoordinates[] ValidTrees { get; set; }
private TreeCoordinates[] InvalidTrees { get; set; }
public readonly int Location;
private readonly TreeEncounterAvailable[] TrainerModerateEncounterTree;
private readonly TreeEncounterAvailable[] TrainerLowEncounterTree;
private readonly int[] ValidTreeIndex;
private readonly int[] InvalidTreeIndex;
private readonly TreeCoordinates[] ValidTrees;
private readonly TreeCoordinates[] InvalidTrees;
public TreeEncounterAvailable[] GetTrees(SlotType t) => t == SlotType.Headbutt
? TrainerModerateEncounterTree
: TrainerLowEncounterTree;
private TreesArea(byte[] entry)
{
ReadAreaRawData(entry);
GenerateAreaTreeIndex();
GenerateAreaTrainerEncounters();
}
private void ReadAreaRawData(byte[] entry)
{
// Coordinates of trees were obtained with the program G2Map
// ValidTrees are those accessible by the player
@ -64,18 +57,12 @@ namespace PKHeX.Core
ofs++;
for (int i = 0; i < InvalidTrees.Length; i++, ofs += 2)
InvalidTrees[i] = new TreeCoordinates(entry[ofs], entry[ofs + 1]);
}
private void GenerateAreaTreeIndex()
{
// For legality purposes, only the tree index is needed.
// Group the trees data by their index; trees that share indexes are indistinguishable from one another
ValidTreeIndex = ValidTrees.Select(t => t.Index).Distinct().OrderBy(i => i).ToArray();
InvalidTreeIndex = InvalidTrees.Select(t => t.Index).Distinct().OrderBy(i => i).Except(ValidTreeIndex).ToArray();
}
private void GenerateAreaTrainerEncounters()
{
// Check for every trainer pivot index if there are trees with moderate encounter and low encounter available in the area
TrainerModerateEncounterTree = new TreeEncounterAvailable[PivotCount];
TrainerLowEncounterTree = new TreeEncounterAvailable[PivotCount];

View file

@ -16,56 +16,59 @@ namespace PKHeX.Core
public static string EReaderBerryDisplayName => string.Format(LegalityCheckStrings.L_XEnigmaBerry_0, Util.ToTitleCase(EReaderBerryName.ToLower()));
// Gen 1
internal static readonly Learnset[] LevelUpRB = Learnset1.GetArray(Util.GetBinaryResource("lvlmove_rb.pkl"), MaxSpeciesID_1);
internal static readonly Learnset[] LevelUpY = Learnset1.GetArray(Util.GetBinaryResource("lvlmove_y.pkl"), MaxSpeciesID_1);
internal static readonly Learnset[] LevelUpRB = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_rb.pkl"), MaxSpeciesID_1);
internal static readonly Learnset[] LevelUpY = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_y.pkl"), MaxSpeciesID_1);
// Gen 2
internal static readonly EggMoves[] EggMovesGS = EggMoves2.GetArray(Util.GetBinaryResource("eggmove_gs.pkl"), MaxSpeciesID_2);
internal static readonly Learnset[] LevelUpGS = Learnset1.GetArray(Util.GetBinaryResource("lvlmove_gs.pkl"), MaxSpeciesID_2);
internal static readonly Learnset[] LevelUpGS = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_gs.pkl"), MaxSpeciesID_2);
internal static readonly EggMoves[] EggMovesC = EggMoves2.GetArray(Util.GetBinaryResource("eggmove_c.pkl"), MaxSpeciesID_2);
internal static readonly Learnset[] LevelUpC = Learnset1.GetArray(Util.GetBinaryResource("lvlmove_c.pkl"), MaxSpeciesID_2);
internal static readonly Learnset[] LevelUpC = LearnsetReader.GetArray(Util.GetBinaryResource("lvlmove_c.pkl"), MaxSpeciesID_2);
// Gen 3
internal static readonly Learnset[] LevelUpE = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_e.pkl"), "em"));
internal static readonly Learnset[] LevelUpRS = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_rs.pkl"), "rs"));
internal static readonly Learnset[] LevelUpFR = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_fr.pkl"), "fr"));
internal static readonly Learnset[] LevelUpLG = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_lg.pkl"), "lg"));
internal static readonly EggMoves[] EggMovesRS = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_rs.pkl"), "rs"));
internal static readonly Learnset[] LevelUpE = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_e.pkl"), "em"));
internal static readonly Learnset[] LevelUpRS = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_rs.pkl"), "rs"));
internal static readonly Learnset[] LevelUpFR = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_fr.pkl"), "fr"));
internal static readonly Learnset[] LevelUpLG = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_lg.pkl"), "lg"));
internal static readonly EggMoves6[] EggMovesRS = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_rs.pkl"), "rs"));
// Gen 4
internal static readonly Learnset[] LevelUpDP = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_dp.pkl"), "dp"));
internal static readonly Learnset[] LevelUpPt = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_pt.pkl"), "pt"));
internal static readonly Learnset[] LevelUpHGSS = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_hgss.pkl"), "hs"));
internal static readonly EggMoves[] EggMovesDPPt = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_dppt.pkl"), "dp"));
internal static readonly EggMoves[] EggMovesHGSS = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_hgss.pkl"), "hs"));
internal static readonly Learnset[] LevelUpDP = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_dp.pkl"), "dp"));
internal static readonly Learnset[] LevelUpPt = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_pt.pkl"), "pt"));
internal static readonly Learnset[] LevelUpHGSS = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_hgss.pkl"), "hs"));
internal static readonly EggMoves6[] EggMovesDPPt = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_dppt.pkl"), "dp"));
internal static readonly EggMoves6[] EggMovesHGSS = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_hgss.pkl"), "hs"));
// Gen 5
internal static readonly Learnset[] LevelUpBW = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_bw.pkl"), "51"));
internal static readonly Learnset[] LevelUpB2W2 = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_b2w2.pkl"), "52"));
internal static readonly EggMoves[] EggMovesBW = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_bw.pkl"), "bw"));
internal static readonly Learnset[] LevelUpBW = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_bw.pkl"), "51"));
internal static readonly Learnset[] LevelUpB2W2 = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_b2w2.pkl"), "52"));
internal static readonly EggMoves6[] EggMovesBW = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_bw.pkl"), "bw"));
// Gen 6
internal static readonly EggMoves[] EggMovesXY = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_xy.pkl"), "xy"));
internal static readonly Learnset[] LevelUpXY = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_xy.pkl"), "xy"));
internal static readonly EggMoves[] EggMovesAO = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_ao.pkl"), "ao"));
internal static readonly Learnset[] LevelUpAO = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_ao.pkl"), "ao"));
internal static readonly EggMoves6[] EggMovesXY = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_xy.pkl"), "xy"));
internal static readonly Learnset[] LevelUpXY = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_xy.pkl"), "xy"));
internal static readonly EggMoves6[] EggMovesAO = EggMoves6.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_ao.pkl"), "ao"));
internal static readonly Learnset[] LevelUpAO = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_ao.pkl"), "ao"));
// Gen 7
internal static readonly EggMoves[] EggMovesSM = EggMoves7.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
internal static readonly Learnset[] LevelUpSM = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
internal static readonly EggMoves[] EggMovesUSUM = EggMoves7.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpUSUM = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpGG = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_gg.pkl"), "gg"));
internal static readonly EggMoves7[] EggMovesSM = EggMoves7.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
internal static readonly Learnset[] LevelUpSM = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
internal static readonly EggMoves7[] EggMovesUSUM = EggMoves7.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpUSUM = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_uu.pkl"), "uu"));
internal static readonly Learnset[] LevelUpGG = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_gg.pkl"), "gg"));
// Gen 8
internal static readonly EggMoves[] EggMovesSWSH = EggMoves7.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
internal static readonly Learnset[] LevelUpSWSH = Learnset6.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
internal static readonly EggMoves7[] EggMovesSWSH = EggMoves7.GetArray(Data.UnpackMini(Util.GetBinaryResource("eggmove_sm.pkl"), "sm"));
internal static readonly Learnset[] LevelUpSWSH = LearnsetReader.GetArray(Data.UnpackMini(Util.GetBinaryResource("lvlmove_sm.pkl"), "sm"));
// Setup Help
static Legal()
{
// Misc Fixes to Data pertaining to legality constraints
Array.Resize(ref EggMovesUSUM[198].Moves, 15); // Remove Punishment from USUM Murkrow (no species can pass it #1829)
// Remove Punishment from USUM Murkrow (no species can pass it #1829)
// DONE: Egg Move Data for EggMovesUSUM no longer has it at the end
// Prevent Silvally from being tutored Fire/Water Pledge (logic can only tutor one, and Grass is first)
var pi = PersonalTable.USUM[773];
pi.TypeTutors[1] = false; // fire
@ -563,7 +566,7 @@ namespace PKHeX.Core
// Check also if the current encounter include the evolve move as an special move
// That means the pokemon have the move from the encounter level
if (info.EncounterMatch is IMoveset s && s.Moves?.Any(m => moves.Contains(m)) == true)
if (info.EncounterMatch is IMoveset s && s.Moves.Any(m => moves.Contains(m)))
LearnLevel = Math.Min(LearnLevel, info.EncounterMatch.LevelMin);
// If the encounter is a player hatched egg check if the move could be an egg move or inherited level up move

View file

@ -10,13 +10,13 @@ namespace PKHeX.Core
/// <param name="fileData">Packed data</param>
/// <param name="identifier">Signature expected in the first two bytes (ASCII)</param>
/// <returns>Unpacked array containing all files that were packed.</returns>
public static byte[][] UnpackMini(byte[] fileData, string identifier)
public static byte[][] UnpackMini(byte[]? fileData, string identifier)
{
if (fileData == null || fileData.Length < 4)
return null;
throw new ArgumentException(nameof(fileData));
if (identifier[0] != fileData[0] || identifier[1] != fileData[1])
return null;
throw new ArgumentException(nameof(identifier));
int count = BitConverter.ToUInt16(fileData, 2); int ctr = 4;
int start = BitConverter.ToInt32(fileData, ctr); ctr += 4;

View file

@ -38,11 +38,11 @@ namespace PKHeX.Core
private static HashSet<PGF> GetPGFDB(byte[] bin) => new HashSet<PGF>(ArrayUtil.EnumerateSplit(bin, PGF.Size).Select(d => new PGF(d)));
private static HashSet<WC6> GetWC6DB(byte[] wc6bin, byte[] wc6full) => new HashSet<WC6>(
ArrayUtil.EnumerateSplit(wc6full, WC6.SizeFull).Select(d => new WC6(d))
ArrayUtil.EnumerateSplit(wc6full, WC6Full.Size).Select(d => new WC6Full(d).Gift)
.Concat(ArrayUtil.EnumerateSplit(wc6bin, WC6.Size).Select(d => new WC6(d))));
private static HashSet<WC7> GetWC7DB(byte[] wc7bin, byte[] wc7full) => new HashSet<WC7>(
ArrayUtil.EnumerateSplit(wc7full, WC7.SizeFull).Select(d => new WC7(d))
ArrayUtil.EnumerateSplit(wc7full, WC7Full.Size).Select(d => new WC7Full(d).Gift)
.Concat(ArrayUtil.EnumerateSplit(wc7bin, WC7.Size).Select(d => new WC7(d))));
private static HashSet<WB7> GetWB7DB(byte[] wc7full) => new HashSet<WB7>(ArrayUtil.EnumerateSplit(wc7full, WB7.SizeFull).Select(d => new WB7(d)));

View file

@ -243,13 +243,13 @@ namespace PKHeX.Core
internal static void MarkEncounterTradeStrings(EncounterTrade[] table, string[][] strings)
{
int half = strings[1].Length / 2;
for (var i = 0; i < half; i++)
for (int i = 0; i < half; i++)
{
var t = table[i];
t.Nicknames = getNames(i, strings);
t.TrainerNames = getNames(i + half, strings);
}
string[] getNames(int i, IEnumerable<string[]> names) => names?.Select(z => z?.Length > i ? z[i] : null).ToArray();
string[] getNames(int i, IEnumerable<string[]> names) => names.Select(z => z.Length > i ? z[i] : string.Empty).ToArray();
}
internal static void MarkEncounterGame(IEnumerable<IVersion> table, GameVersion version)

View file

@ -31,7 +31,7 @@ namespace PKHeX.Core
StaticRBY.SetVersion(GameVersion.RBY);
}
internal static readonly string[] TradeOTG1 = {null, "トレーナー", "Trainer", "Dresseur", "Allenatore", "Trainer", null, "Entrenador", "트레이너"};
internal static readonly string[] TradeOTG1 = {string.Empty, "トレーナー", "Trainer", "Dresseur", "Allenatore", "Trainer", string.Empty, "Entrenador", "트레이너"};
private static EncounterArea1[] GetAreas()
{

View file

@ -273,6 +273,13 @@ namespace PKHeX.Core
private static readonly string[][] TradeRSE = Util.GetLanguageStrings7(tradeRSE);
private static readonly string[][] TradeFRLG = Util.GetLanguageStrings7(tradeFRLG);
private static readonly int[] MoveSwarmSurskit = { 145, 098 }; /* Bubble, Quick Attack */
private static readonly int[] MoveSwarmSeedot = { 145, 098 }; /* Bide, Harden, Leech Seed */
private static readonly int[] MoveSwarmNuzleaf = { 145, 098 }; /* Harden, Growth, Nature Power, Leech Seed */
private static readonly int[] MoveSwarmSeedotF = { 202, 218, 076, 073 }; /* Giga Drain, Frustration, Solar Beam, Leech Seed */
private static readonly int[] MoveSwarmSkittyRS = { 045, 033 }; /* Growl, Tackle */
private static readonly int[] MoveSwarmSkittyE = { 045, 033, 039, 213 }; /* Growl, Tackle, Tail Whip, Attract */
#region AltSlots
private static readonly EncounterArea3[] SlotsRSEAlt =
{
@ -280,38 +287,38 @@ namespace PKHeX.Core
// Encounter Percent is a 50% call
new EncounterArea3 {
Location = 17, // Route 102
Slots = new[]
Slots = new EncounterSlot[]
{
new EncounterSlotMoves { Species = 283, LevelMin = 03, LevelMax = 03, Type = SlotType.Swarm, Moves = new[] {145, 098} /* Bubble, Quick Attack */ }, // Surskit (R/S)
new EncounterSlotMoves { Species = 273, LevelMin = 03, LevelMax = 03, Type = SlotType.Swarm, Moves = new[] {117, 106, 073} /* Bide, Harden, Leech Seed */ }, // Seedot (E)
new EncounterSlot3Swarm(MoveSwarmSurskit) { Species = 283, LevelMin = 03, LevelMax = 03, Type = SlotType.Swarm },
new EncounterSlot3Swarm(MoveSwarmSeedot) { Species = 273, LevelMin = 03, LevelMax = 03, Type = SlotType.Swarm },
},},
new EncounterArea3 {
Location = 29, // Route 114
Slots = new[]
Slots = new EncounterSlot[]
{
new EncounterSlotMoves { Species = 283, LevelMin = 15, LevelMax = 15, Type = SlotType.Swarm, Moves = new[] {145, 098} /* Bubble, Quick Attack */ }, // Surskit (R/S)
new EncounterSlotMoves { Species = 274, LevelMin = 15, LevelMax = 15, Type = SlotType.Swarm, Moves = new[] {106, 074, 267, 073} /* Harden, Growth, Nature Power, Leech Seed */ }, // Nuzleaf (E)
new EncounterSlot3Swarm(MoveSwarmSurskit) { Species = 283, LevelMin = 15, LevelMax = 15, Type = SlotType.Swarm },
new EncounterSlot3Swarm(MoveSwarmNuzleaf) { Species = 274, LevelMin = 15, LevelMax = 15, Type = SlotType.Swarm },
},},
new EncounterArea3 {
Location = 31, // Route 116
Slots = new[]
Slots = new EncounterSlot[]
{
new EncounterSlotMoves { Species = 300, LevelMin = 15, LevelMax = 15, Type = SlotType.Swarm, Moves = new[] {045, 033} /* Growl, Tackle */ }, // Skitty (R/S)
new EncounterSlotMoves { Species = 300, LevelMin = 08, LevelMax = 08, Type = SlotType.Swarm, Moves = new[] {045, 033, 039, 213} /* Growl, Tackle, Tail Whip, Attract */ }, // Skitty (E)
new EncounterSlot3Swarm(MoveSwarmSkittyRS) { Species = 300, LevelMin = 15, LevelMax = 15, Type = SlotType.Swarm },
new EncounterSlot3Swarm(MoveSwarmSkittyE) { Species = 300, LevelMin = 08, LevelMax = 08, Type = SlotType.Swarm },
},},
new EncounterArea3 {
Location = 32, // Route 117
Slots = new[]
Slots = new EncounterSlot[]
{
new EncounterSlotMoves { Species = 283, LevelMin = 15, LevelMax = 15, Type = SlotType.Swarm, Moves = new[] {145, 098} /* Bubble, Quick Attack */ }, // Surskit (R/S)
new EncounterSlotMoves { Species = 273, LevelMin = 13, LevelMax = 13, Type = SlotType.Swarm, Moves = new[] {106, 074, 267, 073} /* Harden, Growth, Nature Power, Leech Seed */ }, // Seedot (E)
new EncounterSlot3Swarm(MoveSwarmSurskit) { Species = 283, LevelMin = 15, LevelMax = 15, Type = SlotType.Swarm },
new EncounterSlot3Swarm(MoveSwarmNuzleaf) { Species = 273, LevelMin = 13, LevelMax = 13, Type = SlotType.Swarm }, // Has same moves as Nuzleaf
},},
new EncounterArea3 {
Location = 35, // Route 120
Slots = new[]
Slots = new EncounterSlot[]
{
new EncounterSlotMoves { Species = 283, LevelMin = 28, LevelMax = 28, Type = SlotType.Swarm, Moves = new[] {145, 098} /* Bubble, Quick Attack */ }, // Surskit (R/S)
new EncounterSlotMoves { Species = 273, LevelMin = 25, LevelMax = 25, Type = SlotType.Swarm, Moves = new[] {202, 218, 076, 073} /* Giga Drain, Frustration, Solar Beam, Leech Seed */ }, // Seedot (E)
new EncounterSlot3Swarm(MoveSwarmSurskit) { Species = 283, LevelMin = 28, LevelMax = 28, Type = SlotType.Swarm },
new EncounterSlot3Swarm(MoveSwarmSeedotF) { Species = 273, LevelMin = 25, LevelMax = 25, Type = SlotType.Swarm },
},},
// Feebas fishing spot
@ -356,7 +363,7 @@ namespace PKHeX.Core
new EncounterStatic { Gift = true, Species = 196, Level = 25, Location = 254, Gender = 0 }, // Espeon
new EncounterStatic { Gift = true, Species = 197, Level = 26, Location = 254, Gender = 0, Moves = new[] {044} }, // Umbreon (Bite)
new EncounterStaticShadow { Species = 296, Level = 30, Gauge = 03000, Moves = new[] {193,116,233,238}, Location = 005, Locks = ColoMakuhita }, // Makuhita: Miror B.Peon Trudly @ Phenac City
new EncounterStaticShadow(ColoMakuhita) { Species = 296, Level = 30, Gauge = 03000, Moves = new[] {193,116,233,238}, Location = 005 }, // Makuhita: Miror B.Peon Trudly @ Phenac City
new EncounterStaticShadow { Species = 153, Level = 30, Gauge = 03000, Moves = new[] {241,235,075,034}, Location = 003 }, // Bayleef: Cipher Peon Verde @ Phenac City
new EncounterStaticShadow { Species = 156, Level = 30, Gauge = 03000, Moves = new[] {241,108,091,172}, Location = 003 }, // Quilava: Cipher Peon Rosso @ Phenac City
@ -415,8 +422,6 @@ namespace PKHeX.Core
new EncounterStaticShadow { Species = 243, Level = 40, Gauge = 13000, Moves = new[] {240,043,098,087}, Location = 125 }, // Raikou: Cipher Admin Ein @ Deep Colosseum
new EncounterStaticShadow { Species = 243, Level = 40, Gauge = 13000, Moves = new[] {240,043,098,087}, Location = 069 }, // Raikou: Cipher Admin Ein @ Shadow PKMN Lab
new EncounterStaticShadow { Species = 207, Level = 43, Gauge = 06000, Moves = new[] {185,028,040,163}, Location = 058, Locks = Gligar }, // Gligar: Hunter Frena @ The Under Subway
new EncounterStaticShadow { Species = 207, Level = 43, Gauge = 06000, Moves = new[] {185,028,040,163}, Location = 133, Locks = Gligar }, // Gligar: Hunter Frena @ Snagem Hideout
new EncounterStaticShadow { Species = 234, Level = 43, Gauge = 06000, Moves = new[] {310,095,043,036}, Location = 058 }, // Stantler: Chaser Liaks @ The Under Subway
new EncounterStaticShadow { Species = 234, Level = 43, Gauge = 06000, Moves = new[] {310,095,043,036}, Location = 133 }, // Stantler: Chaser Liaks @ Snagem Hideout
new EncounterStaticShadow { Species = 221, Level = 43, Gauge = 06000, Moves = new[] {203,316,091,059}, Location = 058 }, // Piloswine: Bodybuilder Lonia @ The Under Subway
@ -424,7 +429,6 @@ namespace PKHeX.Core
new EncounterStaticShadow { Species = 215, Level = 43, Gauge = 06000, Moves = new[] {185,103,154,196}, Location = 058 }, // Sneasel: Rider Nelis @ The Under Subway
new EncounterStaticShadow { Species = 215, Level = 43, Gauge = 06000, Moves = new[] {185,103,154,196}, Location = 134 }, // Sneasel: Rider Nelis @ Snagem Hideout
new EncounterStaticShadow { Species = 190, Level = 43, Gauge = 06000, Moves = new[] {226,321,154,129}, Location = 067 }, // Aipom: Cipher Peon Cole @ Shadow PKMN Lab
new EncounterStaticShadow { Species = 198, Level = 43, Gauge = 06000, Moves = new[] {185,212,101,019}, Location = 067, Locks = Murkrow }, // Murkrow: Cipher Peon Lare @ Shadow PKMN Lab
new EncounterStaticShadow { Species = 205, Level = 43, Gauge = 06000, Moves = new[] {153,182,117,229}, Location = 067 }, // Forretress: Cipher Peon Vana @ Shadow PKMN Lab
new EncounterStaticShadow { Species = 168, Level = 43, Gauge = 06000, Moves = new[] {169,184,141,188}, Location = 069 }, // Ariados: Cipher Peon Lesar @ Shadow PKMN Lab
new EncounterStaticShadow { Species = 210, Level = 43, Gauge = 06000, Moves = new[] {044,184,046,070}, Location = 069 }, // Granbull: Cipher Peon Tanie @ Shadow PKMN Lab
@ -432,7 +436,6 @@ namespace PKHeX.Core
new EncounterStaticShadow { Species = 192, Level = 45, Gauge = 07000, Moves = new[] {241,074,275,076}, Location = 109 }, // Sunflora: Cipher Peon Baila @ Realgam Tower
new EncounterStaticShadow { Species = 225, Level = 45, Gauge = 07000, Moves = new[] {059,213,217,019}, Location = 109 }, // Delibird: Cipher Peon Arton @ Realgam Tower
new EncounterStaticShadow { Species = 214, Level = 45, Gauge = 07000, Moves = new[] {179,203,068,280}, Location = 111, Locks = Heracross }, // Heracross: Cipher Peon Dioge @ Realgam Tower
new EncounterStaticShadow { Species = 227, Level = 47, Gauge = 13000, Moves = new[] {065,319,314,211}, Location = 117 }, // Skarmory: Snagem Head Gonzap @ Realgam Tower
new EncounterStaticShadow { Species = 192, Level = 45, Gauge = 07000, Moves = new[] {241,074,275,076}, Location = 132 }, // Sunflora: Cipher Peon Baila @ Snagem Hideout
new EncounterStaticShadow { Species = 225, Level = 45, Gauge = 07000, Moves = new[] {059,213,217,019}, Location = 132 }, // Delibird: Cipher Peon Arton @ Snagem Hideout
@ -446,12 +449,17 @@ namespace PKHeX.Core
new EncounterStaticShadow { Species = 376, Level = 50, Gauge = 15000, Moves = new[] {063,334,232,094}, Location = 118 }, // Metagross: Cipher Nascour @ Tower Colosseum
new EncounterStaticShadow { Species = 248, Level = 55, Gauge = 20000, Moves = new[] {242,087,157,059}, Location = 118 }, // Tyranitar: Cipher Head Evice @ Tower Colosseum
new EncounterStaticShadow { Species = 235, Level = 45, Gauge = 07000, Moves = new[] {166,039,003,231}, Location = 132 }, // Smeargle: Team Snagem Biden @ Snagem Hideout
new EncounterStaticShadow { Species = 217, Level = 45, Gauge = 07000, Moves = new[] {185,313,122,163}, Location = 132, Locks = Ursaring }, // Ursaring: Team Snagem Agrev @ Snagem Hideout
new EncounterStaticShadow { Species = 213, Level = 45, Gauge = 07000, Moves = new[] {219,227,156,117}, Location = 125 }, // Shuckle: Deep King Agnol @ Deep Colosseum
new EncounterStaticShadow { Species = 176, Level = 20, Gauge = 05000, Moves = new[] {118,204,186,281}, Location = 001 }, // Togetic: Cipher Peon Fein @ Outskirt Stand
new EncounterStaticShadow { Species = 175, Level = 20, Gauge = 00000, Moves = new[] {118,204,186,281}, IVs = new[] {0,0,0,0,0,0}, EReader = true, Locks = CTogepi }, // Togepi: Chaser ボデス @ Card e Room (Japanese games only)
new EncounterStaticShadow { Species = 179, Level = 37, Gauge = 00000, Moves = new[] {087,084,086,178}, IVs = new[] {0,0,0,0,0,0}, EReader = true, Locks = CMareep }, // Mareep: Hunter ホル @ Card e Room (Japanese games only)
new EncounterStaticShadow { Species = 212, Level = 50, Gauge = 00000, Moves = new[] {210,232,014,163}, IVs = new[] {0,0,0,0,0,0}, EReader = true, Locks = CScizor }, // Scizor: Bodybuilder ワーバン @ Card e Room (Japanese games only)
new EncounterStaticShadow(Gligar) { Species = 207, Level = 43, Gauge = 06000, Moves = new[] {185,028,040,163}, Location = 058 }, // Gligar: Hunter Frena @ The Under Subway
new EncounterStaticShadow(Gligar) { Species = 207, Level = 43, Gauge = 06000, Moves = new[] {185,028,040,163}, Location = 133 }, // Gligar: Hunter Frena @ Snagem Hideout
new EncounterStaticShadow(Murkrow) { Species = 198, Level = 43, Gauge = 06000, Moves = new[] {185,212,101,019}, Location = 067 }, // Murkrow: Cipher Peon Lare @ Shadow PKMN Lab
new EncounterStaticShadow(Heracross) { Species = 214, Level = 45, Gauge = 07000, Moves = new[] {179,203,068,280}, Location = 111, }, // Heracross: Cipher Peon Dioge @ Realgam Tower
new EncounterStaticShadow(Ursaring) { Species = 217, Level = 45, Gauge = 07000, Moves = new[] {185,313,122,163}, Location = 132 }, // Ursaring: Team Snagem Agrev @ Snagem Hideout
new EncounterStaticShadow(CTogepi) { Species = 175, Level = 20, Gauge = 00000, Moves = new[] {118,204,186,281}, IVs = new[] {0,0,0,0,0,0}, EReader = true }, // Togepi: Chaser ボデス @ Card e Room (Japanese games only)
new EncounterStaticShadow(CMareep) { Species = 179, Level = 37, Gauge = 00000, Moves = new[] {087,084,086,178}, IVs = new[] {0,0,0,0,0,0}, EReader = true }, // Mareep: Hunter ホル @ Card e Room (Japanese games only)
new EncounterStaticShadow(CScizor) { Species = 212, Level = 50, Gauge = 00000, Moves = new[] {210,232,014,163}, IVs = new[] {0,0,0,0,0,0}, EReader = true }, // Scizor: Bodybuilder ワーバン @ Card e Room (Japanese games only)
};
#endregion
@ -474,113 +482,113 @@ namespace PKHeX.Core
new EncounterStatic { Fateful = true, Gift = true, Species = 158, Level = 05, Location = 016, Moves = new[] {242,010,043,308} }, // Totodile
new EncounterStaticShadow { Fateful = true, Species = 216, Level = 11, Gauge = 03000, Moves = new[] {216,287,122,232}, Location = 143 }, // Teddiursa: Cipher Peon Naps @ Pokémon HQ Lab
new EncounterStaticShadow { Fateful = true, Species = 165, Level = 10, Gauge = 02500, Moves = new[] {060,287,332,048}, Location = 153, Locks = Ledyba, }, // Ledyba: Casual Guy Cyle @ Gateon Port
new EncounterStaticShadow { Fateful = true, Species = 261, Level = 10, Gauge = 02500, Moves = new[] {091,215,305,336}, Location = 162, Locks = Poochyena, }, // Poochyena: Bodybuilder Kilen @ Gateon Port
new EncounterStaticShadow { Fateful = true, Species = 228, Level = 17, Gauge = 01500, Moves = new[] {185,204,052,046}, Location = 011, }, // Houndour: Cipher Peon Resix @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 343, Level = 17, Gauge = 01500, Moves = new[] {317,287,189,060}, Location = 011, }, // Baltoy: Cipher Peon Browsix @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 179, Level = 17, Gauge = 01500, Moves = new[] {034,215,084,086}, Location = 011, }, // Mareep: Cipher Peon Yellosix @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 273, Level = 17, Gauge = 01500, Moves = new[] {202,287,331,290}, Location = 011, Locks = Seedot, }, // Seedot: Cipher Peon Greesix @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 363, Level = 17, Gauge = 01500, Moves = new[] {062,204,055,189}, Location = 011, Locks = Spheal, }, // Spheal: Cipher Peon Blusix @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 316, Level = 17, Gauge = 01500, Moves = new[] {351,047,124,092}, Location = 011, Locks = Gulpin, }, // Gulpin: Cipher Peon Purpsix @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 167, Level = 14, Gauge = 01500, Moves = new[] {091,287,324,101}, Location = 010, Locks = Spinarak, }, // Spinarak: Cipher Peon Nexir @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 322, Level = 14, Gauge = 01500, Moves = new[] {036,204,091,052}, Location = 009, Locks = Numel, }, // Numel: Cipher Peon Solox @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 318, Level = 15, Gauge = 01700, Moves = new[] {352,287,184,044}, Location = 008, }, // Carvanha: Cipher Peon Cabol @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 285, Level = 15, Gauge = 01800, Moves = new[] {206,287,072,078}, Location = 008, Locks = Shroomish, }, // Shroomish: Cipher R&D Klots @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 301, Level = 18, Gauge = 02500, Moves = new[] {290,186,213,351}, Location = 008, Locks = Delcatty, }, // Delcatty: Cipher Admin Lovrina @ Cipher Lab
new EncounterStaticShadow { Fateful = true, Species = 100, Level = 19, Gauge = 02500, Moves = new[] {243,287,209,129}, Location = 092, Locks = Voltorb, }, // Voltorb: Wanderer Miror B. @ Cave Poké Spot
new EncounterStaticShadow { Fateful = true, Species = 296, Level = 18, Gauge = 02000, Moves = new[] {280,287,292,317}, Location = 109, Locks = Makuhita, }, // Makuhita: Cipher Peon Torkin @ ONBS Building
new EncounterStaticShadow { Fateful = true, Species = 037, Level = 18, Gauge = 02000, Moves = new[] {257,204,052,091}, Location = 109, Locks = Vulpix, }, // Vulpix: Cipher Peon Mesin @ ONBS Building
new EncounterStaticShadow { Fateful = true, Species = 355, Level = 19, Gauge = 02200, Moves = new[] {247,270,310,109}, Location = 110, Locks = Duskull, }, // Duskull: Cipher Peon Lobar @ ONBS Building
new EncounterStaticShadow { Fateful = true, Species = 280, Level = 20, Gauge = 02200, Moves = new[] {351,047,115,093}, Location = 119, Locks = Ralts, }, // Ralts: Cipher Peon Feldas @ ONBS Building
new EncounterStaticShadow { Fateful = true, Species = 303, Level = 22, Gauge = 02500, Moves = new[] {206,047,011,334}, Location = 111, Locks = Mawile, }, // Mawile: Cipher Cmdr Exol @ ONBS Building
new EncounterStaticShadow { Fateful = true, Species = 361, Level = 20, Gauge = 02500, Moves = new[] {352,047,044,196}, Location = 097, Locks = Snorunt }, // Snorunt: Cipher Peon Exinn @ Phenac City
new EncounterStaticShadow { Fateful = true, Species = 204, Level = 20, Gauge = 02500, Moves = new[] {042,287,191,068}, Location = 096, Locks = Pineco, }, // Pineco: Cipher Peon Gonrap @ Phenac City
new EncounterStaticShadow { Fateful = true, Species = 177, Level = 22, Gauge = 02500, Moves = new[] {248,226,101,332}, Location = 094, Locks = Natu, }, // Natu: Cipher Peon Eloin @ Phenac City
new EncounterStaticShadow { Fateful = true, Species = 315, Level = 22, Gauge = 03000, Moves = new[] {345,186,320,073}, Location = 113, Locks = Roselia }, // Roselia: Cipher Peon Fasin @ Phenac City
new EncounterStaticShadow { Fateful = true, Species = 315, Level = 22, Gauge = 03000, Moves = new[] {345,186,320,073}, Location = 094, Locks = Roselia }, // Roselia: Cipher Peon Fasin @ Phenac City
new EncounterStaticShadow { Fateful = true, Species = 052, Level = 22, Gauge = 03500, Moves = new[] {163,047,006,044}, Location = 113, Locks = Meowth }, // Meowth: Cipher Peon Fostin @ Phenac City
new EncounterStaticShadow { Fateful = true, Species = 052, Level = 22, Gauge = 03500, Moves = new[] {163,047,006,044}, Location = 094, Locks = Meowth }, // Meowth: Cipher Peon Fostin @ Phenac City
new EncounterStaticShadow { Fateful = true, Species = 220, Level = 22, Gauge = 02500, Moves = new[] {246,204,054,341}, Location = 100, Locks = Swinub }, // Swinub: Cipher Peon Greck @ Phenac City
new EncounterStaticShadow { Fateful = true, Species = 021, Level = 22, Gauge = 04500, Moves = new[] {206,226,043,332}, Location = 059, Locks = Spearow }, // Spearow: Cipher Peon Ezin @ Phenac Stadium
new EncounterStaticShadow { Fateful = true, Species = 021, Level = 22, Gauge = 04500, Moves = new[] {206,226,043,332}, Location = 107, Locks = Spearow }, // Spearow: Cipher Peon Ezin @ Phenac Stadium
new EncounterStaticShadow { Fateful = true, Species = 088, Level = 23, Gauge = 03000, Moves = new[] {188,270,325,107}, Location = 059, Locks = Grimer }, // Grimer: Cipher Peon Faltly @ Phenac Stadium
new EncounterStaticShadow { Fateful = true, Species = 088, Level = 23, Gauge = 03000, Moves = new[] {188,270,325,107}, Location = 107, Locks = Grimer }, // Grimer: Cipher Peon Faltly @ Phenac Stadium
new EncounterStaticShadow { Fateful = true, Species = 086, Level = 23, Gauge = 03500, Moves = new[] {057,270,219,058}, Location = 107, Locks = Seel }, // Seel: Cipher Peon Egrog @ Phenac Stadium
new EncounterStaticShadow { Fateful = true, Species = 337, Level = 25, Gauge = 05000, Moves = new[] {094,226,240,317}, Location = 107, Locks = Lunatone }, // Lunatone: Cipher Admin Snattle @ Phenac Stadium
new EncounterStaticShadow { Fateful = true, Species = 175, Level = 25, Gauge = 04500, Moves = new[] {266,161,246,270}, Location = 164, Gift = true }, // Togepi: Pokémon Trainer Hordel @ Outskirt Stand
new EncounterStaticShadow { Fateful = true, Species = 299, Level = 26, Gauge = 04000, Moves = new[] {085,270,086,157}, Location = 090, Locks = Nosepass }, // Nosepass: Wanderer Miror B. @ Pyrite Colosseum/Realgam Colosseum/Poké Spots
new EncounterStaticShadow { Fateful = true, Species = 299, Level = 26, Gauge = 04000, Moves = new[] {085,270,086,157}, Location = 113, Locks = Nosepass }, // Nosepass: Wanderer Miror B. @ Pyrite Colosseum/Realgam Colosseum/Poké Spots
new EncounterStaticShadow { Fateful = true, Species = 335, Level = 28, Gauge = 05000, Moves = new[] {280,287,068,306}, Location = 071 }, // Zangoose: Thug Zook @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 335, Level = 28, Gauge = 05000, Moves = new[] {280,287,068,306}, Location = 090 }, // Zangoose: Thug Zook @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 046, Level = 28, Gauge = 04000, Moves = new[] {147,287,163,206}, Location = 064, Locks = Paras }, // Paras: Cipher Peon Humah @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 058, Level = 28, Gauge = 04000, Moves = new[] {053,204,044,036}, Location = 064, Locks = Growlithe }, // Growlithe: Cipher Peon Humah @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 058, Level = 28, Gauge = 04000, Moves = new[] {053,204,044,036}, Location = 113, Locks = Growlithe }, // Growlithe: Cipher Peon Humah @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 015, Level = 30, Gauge = 04500, Moves = new[] {188,226,041,014}, Location = 059 }, // Beedrill: Cipher Peon Lok @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 012, Level = 30, Gauge = 04000, Moves = new[] {094,234,079,332}, Location = 059, Locks = Butterfree }, // Butterfree: Cipher Peon Targ @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 049, Level = 32, Gauge = 04000, Moves = new[] {318,287,164,094}, Location = 059, Locks = Venomoth }, // Venomoth: Cipher Peon Angic @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 097, Level = 34, Gauge = 05500, Moves = new[] {094,226,096,247}, Location = 059, Locks = Hypno }, // Hypno: Cipher Admin Gorigan @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 354, Level = 37, Gauge = 07000, Moves = new[] {185,270,247,174}, Location = 059, Locks = Banette }, // Banette: Cipher Peon Litnar @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 090, Level = 29, Gauge = 04000, Moves = new[] {036,287,057,062}, Location = 065 }, // Shellder: Cipher Peon Gorog @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 015, Level = 30, Gauge = 04500, Moves = new[] {188,226,041,014}, Location = 066 }, // Beedrill: Cipher Peon Lok @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 017, Level = 30, Gauge = 04000, Moves = new[] {017,287,211,297}, Location = 066, Locks = Pidgeotto }, // Pidgeotto: Cipher Peon Lok @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 114, Level = 30, Gauge = 04000, Moves = new[] {076,234,241,275}, Location = 067, Locks = Tangela }, // Tangela: Cipher Peon Targ @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 012, Level = 30, Gauge = 04000, Moves = new[] {094,234,079,332}, Location = 067, Locks = Butterfree }, // Butterfree: Cipher Peon Targ @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 082, Level = 30, Gauge = 04500, Moves = new[] {038,287,240,087}, Location = 067, Locks = Magneton }, // Magneton: Cipher Peon Snidle @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 049, Level = 32, Gauge = 04000, Moves = new[] {318,287,164,094}, Location = 070, Locks = Venomoth }, // Venomoth: Cipher Peon Angic @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 070, Level = 32, Gauge = 04000, Moves = new[] {345,234,188,230}, Location = 070, Locks = Weepinbell }, // Weepinbell: Cipher Peon Angic @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 024, Level = 33, Gauge = 05000, Moves = new[] {188,287,137,044}, Location = 070, Locks = Arbok }, // Arbok: Cipher Peon Smarton @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 057, Level = 34, Gauge = 06000, Moves = new[] {238,270,116,179}, Location = 069, Locks = Primeape }, // Primeape: Cipher Admin Gorigan @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 097, Level = 34, Gauge = 05500, Moves = new[] {094,226,096,247}, Location = 069, Locks = Hypno }, // Hypno: Cipher Admin Gorigan @ Cipher Key Lair
new EncounterStaticShadow { Fateful = true, Species = 055, Level = 33, Gauge = 06500, Moves = new[] {127,204,244,280}, Location = 088, Locks = Golduck }, // Golduck: Navigator Abson @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 302, Level = 33, Gauge = 07000, Moves = new[] {247,270,185,105}, Location = 088, Locks = Sableye }, // Sableye: Navigator Abson @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 085, Level = 34, Gauge = 08000, Moves = new[] {065,226,097,161}, Location = 076, Locks = Dodrio }, // Dodrio: Chaser Furgy @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 020, Level = 34, Gauge = 06000, Moves = new[] {162,287,184,158}, Location = 076, Locks = Raticate }, // Raticate: Chaser Furgy @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 083, Level = 36, Gauge = 05500, Moves = new[] {163,226,014,332}, Location = 076, Locks = Farfetchd }, // Farfetch'd: Cipher Admin Lovrina @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 334, Level = 36, Gauge = 06500, Moves = new[] {225,215,076,332}, Location = 076, Locks = Altaria }, // Altaria: Cipher Admin Lovrina @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 115, Level = 35, Gauge = 06000, Moves = new[] {089,047,039,146}, Location = 085, Locks = Kangaskhan }, // Kangaskhan: Cipher Peon Litnar @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 354, Level = 37, Gauge = 07000, Moves = new[] {185,270,247,174}, Location = 085, Locks = Banette }, // Banette: Cipher Peon Litnar @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 126, Level = 36, Gauge = 07000, Moves = new[] {126,266,238,009}, Location = 077, Locks = Magmar }, // Magmar: Cipher Peon Grupel @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 127, Level = 35, Gauge = 07000, Moves = new[] {012,270,206,066}, Location = 077, Locks = Pinsir }, // Pinsir: Cipher Peon Grupel @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 078, Level = 40, Gauge = 06000, Moves = new[] {076,226,241,053}, Location = 080, Locks = Rapidash }, // Rapidash: Cipher Peon Kolest @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 219, Level = 38, Gauge = 05500, Moves = new[] {257,287,089,053}, Location = 080, Locks = Magcargo }, // Magcargo: Cipher Peon Kolest @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 107, Level = 38, Gauge = 06000, Moves = new[] {005,270,170,327}, Location = 081, Locks = Hitmonchan }, // Hitmonchan: Cipher Peon Karbon @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 106, Level = 38, Gauge = 07000, Moves = new[] {136,287,170,025}, Location = 081, Locks = Hitmonlee }, // Hitmonlee: Cipher Peon Petro @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 108, Level = 38, Gauge = 05000, Moves = new[] {038,270,111,205}, Location = 084, Locks = Lickitung }, // Lickitung: Cipher Peon Geftal @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 123, Level = 40, Gauge = 08000, Moves = new[] {013,234,318,163}, Location = 084, Locks = Scyther }, // Scyther: Cipher Peon Leden @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 113, Level = 39, Gauge = 04000, Moves = new[] {085,186,135,285}, Location = 084, Locks = Chansey }, // Chansey: Cipher Peon Leden @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 113, Level = 39, Gauge = 04000, Moves = new[] {085,186,135,285}, Location = 087, Locks = Chansey }, // Chansey: Cipher Peon Leden @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 338, Level = 41, Gauge = 07500, Moves = new[] {094,226,241,322}, Location = 087, Locks = Solrock }, // Solrock: Cipher Admin Snattle @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 121, Level = 41, Gauge = 07500, Moves = new[] {127,287,058,105}, Location = 087, Locks = Starmie }, // Starmie: Cipher Admin Snattle @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 277, Level = 43, Gauge = 07000, Moves = new[] {143,226,097,263}, Location = 087 }, // Swellow: Cipher Admin Ardos @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 125, Level = 43, Gauge = 07000, Moves = new[] {238,266,086,085}, Location = 087, Locks = Electabuzz }, // Electabuzz: Cipher Admin Ardos @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 143, Level = 43, Gauge = 09000, Moves = new[] {090,287,174,034}, Location = 087, Locks = Snorlax }, // Snorlax: Cipher Admin Ardos @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 062, Level = 42, Gauge = 07500, Moves = new[] {056,270,240,280}, Location = 087, Locks = Poliwrath }, // Poliwrath: Cipher Admin Gorigan @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 122, Level = 42, Gauge = 06500, Moves = new[] {094,266,227,009}, Location = 087, Locks = MrMime }, // Mr. Mime: Cipher Admin Gorigan @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 051, Level = 40, Gauge = 05000, Moves = new[] {089,204,201,161}, Location = 075, Locks = Dugtrio }, // Dugtrio: Cipher Peon Kolax @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 310, Level = 44, Gauge = 07000, Moves = new[] {087,287,240,044}, Location = 073, Locks = Manectric }, // Manectric: Cipher Admin Eldes @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 373, Level = 50, Gauge = 09000, Moves = new[] {337,287,349,332}, Location = 073, Locks = Salamence }, // Salamence: Cipher Admin Eldes @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 105, Level = 44, Gauge = 06500, Moves = new[] {089,047,014,157}, Location = 073, Locks = Marowak }, // Marowak: Cipher Admin Eldes @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 131, Level = 44, Gauge = 06000, Moves = new[] {056,215,240,059}, Location = 073, Locks = Lapras }, // Lapras: Cipher Admin Eldes @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 249, Level = 50, Gauge = 12000, Moves = new[] {354,297,089,056}, Location = 074 }, // Lugia: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 112, Level = 46, Gauge = 07000, Moves = new[] {224,270,184,089}, Location = 074 }, // Rhydon: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 146, Level = 50, Gauge = 10000, Moves = new[] {326,234,261,053}, Location = 074, Locks = Moltres }, // Moltres: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 103, Level = 46, Gauge = 09000, Moves = new[] {094,287,095,246}, Location = 074, Locks = Exeggutor }, // Exeggutor: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 128, Level = 46, Gauge = 09000, Moves = new[] {089,287,039,034}, Location = 074, Locks = Tauros }, // Tauros: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 144, Level = 50, Gauge = 10000, Moves = new[] {326,215,114,058}, Location = 074, Locks = Articuno }, // Articuno: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 145, Level = 50, Gauge = 10000, Moves = new[] {326,226,319,085}, Location = 074, Locks = Zapdos }, // Zapdos: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow { Fateful = true, Species = 149, Level = 55, Gauge = 09000, Moves = new[] {063,215,349,089}, Location = 162, Locks = Dragonite }, // Dragonite: Wanderer Miror B. @ Gateon Port
new EncounterStaticShadow { Fateful = true, Species = 277, Level = 43, Gauge = 07000, Moves = new[] {143,226,097,263}, Location = 087 }, // Swellow: Cipher Admin Ardos @ Citadark Isle
new EncounterStaticShadow(Ledyba) { Fateful = true, Species = 165, Level = 10, Gauge = 02500, Moves = new[] {060,287,332,048}, Location = 153 }, // Ledyba: Casual Guy Cyle @ Gateon Port
new EncounterStaticShadow(Poochyena){ Fateful = true, Species = 261, Level = 10, Gauge = 02500, Moves = new[] {091,215,305,336}, Location = 162 }, // Poochyena: Bodybuilder Kilen @ Gateon Port
new EncounterStaticShadow(Seedot) { Fateful = true, Species = 273, Level = 17, Gauge = 01500, Moves = new[] {202,287,331,290}, Location = 011 }, // Seedot: Cipher Peon Greesix @ Cipher Lab
new EncounterStaticShadow(Spheal) { Fateful = true, Species = 363, Level = 17, Gauge = 01500, Moves = new[] {062,204,055,189}, Location = 011 }, // Spheal: Cipher Peon Blusix @ Cipher Lab
new EncounterStaticShadow(Gulpin) { Fateful = true, Species = 316, Level = 17, Gauge = 01500, Moves = new[] {351,047,124,092}, Location = 011 }, // Gulpin: Cipher Peon Purpsix @ Cipher Lab
new EncounterStaticShadow(Spinarak) { Fateful = true, Species = 167, Level = 14, Gauge = 01500, Moves = new[] {091,287,324,101}, Location = 010 }, // Spinarak: Cipher Peon Nexir @ Cipher Lab
new EncounterStaticShadow(Numel) { Fateful = true, Species = 322, Level = 14, Gauge = 01500, Moves = new[] {036,204,091,052}, Location = 009 }, // Numel: Cipher Peon Solox @ Cipher Lab
new EncounterStaticShadow(Shroomish){ Fateful = true, Species = 285, Level = 15, Gauge = 01800, Moves = new[] {206,287,072,078}, Location = 008 }, // Shroomish: Cipher R&D Klots @ Cipher Lab
new EncounterStaticShadow(Delcatty) { Fateful = true, Species = 301, Level = 18, Gauge = 02500, Moves = new[] {290,186,213,351}, Location = 008 }, // Delcatty: Cipher Admin Lovrina @ Cipher Lab
new EncounterStaticShadow(Voltorb) { Fateful = true, Species = 100, Level = 19, Gauge = 02500, Moves = new[] {243,287,209,129}, Location = 092 }, // Voltorb: Wanderer Miror B. @ Cave Poké Spot
new EncounterStaticShadow(Makuhita) { Fateful = true, Species = 296, Level = 18, Gauge = 02000, Moves = new[] {280,287,292,317}, Location = 109 }, // Makuhita: Cipher Peon Torkin @ ONBS Building
new EncounterStaticShadow(Vulpix) { Fateful = true, Species = 037, Level = 18, Gauge = 02000, Moves = new[] {257,204,052,091}, Location = 109 }, // Vulpix: Cipher Peon Mesin @ ONBS Building
new EncounterStaticShadow(Duskull) { Fateful = true, Species = 355, Level = 19, Gauge = 02200, Moves = new[] {247,270,310,109}, Location = 110 }, // Duskull: Cipher Peon Lobar @ ONBS Building
new EncounterStaticShadow(Ralts) { Fateful = true, Species = 280, Level = 20, Gauge = 02200, Moves = new[] {351,047,115,093}, Location = 119 }, // Ralts: Cipher Peon Feldas @ ONBS Building
new EncounterStaticShadow(Mawile) { Fateful = true, Species = 303, Level = 22, Gauge = 02500, Moves = new[] {206,047,011,334}, Location = 111 }, // Mawile: Cipher Cmdr Exol @ ONBS Building
new EncounterStaticShadow(Snorunt) { Fateful = true, Species = 361, Level = 20, Gauge = 02500, Moves = new[] {352,047,044,196}, Location = 097 }, // Snorunt: Cipher Peon Exinn @ Phenac City
new EncounterStaticShadow(Pineco) { Fateful = true, Species = 204, Level = 20, Gauge = 02500, Moves = new[] {042,287,191,068}, Location = 096 }, // Pineco: Cipher Peon Gonrap @ Phenac City
new EncounterStaticShadow(Natu) { Fateful = true, Species = 177, Level = 22, Gauge = 02500, Moves = new[] {248,226,101,332}, Location = 094 }, // Natu: Cipher Peon Eloin @ Phenac City
new EncounterStaticShadow(Roselia) { Fateful = true, Species = 315, Level = 22, Gauge = 03000, Moves = new[] {345,186,320,073}, Location = 113, }, // Roselia: Cipher Peon Fasin @ Phenac City
new EncounterStaticShadow(Roselia) { Fateful = true, Species = 315, Level = 22, Gauge = 03000, Moves = new[] {345,186,320,073}, Location = 094, }, // Roselia: Cipher Peon Fasin @ Phenac City
new EncounterStaticShadow(Meowth) { Fateful = true, Species = 052, Level = 22, Gauge = 03500, Moves = new[] {163,047,006,044}, Location = 113, }, // Meowth: Cipher Peon Fostin @ Phenac City
new EncounterStaticShadow(Meowth) { Fateful = true, Species = 052, Level = 22, Gauge = 03500, Moves = new[] {163,047,006,044}, Location = 094, }, // Meowth: Cipher Peon Fostin @ Phenac City
new EncounterStaticShadow(Swinub) { Fateful = true, Species = 220, Level = 22, Gauge = 02500, Moves = new[] {246,204,054,341}, Location = 100, }, // Swinub: Cipher Peon Greck @ Phenac City
new EncounterStaticShadow(Spearow) { Fateful = true, Species = 021, Level = 22, Gauge = 04500, Moves = new[] {206,226,043,332}, Location = 059, }, // Spearow: Cipher Peon Ezin @ Phenac Stadium
new EncounterStaticShadow(Spearow) { Fateful = true, Species = 021, Level = 22, Gauge = 04500, Moves = new[] {206,226,043,332}, Location = 107, }, // Spearow: Cipher Peon Ezin @ Phenac Stadium
new EncounterStaticShadow(Grimer) { Fateful = true, Species = 088, Level = 23, Gauge = 03000, Moves = new[] {188,270,325,107}, Location = 059, }, // Grimer: Cipher Peon Faltly @ Phenac Stadium
new EncounterStaticShadow(Grimer) { Fateful = true, Species = 088, Level = 23, Gauge = 03000, Moves = new[] {188,270,325,107}, Location = 107, }, // Grimer: Cipher Peon Faltly @ Phenac Stadium
new EncounterStaticShadow(Seel) { Fateful = true, Species = 086, Level = 23, Gauge = 03500, Moves = new[] {057,270,219,058}, Location = 107, }, // Seel: Cipher Peon Egrog @ Phenac Stadium
new EncounterStaticShadow(Lunatone) { Fateful = true, Species = 337, Level = 25, Gauge = 05000, Moves = new[] {094,226,240,317}, Location = 107, }, // Lunatone: Cipher Admin Snattle @ Phenac Stadium
new EncounterStaticShadow(Nosepass) { Fateful = true, Species = 299, Level = 26, Gauge = 04000, Moves = new[] {085,270,086,157}, Location = 090, }, // Nosepass: Wanderer Miror B. @ Pyrite Colosseum/Realgam Colosseum/Poké Spots
new EncounterStaticShadow(Nosepass) { Fateful = true, Species = 299, Level = 26, Gauge = 04000, Moves = new[] {085,270,086,157}, Location = 113, }, // Nosepass: Wanderer Miror B. @ Pyrite Colosseum/Realgam Colosseum/Poké Spots
new EncounterStaticShadow(Paras) { Fateful = true, Species = 046, Level = 28, Gauge = 04000, Moves = new[] {147,287,163,206}, Location = 064, }, // Paras: Cipher Peon Humah @ Cipher Key Lair
new EncounterStaticShadow(Growlithe) { Fateful = true, Species = 058, Level = 28, Gauge = 04000, Moves = new[] {053,204,044,036}, Location = 064 }, // Growlithe: Cipher Peon Humah @ Cipher Key Lair
new EncounterStaticShadow(Growlithe) { Fateful = true, Species = 058, Level = 28, Gauge = 04000, Moves = new[] {053,204,044,036}, Location = 113 }, // Growlithe: Cipher Peon Humah @ Cipher Key Lair
new EncounterStaticShadow(Butterfree){ Fateful = true, Species = 012, Level = 30, Gauge = 04000, Moves = new[] {094,234,079,332}, Location = 059, }, // Butterfree: Cipher Peon Targ @ Cipher Key Lair
new EncounterStaticShadow(Venomoth) { Fateful = true, Species = 049, Level = 32, Gauge = 04000, Moves = new[] {318,287,164,094}, Location = 059, }, // Venomoth: Cipher Peon Angic @ Cipher Key Lair
new EncounterStaticShadow(Hypno) { Fateful = true, Species = 097, Level = 34, Gauge = 05500, Moves = new[] {094,226,096,247}, Location = 059, }, // Hypno: Cipher Admin Gorigan @ Cipher Key Lair
new EncounterStaticShadow(Banette) { Fateful = true, Species = 354, Level = 37, Gauge = 07000, Moves = new[] {185,270,247,174}, Location = 059, }, // Banette: Cipher Peon Litnar @ Citadark Isle
new EncounterStaticShadow(Pidgeotto) { Fateful = true, Species = 017, Level = 30, Gauge = 04000, Moves = new[] {017,287,211,297}, Location = 066, }, // Pidgeotto: Cipher Peon Lok @ Cipher Key Lair
new EncounterStaticShadow(Tangela) { Fateful = true, Species = 114, Level = 30, Gauge = 04000, Moves = new[] {076,234,241,275}, Location = 067, }, // Tangela: Cipher Peon Targ @ Cipher Key Lair
new EncounterStaticShadow(Butterfree){ Fateful = true, Species = 012, Level = 30, Gauge = 04000, Moves = new[] {094,234,079,332}, Location = 067, }, // Butterfree: Cipher Peon Targ @ Cipher Key Lair
new EncounterStaticShadow(Magneton) { Fateful = true, Species = 082, Level = 30, Gauge = 04500, Moves = new[] {038,287,240,087}, Location = 067, }, // Magneton: Cipher Peon Snidle @ Cipher Key Lair
new EncounterStaticShadow(Venomoth) { Fateful = true, Species = 049, Level = 32, Gauge = 04000, Moves = new[] {318,287,164,094}, Location = 070, }, // Venomoth: Cipher Peon Angic @ Cipher Key Lair
new EncounterStaticShadow(Weepinbell){ Fateful = true, Species = 070, Level = 32, Gauge = 04000, Moves = new[] {345,234,188,230}, Location = 070, }, // Weepinbell: Cipher Peon Angic @ Cipher Key Lair
new EncounterStaticShadow(Arbok) { Fateful = true, Species = 024, Level = 33, Gauge = 05000, Moves = new[] {188,287,137,044}, Location = 070, }, // Arbok: Cipher Peon Smarton @ Cipher Key Lair
new EncounterStaticShadow(Primeape) { Fateful = true, Species = 057, Level = 34, Gauge = 06000, Moves = new[] {238,270,116,179}, Location = 069, }, // Primeape: Cipher Admin Gorigan @ Cipher Key Lair
new EncounterStaticShadow(Hypno) { Fateful = true, Species = 097, Level = 34, Gauge = 05500, Moves = new[] {094,226,096,247}, Location = 069, }, // Hypno: Cipher Admin Gorigan @ Cipher Key Lair
new EncounterStaticShadow(Golduck) { Fateful = true, Species = 055, Level = 33, Gauge = 06500, Moves = new[] {127,204,244,280}, Location = 088, }, // Golduck: Navigator Abson @ Citadark Isle
new EncounterStaticShadow(Sableye) { Fateful = true, Species = 302, Level = 33, Gauge = 07000, Moves = new[] {247,270,185,105}, Location = 088, }, // Sableye: Navigator Abson @ Citadark Isle
new EncounterStaticShadow(Dodrio) { Fateful = true, Species = 085, Level = 34, Gauge = 08000, Moves = new[] {065,226,097,161}, Location = 076, }, // Dodrio: Chaser Furgy @ Citadark Isle
new EncounterStaticShadow(Raticate) { Fateful = true, Species = 020, Level = 34, Gauge = 06000, Moves = new[] {162,287,184,158}, Location = 076, }, // Raticate: Chaser Furgy @ Citadark Isle
new EncounterStaticShadow(Farfetchd) { Fateful = true, Species = 083, Level = 36, Gauge = 05500, Moves = new[] {163,226,014,332}, Location = 076, }, // Farfetch'd: Cipher Admin Lovrina @ Citadark Isle
new EncounterStaticShadow(Altaria) { Fateful = true, Species = 334, Level = 36, Gauge = 06500, Moves = new[] {225,215,076,332}, Location = 076, }, // Altaria: Cipher Admin Lovrina @ Citadark Isle
new EncounterStaticShadow(Kangaskhan){ Fateful = true, Species = 115, Level = 35, Gauge = 06000, Moves = new[] {089,047,039,146}, Location = 085, }, // Kangaskhan: Cipher Peon Litnar @ Citadark Isle
new EncounterStaticShadow(Banette) { Fateful = true, Species = 354, Level = 37, Gauge = 07000, Moves = new[] {185,270,247,174}, Location = 085, }, // Banette: Cipher Peon Litnar @ Citadark Isle
new EncounterStaticShadow(Magmar) { Fateful = true, Species = 126, Level = 36, Gauge = 07000, Moves = new[] {126,266,238,009}, Location = 077, }, // Magmar: Cipher Peon Grupel @ Citadark Isle
new EncounterStaticShadow(Pinsir) { Fateful = true, Species = 127, Level = 35, Gauge = 07000, Moves = new[] {012,270,206,066}, Location = 077, }, // Pinsir: Cipher Peon Grupel @ Citadark Isle
new EncounterStaticShadow(Rapidash) { Fateful = true, Species = 078, Level = 40, Gauge = 06000, Moves = new[] {076,226,241,053}, Location = 080, }, // Rapidash: Cipher Peon Kolest @ Citadark Isle
new EncounterStaticShadow(Magcargo) { Fateful = true, Species = 219, Level = 38, Gauge = 05500, Moves = new[] {257,287,089,053}, Location = 080, }, // Magcargo: Cipher Peon Kolest @ Citadark Isle
new EncounterStaticShadow(Hitmonchan){ Fateful = true, Species = 107, Level = 38, Gauge = 06000, Moves = new[] {005,270,170,327}, Location = 081, }, // Hitmonchan: Cipher Peon Karbon @ Citadark Isle
new EncounterStaticShadow(Hitmonlee) { Fateful = true, Species = 106, Level = 38, Gauge = 07000, Moves = new[] {136,287,170,025}, Location = 081, }, // Hitmonlee: Cipher Peon Petro @ Citadark Isle
new EncounterStaticShadow(Lickitung) { Fateful = true, Species = 108, Level = 38, Gauge = 05000, Moves = new[] {038,270,111,205}, Location = 084, }, // Lickitung: Cipher Peon Geftal @ Citadark Isle
new EncounterStaticShadow(Scyther) { Fateful = true, Species = 123, Level = 40, Gauge = 08000, Moves = new[] {013,234,318,163}, Location = 084, }, // Scyther: Cipher Peon Leden @ Citadark Isle
new EncounterStaticShadow(Chansey) { Fateful = true, Species = 113, Level = 39, Gauge = 04000, Moves = new[] {085,186,135,285}, Location = 084, }, // Chansey: Cipher Peon Leden @ Citadark Isle
new EncounterStaticShadow(Chansey) { Fateful = true, Species = 113, Level = 39, Gauge = 04000, Moves = new[] {085,186,135,285}, Location = 087, }, // Chansey: Cipher Peon Leden @ Citadark Isle
new EncounterStaticShadow(Solrock) { Fateful = true, Species = 338, Level = 41, Gauge = 07500, Moves = new[] {094,226,241,322}, Location = 087, }, // Solrock: Cipher Admin Snattle @ Citadark Isle
new EncounterStaticShadow(Starmie) { Fateful = true, Species = 121, Level = 41, Gauge = 07500, Moves = new[] {127,287,058,105}, Location = 087, }, // Starmie: Cipher Admin Snattle @ Citadark Isle
new EncounterStaticShadow(Electabuzz){ Fateful = true, Species = 125, Level = 43, Gauge = 07000, Moves = new[] {238,266,086,085}, Location = 087, }, // Electabuzz: Cipher Admin Ardos @ Citadark Isle
new EncounterStaticShadow(Snorlax) { Fateful = true, Species = 143, Level = 43, Gauge = 09000, Moves = new[] {090,287,174,034}, Location = 087, }, // Snorlax: Cipher Admin Ardos @ Citadark Isle
new EncounterStaticShadow(Poliwrath) { Fateful = true, Species = 062, Level = 42, Gauge = 07500, Moves = new[] {056,270,240,280}, Location = 087, }, // Poliwrath: Cipher Admin Gorigan @ Citadark Isle
new EncounterStaticShadow(MrMime) { Fateful = true, Species = 122, Level = 42, Gauge = 06500, Moves = new[] {094,266,227,009}, Location = 087, }, // Mr. Mime: Cipher Admin Gorigan @ Citadark Isle
new EncounterStaticShadow(Dugtrio) { Fateful = true, Species = 051, Level = 40, Gauge = 05000, Moves = new[] {089,204,201,161}, Location = 075, }, // Dugtrio: Cipher Peon Kolax @ Citadark Isle
new EncounterStaticShadow(Manectric) { Fateful = true, Species = 310, Level = 44, Gauge = 07000, Moves = new[] {087,287,240,044}, Location = 073, }, // Manectric: Cipher Admin Eldes @ Citadark Isle
new EncounterStaticShadow(Salamence) { Fateful = true, Species = 373, Level = 50, Gauge = 09000, Moves = new[] {337,287,349,332}, Location = 073, }, // Salamence: Cipher Admin Eldes @ Citadark Isle
new EncounterStaticShadow(Marowak) { Fateful = true, Species = 105, Level = 44, Gauge = 06500, Moves = new[] {089,047,014,157}, Location = 073, }, // Marowak: Cipher Admin Eldes @ Citadark Isle
new EncounterStaticShadow(Lapras) { Fateful = true, Species = 131, Level = 44, Gauge = 06000, Moves = new[] {056,215,240,059}, Location = 073, }, // Lapras: Cipher Admin Eldes @ Citadark Isle
new EncounterStaticShadow(Moltres) { Fateful = true, Species = 146, Level = 50, Gauge = 10000, Moves = new[] {326,234,261,053}, Location = 074, }, // Moltres: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow(Exeggutor) { Fateful = true, Species = 103, Level = 46, Gauge = 09000, Moves = new[] {094,287,095,246}, Location = 074, }, // Exeggutor: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow(Tauros) { Fateful = true, Species = 128, Level = 46, Gauge = 09000, Moves = new[] {089,287,039,034}, Location = 074, }, // Tauros: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow(Articuno) { Fateful = true, Species = 144, Level = 50, Gauge = 10000, Moves = new[] {326,215,114,058}, Location = 074, }, // Articuno: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow(Zapdos) { Fateful = true, Species = 145, Level = 50, Gauge = 10000, Moves = new[] {326,226,319,085}, Location = 074, }, // Zapdos: Grand Master Greevil @ Citadark Isle
new EncounterStaticShadow(Dragonite) { Fateful = true, Species = 149, Level = 55, Gauge = 09000, Moves = new[] {063,215,349,089}, Location = 162, }, // Dragonite: Wanderer Miror B. @ Gateon Port
}.SelectMany(CloneMirorB).ToArray();
internal static readonly EncounterArea3[] SlotsXD =

File diff suppressed because it is too large Load diff

View file

@ -996,7 +996,7 @@ namespace PKHeX.Core
Encounter_HGSS_Regular);
#endregion
#region Trade Tables
private static readonly string[] RanchOTNames = { null, "ユカリ", "Hayley", "EULALIE", "GIULIA", "EUKALIA", null, "Eulalia" };
private static readonly string[] RanchOTNames = { string.Empty, "ユカリ", "Hayley", "EULALIE", "GIULIA", "EUKALIA", string.Empty, "Eulalia" };
private static readonly EncounterTrade[] RanchGifts =
{

View file

@ -100,7 +100,7 @@ namespace PKHeX.Core
var list = new List<EncounterStatic>();
foreach (EncounterStatic s in t)
{
if (s.Moves == null || s.Moves.Length <= 1) // no special moves
if (s.Moves.Length <= 1) // no special moves
{
list.Add(s);
continue;
@ -745,8 +745,8 @@ namespace PKHeX.Core
private const string tradeB2W2 = "tradeb2w2";
private static readonly string[][] TradeBW = Util.GetLanguageStrings8(tradeBW);
private static readonly string[][] TradeB2W2 = Util.GetLanguageStrings8(tradeB2W2);
private static readonly string[] TradeOT_B2W2_F = {null, "ルリ", "Yancy", "Brenda", "Lilì", "Sabine", null, "Belinda", "루리"};
private static readonly string[] TradeOT_B2W2_M = {null, "テツ", "Curtis", "Julien", "Dadi", "Markus", null, "Julián", "철권"};
private static readonly string[] TradeOT_B2W2_F = {string.Empty, "ルリ", "Yancy", "Brenda", "Lilì", "Sabine", string.Empty, "Belinda", "루리"};
private static readonly string[] TradeOT_B2W2_M = {string.Empty, "テツ", "Curtis", "Julien", "Dadi", "Markus", string.Empty, "Julián", "철권"};
internal static readonly EncounterTrade[] TradeGift_B2W2 = TradeGift_B2W2_Regular.Concat(TradeGift_B2W2_YancyCurtis).ToArray();

View file

@ -26,9 +26,11 @@ namespace PKHeX.Core
MarkG7REGSlots(ref REG_MN);
MarkG7SMSlots(ref SOS_SN);
MarkG7SMSlots(ref SOS_MN);
InitializePelagoAreas();
SlotsSN = AddExtraTableSlots(REG_SN, SOS_SN, Encounter_Pelago_SN);
SlotsMN = AddExtraTableSlots(REG_MN, SOS_MN, Encounter_Pelago_MN);
int[] pelagoMin = { 1, 11, 21, 37, 49 };
InitializePelagoSM(pelagoMin, out var p_sn, out var p_mn);
InitializePelagoUltra(pelagoMin, out var p_us, out var p_um);
SlotsSN = AddExtraTableSlots(REG_SN, SOS_SN, p_sn);
SlotsMN = AddExtraTableSlots(REG_MN, SOS_MN, p_mn);
var REG_US = GetEncounterTables<EncounterArea7>("uu", "us");
var REG_UM = GetEncounterTables<EncounterArea7>("uu", "um");
@ -38,12 +40,12 @@ namespace PKHeX.Core
MarkG7REGSlots(ref REG_UM);
MarkG7SMSlots(ref SOS_US);
MarkG7SMSlots(ref SOS_UM);
SlotsUS = AddExtraTableSlots(REG_US, SOS_US, Encounter_Pelago_US);
SlotsUM = AddExtraTableSlots(REG_UM, SOS_UM, Encounter_Pelago_UM);
SlotsUS = AddExtraTableSlots(REG_US, SOS_US, p_us);
SlotsUM = AddExtraTableSlots(REG_UM, SOS_UM, p_um);
MarkEncounterAreaArray(SOS_SN, SOS_MN, SOS_US, SOS_UM,
Encounter_Pelago_SN, Encounter_Pelago_MN,
Encounter_Pelago_US, Encounter_Pelago_UM);
p_sn, p_mn,
p_us, p_um);
MarkEncountersGeneration(7, SlotsSN, SlotsMN, SlotsUS, SlotsUM);
MarkEncountersGeneration(7, StaticSN, StaticMN, StaticUS, StaticUM, TradeGift_SM, TradeGift_USUM);
@ -407,11 +409,8 @@ namespace PKHeX.Core
private static readonly string[][] TradeSM = Util.GetLanguageStrings10(tradeSM);
private static readonly string[][] TradeUSUM = Util.GetLanguageStrings10(tradeUSUM);
private static EncounterArea7[] Encounter_Pelago_SN, Encounter_Pelago_MN, Encounter_Pelago_US, Encounter_Pelago_UM;
private static void InitializePelagoAreas()
private static void InitializePelagoSM(int[] minLevels, out EncounterArea7[] sn, out EncounterArea7[] mn)
{
int[] minLevels = { 1, 11, 21, 37, 49 };
int[][] speciesSM =
{
new[] {627/*SN*/, 021, 041, 090, 278, 731}, // 1-7
@ -420,10 +419,13 @@ namespace PKHeX.Core
new[] {227, 375, 707}, // 37-43
new[] {123, 131, 429, 587}, // 49-55
};
Encounter_Pelago_SN = GetPelagoArea(speciesSM, minLevels);
sn = GetPelagoArea(speciesSM, minLevels);
speciesSM[0][0] = 629; // Rufflet -> Vullaby
Encounter_Pelago_MN = GetPelagoArea(speciesSM, minLevels);
mn = GetPelagoArea(speciesSM, minLevels);
}
private static void InitializePelagoUltra(int[] minLevels, out EncounterArea7[] us, out EncounterArea7[] um)
{
int[][] speciesUU =
{
new[] {731, 278, 041, 742, 086}, // 1-7
@ -432,9 +434,9 @@ namespace PKHeX.Core
new[] {131, 354, 200, /* US */ 228}, // 37-43
new[] {209, 667, 357, 430}, // 49-55
};
Encounter_Pelago_US = GetPelagoArea(speciesUU, minLevels);
us = GetPelagoArea(speciesUU, minLevels);
speciesUU[3][3] = 309; // Houndour -> Electrike
Encounter_Pelago_UM = GetPelagoArea(speciesUU, minLevels);
um = GetPelagoArea(speciesUU, minLevels);
}
private static EncounterArea7[] GetPelagoArea(int[][] species, int[] min)

View file

@ -60,14 +60,14 @@ namespace PKHeX.Core
new EncounterStatic { Species = 059, Level = 16, Location = 33, Gift = true, IVs = new[] {25,30,25,31,30,25}, Version = GameVersion.GE }, // Arcanine @ Vermillion City (Outside Fan Club)
};
private static readonly string[] T1 = { null, "ミニコ", "Tatianna", "BarbaRatatta", "Addoloratta", "Barbaratt", null, "Tatiana", "미니꼬", "小幂妮", "小幂妮", };
private static readonly string[] T2 = { null, "ボーアイス", "Nicholice", "Iceman-4L0L4", "Goffreddo", "Eisper", null, "Gelasio", "보아이스", "露冰冰", "露冰冰", };
private static readonly string[] T3 = { null, "レディダグ", "Diggette", "Taupilady", "Lady Glett", "Digga", null, "Glenda", "레이디그다", "蒂淑", "蒂淑", };
private static readonly string[] T4 = { null, "ワルモン", "Darko", "AlolaZeDark", "Mattetro", "Bösbert", null, "Sinesio", "나뻐기", "达怀丹", "达怀丹", };
private static readonly string[] T5 = { null, "エリッチ", "Psytrice", "TopDeTonCœur", "Chulia", "Assana", null, "Menchu", "엘리츄", "晶莹丘", "晶莹丘", };
private static readonly string[] T6 = { null, "ジェンガラ", "Genmar", "OSS-Dandy7", "Mr. Owak", "Knoggelius", null, "Mario", "젠구리", "申史加拉", "申史加拉", };
private static readonly string[] T7 = { null, "マニシ", "Exemann", "Koko-fan", "Exechiele", "Einrich", null, "Gunter", "마니시", "艾浩舒", "艾浩舒", };
private static readonly string[] T8 = { null, "コツブ", "Higeo", "Montagnou", "George", "Karstein", null, "Georgie", "산돌", "科布", "科布", };
private static readonly string[] T1 = { string.Empty, "ミニコ", "Tatianna", "BarbaRatatta", "Addoloratta", "Barbaratt", string.Empty, "Tatiana", "미니꼬", "小幂妮", "小幂妮", };
private static readonly string[] T2 = { string.Empty, "ボーアイス", "Nicholice", "Iceman-4L0L4", "Goffreddo", "Eisper", string.Empty, "Gelasio", "보아이스", "露冰冰", "露冰冰", };
private static readonly string[] T3 = { string.Empty, "レディダグ", "Diggette", "Taupilady", "Lady Glett", "Digga", string.Empty, "Glenda", "레이디그다", "蒂淑", "蒂淑", };
private static readonly string[] T4 = { string.Empty, "ワルモン", "Darko", "AlolaZeDark", "Mattetro", "Bösbert", string.Empty, "Sinesio", "나뻐기", "达怀丹", "达怀丹", };
private static readonly string[] T5 = { string.Empty, "エリッチ", "Psytrice", "TopDeTonCœur", "Chulia", "Assana", string.Empty, "Menchu", "엘리츄", "晶莹丘", "晶莹丘", };
private static readonly string[] T6 = { string.Empty, "ジェンガラ", "Genmar", "OSS-Dandy7", "Mr. Owak", "Knoggelius", string.Empty, "Mario", "젠구리", "申史加拉", "申史加拉", };
private static readonly string[] T7 = { string.Empty, "マニシ", "Exemann", "Koko-fan", "Exechiele", "Einrich", string.Empty, "Gunter", "마니시", "艾浩舒", "艾浩舒", };
private static readonly string[] T8 = { string.Empty, "コツブ", "Higeo", "Montagnou", "George", "Karstein", string.Empty, "Georgie", "산돌", "科布", "科布", };
internal static readonly EncounterTrade[] TradeGift_GG =
{
@ -145,33 +145,39 @@ namespace PKHeX.Core
private class RareSpawn
{
public int Species;
public int[] Locations;
public readonly int Species;
public readonly byte[] Locations;
protected internal RareSpawn(int species, params byte[] locations)
{
Species = species;
Locations = locations;
}
}
private static readonly int[] Sky = {003, 004, 005, 006, 009, 010, 011, 012, 013, 014, 015, 016, 017, 018, 019, 020, 021, 022, 023, 024, 025, 026, 027};
private static readonly byte[] Sky = {003, 004, 005, 006, 009, 010, 011, 012, 013, 014, 015, 016, 017, 018, 019, 020, 021, 022, 023, 024, 025, 026, 027};
private static readonly RareSpawn[] Rare =
{
// Normal
new RareSpawn {Species = 001, Locations = new[] {039}},
new RareSpawn {Species = 004, Locations = new[] {005, 006, 041}},
new RareSpawn {Species = 007, Locations = new[] {026, 027, 044}},
new RareSpawn {Species = 106, Locations = new[] {045}},
new RareSpawn {Species = 107, Locations = new[] {045}},
new RareSpawn {Species = 113, Locations = new[] {007, 008, 010, 011, 012, 013, 014, 015, 016, 017, 018, 019, 020, 023, 025, 040, 042, 043, 045, 047, 051}},
new RareSpawn {Species = 137, Locations = new[] {009}},
new RareSpawn {Species = 143, Locations = new[] {046}},
new RareSpawn(001, 039),
new RareSpawn(004, 005, 006, 041),
new RareSpawn(007, 026, 027, 044),
new RareSpawn(106, 045),
new RareSpawn(107, 045),
new RareSpawn(113, 007, 008, 010, 011, 012, 013, 014, 015, 016, 017, 018, 019, 020, 023, 025, 040, 042, 043, 045, 047, 051),
new RareSpawn(137, 009),
new RareSpawn(143, 046),
// Water
new RareSpawn {Species = 131, Locations = new[] {021, 022}},
new RareSpawn(131, 021, 022),
// Fly
new RareSpawn {Species = 006, Locations = Sky,},
new RareSpawn {Species = 144, Locations = Sky,},
new RareSpawn {Species = 145, Locations = Sky,},
new RareSpawn {Species = 146, Locations = Sky,},
new RareSpawn {Species = 149, Locations = Sky,},
new RareSpawn(006, Sky),
new RareSpawn(144, Sky),
new RareSpawn(145, Sky),
new RareSpawn(146, Sky),
new RareSpawn(149, Sky),
};
private static void ManuallyAddRareSpawns(IEnumerable<EncounterArea> areas)
@ -179,7 +185,7 @@ namespace PKHeX.Core
foreach (var table in areas)
{
var loc = table.Location;
var species = Rare.Where(z => z.Locations.Contains(loc)).Select(z => z.Species).ToArray();
var species = Rare.Where(z => z.Locations.Contains((byte)loc)).Select(z => z.Species).ToArray();
if (species.Length == 0)
continue;
var slots = table.Slots;

View file

@ -25,9 +25,9 @@ namespace PKHeX.Core
private static IEnumerable<WC3> GetIngameCXDData()
{
var langs = new[]{LanguageID.Japanese, LanguageID.English, LanguageID.French, LanguageID.Italian, LanguageID.German, LanguageID.Spanish};
var h = new[] {null, "ダニー", "HORDEL", "VOLKER", "ODINO", "HORAZ", null, "HORDEL"};
var d = new[] {null, "ギンザル", "DUKING", "DOKING", "RODRIGO", "GRAND", null, "GERMÁN"};
var m = new[] {null, "バトルやま", "MATTLE", "MT BATAILL", "MONTE LOTT", "DUELLBERG", null, "ERNESTO"}; // truncated on ck3->pk3 transfer
var h = new[] {string.Empty, "ダニー", "HORDEL", "VOLKER", "ODINO", "HORAZ", string.Empty, "HORDEL"};
var d = new[] {string.Empty, "ギンザル", "DUKING", "DOKING", "RODRIGO", "GRAND", string.Empty, "GERMÁN"};
var m = new[] {string.Empty, "バトルやま", "MATTLE", "MT BATAILL", "MONTE LOTT", "DUELLBERG", string.Empty, "ERNESTO"}; // truncated on ck3->pk3 transfer
return langs.SelectMany(l => GetIngame((int)l));
IEnumerable<WC3> GetIngame(int l)

View file

@ -77,7 +77,7 @@ namespace PKHeX.Core
case (int)Core.Species.Scatterbug:
case (int)Core.Species.Spewpa:
case (int)Core.Species.Vivillon:
pk.AltForm = Legal.GetVivillonPattern(SAV.Country, SAV.SubRegion);
pk.AltForm = Legal.GetVivillonPattern((byte)SAV.Country, (byte)SAV.SubRegion);
break;
}
}

View file

@ -7,6 +7,8 @@ namespace PKHeX.Core
/// </summary>
public sealed class EncounterInvalid : IEncounterable
{
public static readonly EncounterInvalid Default = new EncounterInvalid();
public int Species { get; }
public int LevelMin { get; }
public int LevelMax { get; }
@ -15,6 +17,8 @@ namespace PKHeX.Core
public string Name => "Invalid";
public string LongName => "Invalid";
private EncounterInvalid() { }
public EncounterInvalid(PKM pkm)
{
Species = pkm.Species;

View file

@ -50,12 +50,12 @@ namespace PKHeX.Core
public EncounterType TypeEncounter { get; set; } = EncounterType.None;
public int SlotNumber { get; set; }
public int Generation { get; set; } = -1;
internal EncounterSlotPermissions _perm;
private EncounterSlotPermissions? _perm;
public EncounterSlotPermissions Permissions => _perm ??= new EncounterSlotPermissions();
public GameVersion Version { get; set; }
internal EncounterArea Area { private get; set; }
public int Location { get => Area.Location; set { } }
internal EncounterArea? Area { private get; set; }
public int Location { get => Area?.Location ?? 0; set { } }
public bool EggEncounter => false;
public int EggLocation { get => 0; set { } }
@ -122,7 +122,7 @@ namespace PKHeX.Core
private void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var moves = this is EncounterSlotMoves m ? m.Moves : MoveLevelUp.GetEncounterMoves(pk, level, version);
var moves = this is IMoveset m ? m.Moves : MoveLevelUp.GetEncounterMoves(pk, level, version);
pk.Moves = moves;
pk.SetMaximumPPCurrent(moves);
}
@ -205,7 +205,7 @@ namespace PKHeX.Core
int spec = pk.Species;
if (spec == (int)Core.Species.Scatterbug || spec == (int)Core.Species.Spewpa || spec == (int)Core.Species.Vivillon)
return Legal.GetVivillonPattern(SAV.Country, SAV.SubRegion);
return Legal.GetVivillonPattern((byte)SAV.Country, (byte)SAV.SubRegion);
return 0;
}

View file

@ -0,0 +1,9 @@
namespace PKHeX.Core
{
internal sealed class EncounterSlot3Swarm : EncounterSlot, IMoveset
{
public int[] Moves { get; }
public EncounterSlot3Swarm(int[] moves) => Moves = moves;
}
}

View file

@ -1,7 +0,0 @@
namespace PKHeX.Core
{
internal sealed class EncounterSlotMoves : EncounterSlot, IMoveset
{
public int[] Moves { get; set; }
}
}

View file

@ -12,7 +12,7 @@ namespace PKHeX.Core
public class EncounterStatic : IEncounterable, IMoveset, IGeneration, ILocation, IContestStats, IVersion
{
public int Species { get; set; }
public int[] Moves { get; set; }
public int[] Moves { get; set; } = Array.Empty<int>();
public int Level { get; set; }
public int LevelMin => Level;
@ -29,7 +29,7 @@ namespace PKHeX.Core
public bool Gift { get; set; }
public int Ball { get; set; } = 4; // Only checked when is Gift
public GameVersion Version { get; set; } = GameVersion.Any;
public int[] IVs { get; set; }
public int[] IVs { get; set; } = Array.Empty<int>();
public int FlawlessIVCount { get; set; }
public int[] Contest { set => this.SetContestStats(value); }
@ -52,9 +52,9 @@ namespace PKHeX.Core
private void CloneArrays()
{
// dereference original arrays with new copies
Moves = (int[])Moves?.Clone();
Relearn = (int[])Relearn.Clone();
IVs = (int[])IVs?.Clone();
Moves = Moves.Length == 0 ? Moves : (int[])Moves.Clone();
Relearn = Relearn.Length == 0 ? Relearn : (int[])Relearn.Clone();
IVs = IVs.Length == 0 ? IVs : (int[])IVs.Clone();
}
internal virtual EncounterStatic Clone()
@ -176,7 +176,7 @@ namespace PKHeX.Core
private void SetEncounterMoves(PKM pk, GameVersion version, int level)
{
var moves = Moves?.Length > 0 ? Moves : MoveLevelUp.GetEncounterMoves(pk, level, version);
var moves = Moves.Length > 0 ? Moves : MoveLevelUp.GetEncounterMoves(pk, level, version);
pk.Moves = moves;
pk.SetMaximumPPCurrent(moves);
}
@ -196,7 +196,7 @@ namespace PKHeX.Core
protected void SetIVs(PKM pk)
{
if (IVs != null)
if (IVs.Length != 0)
pk.SetRandomIVs(IVs, FlawlessIVCount);
else if (FlawlessIVCount > 0)
pk.SetRandomIVs(flawless: FlawlessIVCount);
@ -353,7 +353,7 @@ namespace PKHeX.Core
if (EggLocation == Locations.Daycare5 && Relearn.Length == 0 && pkm.RelearnMoves.Any(z => z != 0)) // gen7 eevee edge case
return false;
if (IVs != null && (Generation > 2 || pkm.Format <= 2)) // 1,2->7 regenerates IVs, only check if original IVs still exist
if (IVs.Length != 0 && (Generation > 2 || pkm.Format <= 2)) // 1,2->7 regenerates IVs, only check if original IVs still exist
{
if (!Legal.GetIsFixedIVSequenceValidSkipRand(IVs, pkm))
return false;

View file

@ -10,7 +10,7 @@ namespace PKHeX.Core
/// <summary>
/// Team Specification with required <see cref="Species"/>, <see cref="Nature"/> and Gender.
/// </summary>
public TeamLock[] Locks { get; internal set; } = Array.Empty<TeamLock>();
public readonly TeamLock[] Locks;
/// <summary>
/// Initial Shadow Gauge value.
@ -20,19 +20,9 @@ namespace PKHeX.Core
/// <summary>
/// Originates from the EReader scans (Japanese Only)
/// </summary>
public bool EReader { get; set; }
public bool EReader { get; internal set; }
internal override EncounterStatic Clone()
{
var result = (EncounterStaticShadow)base.Clone();
if (Locks.Length == 0)
return result;
result.Locks = new TeamLock[Locks.Length];
for (int i = 0; i < Locks.Length; i++)
result.Locks[i] = Locks[i].Clone();
return result;
}
public EncounterStaticShadow(TeamLock[] locks) => Locks = locks;
public EncounterStaticShadow() => Locks = Array.Empty<TeamLock>();
}
}

View file

@ -12,7 +12,7 @@ namespace PKHeX.Core
public class EncounterTrade : IEncounterable, IMoveset, IGeneration, ILocation, IContestStats, IVersion
{
public int Species { get; set; }
public int[] Moves { get; set; }
public int[] Moves { get; set; } = Array.Empty<int>();
public int Level { get; set; }
public int LevelMin => Level;
public int LevelMax => 100;
@ -24,7 +24,7 @@ namespace PKHeX.Core
public int TID { get; set; }
public int SID { get; set; }
public GameVersion Version { get; set; } = GameVersion.Any;
public int[] IVs { get; set; }
public int[] IVs { get; set; } = Array.Empty<int>();
public int Form { get; set; }
public virtual Shiny Shiny { get; set; } = Shiny.Never;
public int Gender { get; set; } = -1;
@ -58,11 +58,12 @@ namespace PKHeX.Core
public bool Fateful { get; set; }
public bool IsNicknamed { get; set; } = true;
public string[] Nicknames { get; internal set; }
public string[] TrainerNames { get; internal set; }
public string GetNickname(int language) => Nicknames?.Length > language ? Nicknames[language] : null;
public string GetOT(int language) => TrainerNames?.Length > language ? TrainerNames[language] : null;
public bool HasNickname => Nicknames != null;
public string[] Nicknames { get; internal set; } = Array.Empty<string>();
public string[] TrainerNames { get; internal set; } = Array.Empty<string>();
public string GetNickname(int language) => (uint)language < Nicknames.Length ? Nicknames[language] : string.Empty;
public string GetOT(int language) => (uint)language < TrainerNames.Length ? TrainerNames[language] : string.Empty;
public bool HasNickname => Nicknames.Length != 0;
public bool HasTrainerName => TrainerNames.Length != 0;
public static readonly int[] DefaultMetLocation =
{
@ -160,7 +161,7 @@ namespace PKHeX.Core
protected void SetIVs(PKM pk)
{
if (IVs != null)
if (IVs.Length != 0)
pk.SetRandomIVs(IVs, 0);
else
pk.SetRandomIVs(flawless: 3);
@ -168,7 +169,7 @@ namespace PKHeX.Core
private void SetMoves(PKM pk, GameVersion version, int level)
{
var moves = Moves ?? MoveLevelUp.GetEncounterMoves(pk, level, version);
var moves = Moves.Length != 0 ? Moves : MoveLevelUp.GetEncounterMoves(pk, level, version);
if (pk.Format == 1 && moves.All(z => z == 0))
moves = ((PersonalInfoG1)PersonalTable.RB[Species]).Moves;
pk.Moves = moves;
@ -230,7 +231,7 @@ namespace PKHeX.Core
public bool IsMatch(PKM pkm, int lvl)
{
if (IVs != null)
if (IVs.Length != 0)
{
if (!Legal.GetIsFixedIVSequenceValidSkipRand(IVs, pkm))
return false;
@ -332,7 +333,7 @@ namespace PKHeX.Core
{
if (Gender >= 0 && Gender != pkm.Gender)
return false;
if (IVs != null && !Legal.GetIsFixedIVSequenceValidNoRand(IVs, pkm))
if (IVs.Length != 0 && !Legal.GetIsFixedIVSequenceValidNoRand(IVs, pkm))
return false;
}
if (pkm.Met_Location != 0 && pkm.Format == 2 && pkm.Met_Location != 126)

View file

@ -45,7 +45,7 @@
public static EncounterCriteria GetCriteria(ShowdownSet s)
{
int gender = s.Gender == null ? -1 : PKX.GetGenderFromString(s.Gender);
int gender = string.IsNullOrWhiteSpace(s.Gender) ? -1 : PKX.GetGenderFromString(s.Gender);
return new EncounterCriteria
{
Gender = gender,

View file

@ -22,7 +22,7 @@ namespace PKHeX.Core
/// </returns>
public static LegalInfo FindVerifiedEncounter(PKM pkm)
{
LegalInfo info = new LegalInfo(pkm);
var info = new LegalInfo(pkm);
var encounters = EncounterGenerator.GetEncounters(pkm, info);
using var encounter = new PeekEnumerator<IEncounterable>(encounters);

View file

@ -69,7 +69,7 @@ namespace PKHeX.Core
else if (z is EncounterStaticShadow s)
{
bool valid = false;
if (s.IVs == null) // not ereader
if (s.IVs.Length == 0) // not ereader
{
valid = LockFinder.IsAllShadowLockValid(s, info.PIDIV, pkm);
}
@ -280,7 +280,7 @@ namespace PKHeX.Core
case EncounterTrade t:
return t.Generation == 2 ? GBEncounterPriority.TradeEncounterG2 : GBEncounterPriority.TradeEncounterG1;
case EncounterStatic s:
if (s.Moves != null && s.Moves[0] != 0 && pkm.Moves.Contains(s.Moves[0]))
if (s.Moves.Length != 0 && s.Moves[0] != 0 && pkm.Moves.Contains(s.Moves[0]))
return GBEncounterPriority.SpecialEncounter;
return GBEncounterPriority.StaticEncounter;
case EncounterSlot _:

View file

@ -15,10 +15,11 @@ namespace PKHeX.Core
/// Order in which <see cref="IEncounterable"/> objects are yielded from the <see cref="GenerateVersionEncounters"/> generator.
/// </summary>
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
public static IReadOnlyCollection<EncounterOrder> PriorityList { get; set; }
static EncounterMovesetGenerator() => ResetFilters();
public static IReadOnlyCollection<EncounterOrder> PriorityList { get; set; } = PriorityList = (EncounterOrder[])Enum.GetValues(typeof(EncounterOrder));
/// <summary>
/// Resets the <see cref="PriorityList"/> to the default values.
/// </summary>
public static void ResetFilters() => PriorityList = (EncounterOrder[])Enum.GetValues(typeof(EncounterOrder));
/// <summary>
@ -29,7 +30,7 @@ namespace PKHeX.Core
/// <param name="moves">Moves that the resulting <see cref="IEncounterable"/> must be able to learn.</param>
/// <param name="versions">Any specific version(s) to iterate for. If left blank, all will be checked.</param>
/// <returns>A consumable <see cref="PKM"/> list of possible results.</returns>
public static IEnumerable<PKM> GeneratePKMs(PKM pk, ITrainerInfo info, int[] moves = null, params GameVersion[] versions)
public static IEnumerable<PKM> GeneratePKMs(PKM pk, ITrainerInfo info, int[]? moves = null, params GameVersion[] versions)
{
pk.TID = info.TID;
var m = moves ?? pk.Moves;
@ -57,7 +58,7 @@ namespace PKHeX.Core
/// <param name="info">Trainer information of the receiver.</param>
/// <param name="generation">Specific generation to iterate versions for.</param>
/// <param name="moves">Moves that the resulting <see cref="IEncounterable"/> must be able to learn.</param>
public static IEnumerable<PKM> GeneratePKMs(PKM pk, ITrainerInfo info, int generation, int[] moves = null)
public static IEnumerable<PKM> GeneratePKMs(PKM pk, ITrainerInfo info, int generation, int[]? moves = null)
{
var vers = GameUtil.GetVersionsInGeneration(generation, pk.Version);
return GeneratePKMs(pk, info, moves, vers);
@ -70,7 +71,7 @@ namespace PKHeX.Core
/// <param name="generation">Specific generation to iterate versions for.</param>
/// <param name="moves">Moves that the resulting <see cref="IEncounterable"/> must be able to learn.</param>
/// <returns>A consumable <see cref="IEncounterable"/> list of possible encounters.</returns>
public static IEnumerable<IEncounterable> GenerateEncounter(PKM pk, int generation, int[] moves = null)
public static IEnumerable<IEncounterable> GenerateEncounter(PKM pk, int generation, int[]? moves = null)
{
var vers = GameUtil.GetVersionsInGeneration(generation, pk.Version);
return GenerateEncounters(pk, moves, vers);
@ -83,14 +84,14 @@ namespace PKHeX.Core
/// <param name="moves">Moves that the resulting <see cref="IEncounterable"/> must be able to learn.</param>
/// <param name="versions">Any specific version(s) to iterate for. If left blank, all will be checked.</param>
/// <returns>A consumable <see cref="IEncounterable"/> list of possible encounters.</returns>
public static IEnumerable<IEncounterable> GenerateEncounters(PKM pk, int[] moves = null, params GameVersion[] versions)
public static IEnumerable<IEncounterable> GenerateEncounters(PKM pk, int[]? moves = null, params GameVersion[] versions)
{
var m = moves ?? pk.Moves;
moves ??= pk.Moves;
if (versions.Length > 0)
return GenerateEncounters(pk, moves, (IReadOnlyList<GameVersion>)versions);
var vers = GameUtil.GetVersionsWithinRange(pk, pk.Format);
return vers.SelectMany(ver => GenerateVersionEncounters(pk, m, ver));
return vers.SelectMany(ver => GenerateVersionEncounters(pk, moves, ver));
}
/// <summary>
@ -100,10 +101,10 @@ namespace PKHeX.Core
/// <param name="moves">Moves that the resulting <see cref="IEncounterable"/> must be able to learn.</param>
/// <param name="vers">Any specific version(s) to iterate for. If left blank, all will be checked.</param>
/// <returns>A consumable <see cref="IEncounterable"/> list of possible encounters.</returns>
public static IEnumerable<IEncounterable> GenerateEncounters(PKM pk, int[] moves, IReadOnlyList<GameVersion> vers)
public static IEnumerable<IEncounterable> GenerateEncounters(PKM pk, int[]? moves, IReadOnlyList<GameVersion> vers)
{
var m = moves ?? pk.Moves;
return vers.SelectMany(ver => GenerateVersionEncounters(pk, m, ver));
moves ??= pk.Moves;
return vers.SelectMany(ver => GenerateVersionEncounters(pk, moves, ver));
}
/// <summary>

View file

@ -59,7 +59,7 @@ namespace PKHeX.Core
return table.Where(f => p.Any(r => r.Species == f.Species));
}
private static IEnumerable<EncounterTrade> GetEncounterTradeTableVC(GameVersion gameSource)
private static IEnumerable<EncounterTrade>? GetEncounterTradeTableVC(GameVersion gameSource)
{
if (GameVersion.RBY.Contains(gameSource))
return !ParseSettings.AllowGen1Tradeback ? Encounters1.TradeGift_RBY_NoTradeback : Encounters1.TradeGift_RBY_Tradeback;
@ -68,7 +68,7 @@ namespace PKHeX.Core
return null;
}
private static IEnumerable<EncounterTrade> GetEncounterTradeTable(PKM pkm)
private static IEnumerable<EncounterTrade>? GetEncounterTradeTable(PKM pkm)
{
return pkm.GenNumber switch
{

View file

@ -8,10 +8,10 @@ namespace PKHeX.Core
/// Iterates a generic collection with the ability to peek into the collection to see if the next element exists.
/// </summary>
/// <typeparam name="T">Generic Collection Element Type</typeparam>
public sealed class PeekEnumerator<T> : IEnumerator<T>
public sealed class PeekEnumerator<T> : IEnumerator<T> where T : class
{
private readonly IEnumerator<T> Enumerator;
private T peek;
private T? peek;
private bool didPeek;
#region IEnumerator Implementation
@ -34,12 +34,13 @@ namespace PKHeX.Core
public void Reset()
{
Enumerator.Reset();
peek = default;
didPeek = false;
}
object IEnumerator.Current => Current;
object? IEnumerator.Current => Current;
public void Dispose() => Enumerator.Dispose();
public T Current => didPeek ? peek : Enumerator.Current;
public T Current => didPeek ? peek! : Enumerator.Current;
#endregion
@ -68,10 +69,10 @@ namespace PKHeX.Core
if (!TryFetchPeek())
throw new InvalidOperationException("Enumeration already finished.");
return peek;
return peek!;
}
public T PeekOrDefault() => !TryFetchPeek() ? default : peek;
public T? PeekOrDefault() => !TryFetchPeek() ? default : peek;
/// <summary>
/// Checks if a Next element exists

View file

@ -7,11 +7,8 @@ namespace PKHeX.Core
/// </summary>
public static class EncounterSuggestion
{
public static EncounterStatic GetSuggestedMetInfo(PKM pkm)
public static EncounterStatic? GetSuggestedMetInfo(PKM pkm)
{
if (pkm == null)
return null;
int loc = GetSuggestedTransferLocation(pkm);
if (pkm.WasEgg)

View file

@ -9,9 +9,9 @@ namespace PKHeX.Core
/// </summary>
public sealed class ValidEncounterMoves
{
public List<int>[] LevelUpMoves { get; } = Empty;
public List<int>[] TMHMMoves { get; } = Empty;
public List<int>[] TutorMoves { get; } = Empty;
public IReadOnlyList<int>[] LevelUpMoves { get; } = Empty;
public IReadOnlyList<int>[] TMHMMoves { get; } = Empty;
public IReadOnlyList<int>[] TutorMoves { get; } = Empty;
public int[] Relearn = Array.Empty<int>();
private const int EmptyCount = PKX.Generation + 1; // one for each generation index (and 0th)
@ -28,6 +28,10 @@ namespace PKHeX.Core
{
LevelUpMoves = levelup;
}
public ValidEncounterMoves()
{
LevelUpMoves = Array.Empty<int[]>();
}
}
public sealed class LevelUpRestriction

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -22,20 +23,17 @@ namespace PKHeX.Core
get => _match;
set
{
if (_match != null && (value.LevelMin != _match.LevelMin || value.Species != _match.Species))
if (_match != EncounterInvalid.Default && (value.LevelMin != _match.LevelMin || value.Species != _match.Species))
_evochains = null; // clear if evo chain has the potential to be different
_match = value;
Parse.Clear();
}
}
private IEncounterable _match;
/// <summary>Indicates whether or not the <see cref="PKM"/> originated from <see cref="GameVersion.XD"/>.</summary>
public bool WasXD => pkm?.Version == 15 && EncounterMatch is IVersion v && v.Version == GameVersion.XD;
private IEncounterable _match = EncounterInvalid.Default;
/// <summary>Base Relearn Moves for the <see cref="EncounterMatch"/>.</summary>
public IReadOnlyList<int> RelearnBase { get; internal set; }
public IReadOnlyList<int> RelearnBase { get; internal set; } = Array.Empty<int>();
/// <summary>Top level Legality Check result list for the <see cref="EncounterMatch"/>.</summary>
public readonly List<CheckResult> Parse = new List<CheckResult>();
@ -43,12 +41,24 @@ namespace PKHeX.Core
public CheckResult[] Relearn { get; internal set; } = new CheckResult[4];
public CheckMoveResult[] Moves { get; internal set; } = new CheckMoveResult[4];
public ValidEncounterMoves EncounterMoves { get; internal set; }
private static readonly ValidEncounterMoves NONE = new ValidEncounterMoves();
public ValidEncounterMoves EncounterMoves { get; internal set; } = NONE;
public IReadOnlyList<EvoCriteria>[] EvoChainsAllGens => _evochains ??= EvolutionChain.GetEvolutionChainsAllGens(pkm, EncounterMatch);
private IReadOnlyList<EvoCriteria>[] _evochains;
private IReadOnlyList<EvoCriteria>[]? _evochains;
/// <summary><see cref="RNG"/> related information that generated the <see cref="PKM.PID"/>/<see cref="PKM.IVs"/> value(s).</summary>
public PIDIV PIDIV { get; internal set; }
public PIDIV PIDIV
{
get => _pidiv;
internal set
{
_pidiv = value;
PIDParsed = true;
}
}
public bool PIDParsed { get; private set; }
private PIDIV _pidiv = PIDIV.None;
/// <summary>Indicates whether or not the <see cref="PIDIV"/> can originate from the <see cref="EncounterMatch"/>.</summary>
/// <remarks>This boolean is true until all valid <see cref="PIDIV"/> encounters are tested, after which it is false.</remarks>
@ -58,12 +68,9 @@ namespace PKHeX.Core
/// <remarks>This boolean is true until all valid <see cref="Frame"/> entries are tested for all possible <see cref="EncounterSlot"/> matches, after which it is false.</remarks>
public bool FrameMatches { get; internal set; } = true;
public readonly bool Korean;
public LegalInfo(PKM pk)
{
pkm = pk;
Korean = pk.Korean;
// Store repeatedly accessed values
Game = (GameVersion)pkm.Version;
@ -71,7 +78,7 @@ namespace PKHeX.Core
}
/// <summary>List of all near-matches that were rejected for a given reason.</summary>
public List<EncounterRejected> InvalidMatches;
public List<EncounterRejected>? InvalidMatches;
internal void Reject(CheckResult c)
{

View file

@ -43,15 +43,17 @@ namespace PKHeX.Core
var restrict = new LevelUpRestriction(pkm, info);
info.EncounterMoves = new ValidEncounterMoves(pkm, restrict);
List<int> defaultG1LevelMoves = null;
List<int> defaultG2LevelMoves = null;
IReadOnlyList<int> defaultG1LevelMoves = Array.Empty<int>();
IReadOnlyList<int> defaultG2LevelMoves = Array.Empty<int>();
var defaultTradeback = pkm.TradebackStatus;
bool gb = false;
if (info.EncounterMatch is IGeneration g && g.Generation <= 2)
{
gb = true;
defaultG1LevelMoves = info.EncounterMoves.LevelUpMoves[1];
defaultG2LevelMoves = pkm.InhabitedGeneration(2) ? info.EncounterMoves.LevelUpMoves[2] : null;
if (pkm.InhabitedGeneration(2))
defaultG2LevelMoves = info.EncounterMoves.LevelUpMoves[2];
// Generation 1 can have different minimum level in different encounter of the same species; update valid level moves
UpdateGen1LevelUpMoves(pkm, info.EncounterMoves, restrict.MinimumLevelGen1, g.Generation, info);
@ -179,7 +181,7 @@ namespace PKHeX.Core
return ParseMovesSpecialMoveset(pkm, Moves, info);
var InitialMoves = Array.Empty<int>();
var SpecialMoves = GetSpecialMoves(info.EncounterMatch);
var games = info.EncounterMatch is IGeneration g && g.Generation == 1 ? GBRestrictions.GetGen1Versions(info) : GBRestrictions.GetGen2Versions(info);
var games = info.EncounterMatch is IGeneration g && g.Generation == 1 ? GBRestrictions.GetGen1Versions(info) : GBRestrictions.GetGen2Versions(info, pkm.Korean);
foreach (var ver in games)
{
var VerInitialMoves = MoveLevelUp.GetEncounterMoves(G1Encounter.Species, 0, G1Encounter.LevelMin, ver);
@ -212,7 +214,7 @@ namespace PKHeX.Core
private static int[] GetSpecialMoves(IEncounterable EncounterMatch)
{
if (EncounterMatch is IMoveset mg && mg.Moves != null)
if (EncounterMatch is IMoveset mg)
return mg.Moves;
return Array.Empty<int>();
}
@ -258,7 +260,7 @@ namespace PKHeX.Core
return res;
// Encapsulate arguments to simplify method calls
var moveInfo = new LearnInfo(pkm) { Source = source };
var moveInfo = new LearnInfo(pkm, source);
// Check moves going backwards, marking the move valid in the most current generation when it can be learned
int[] generations = GetGenMovesCheckOrder(pkm);
if (pkm.Format <= 2)
@ -549,7 +551,7 @@ namespace PKHeX.Core
}
}
private static void ParseEvolutionsIncompatibleMoves(PKM pkm, IList<CheckMoveResult> res, int[] moves, List<int> tmhm)
private static void ParseEvolutionsIncompatibleMoves(PKM pkm, IList<CheckMoveResult> res, IReadOnlyList<int> moves, IReadOnlyList<int> tmhm)
{
GBRestrictions.GetIncompatibleEvolutionMoves(pkm, moves, tmhm,
out var prevSpeciesID,
@ -829,7 +831,7 @@ namespace PKHeX.Core
{
if (generation >= 3)
return;
var lvlG1 = info.EncounterMatch?.LevelMin + 1 ?? 6;
var lvlG1 = info.EncounterMatch.LevelMin + 1;
if (lvlG1 == defaultLvlG1)
return;
EncounterMoves.LevelUpMoves[1] = Legal.GetValidMoves(pkm, info.EvoChainsAllGens[1], generation: 1, minLvLG1: lvlG1, LVL: true, Tutor: false, Machine: false, MoveReminder: false).ToList();
@ -839,7 +841,7 @@ namespace PKHeX.Core
{
if (generation >= 3)
return;
var lvlG2 = info.EncounterMatch?.LevelMin + 1 ?? 6;
var lvlG2 = info.EncounterMatch.LevelMin + 1;
if (lvlG2 == defaultLvlG2)
return;
EncounterMoves.LevelUpMoves[2] = Legal.GetValidMoves(pkm, info.EvoChainsAllGens[2], generation: 2, minLvLG2: defaultLvlG2, LVL: true, Tutor: false, Machine: false, MoveReminder: false).ToList();

View file

@ -15,7 +15,7 @@ namespace PKHeX.Core
int species = BitConverter.ToUInt16(data, offset + 4);
if (method == 0)
return null;
throw new ArgumentException(nameof(data));
// To have the same structure as gen 6
// Gen 4 Method 6 is Gen 6 Method 7, G4 7 = G6 8, and so on

View file

@ -15,7 +15,7 @@ namespace PKHeX.Core
int species = BitConverter.ToUInt16(data, offset + 4);
if (method == 0)
return null;
throw new ArgumentException(nameof(data));
var evo = new EvolutionMethod
{

View file

@ -104,7 +104,7 @@ namespace PKHeX
return new[] { moves1, moves2 };
}
internal static void GetIncompatibleEvolutionMoves(PKM pkm, int[] moves, List<int> tmhm, out int previousspecies, out IList<int> incompatible_previous, out IList<int> incompatible_current)
internal static void GetIncompatibleEvolutionMoves(PKM pkm, IReadOnlyList<int> moves, IReadOnlyList<int> tmhm, out int previousspecies, out IList<int> incompatible_previous, out IList<int> incompatible_current)
{
switch (pkm.Species)
{
@ -154,7 +154,7 @@ namespace PKHeX
previousspecies = 0;
}
internal static int GetRequiredMoveCount(PKM pk, int[] moves, LegalInfo info, int[] initialmoves)
internal static int GetRequiredMoveCount(PKM pk, IReadOnlyList<int> moves, LegalInfo info, IReadOnlyList<int> initialmoves)
{
if (!pk.Gen1_NotTradeback) // No Move Deleter in Gen 1
return 1; // Move Deleter exits, slots from 2 onwards can always be empty
@ -174,7 +174,7 @@ namespace PKHeX
return Math.Min(4, required);
}
private static int GetRequiredMoveCount(PKM pk, int[] moves, List<int>[] learn, int[] initialmoves)
private static int GetRequiredMoveCount(PKM pk, IReadOnlyList<int> moves, IReadOnlyList<int>[] learn, IReadOnlyList<int> initialmoves)
{
if (SpecialMinMoveSlots.Contains(pk.Species))
return GetRequiredMoveCountSpecial(pk, moves, learn);
@ -185,7 +185,7 @@ namespace PKHeX
return required != 0 ? required : GetRequiredMoveCountDecrement(pk, moves, learn, initialmoves);
}
private static int GetRequiredMoveSlotsRegular(PKM pk, int[] moves, List<int>[] learn, int[] initialmoves)
private static int GetRequiredMoveSlotsRegular(PKM pk, IReadOnlyList<int> moves, IReadOnlyList<int>[] learn, IReadOnlyList<int> initialmoves)
{
int species = pk.Species;
int catch_rate = ((PK1)pk).Catch_Rate;
@ -209,7 +209,7 @@ namespace PKHeX
return IsMoveCountRequired3(species, pk.CurrentLevel, moves) ? 3 : 0; // no match
}
private static bool IsMoveCountRequired3(int species, int level, int[] moves)
private static bool IsMoveCountRequired3(int species, int level, IReadOnlyList<int> moves)
{
// Species that evolve and learn the 4th move as evolved species at a greather level than base species
// The 4th move is included in the level up table set as a preevolution move,
@ -228,7 +228,7 @@ namespace PKHeX
}
}
private static int GetRequiredMoveCountDecrement(PKM pk, int[] moves, List<int>[] learn, int[] initialmoves)
private static int GetRequiredMoveCountDecrement(PKM pk, IReadOnlyList<int> moves, IReadOnlyList<int>[] learn, IReadOnlyList<int> initialmoves)
{
int usedslots = initialmoves.Union(learn[1]).Where(m => m != 0).Distinct().Count();
switch (pk.Species)
@ -272,7 +272,7 @@ namespace PKHeX
return usedslots;
}
private static int GetRequiredMoveCountSpecial(PKM pk, int[] moves, List<int>[] learn)
private static int GetRequiredMoveCountSpecial(PKM pk, IReadOnlyList<int> moves, IReadOnlyList<int>[] learn)
{
// Species with few mandatory slots, species with stone evolutions that could evolve at lower level and do not learn any more moves
// and Pikachu and Nidoran family, those only have mandatory the initial moves and a few have one level up moves,
@ -322,9 +322,9 @@ namespace PKHeX
return MoveLevelUp.GetMovesLevelUp1(basespecies, 0, maxlevel, minlevel);
}
internal static IEnumerable<GameVersion> GetGen2Versions(LegalInfo Info)
internal static IEnumerable<GameVersion> GetGen2Versions(LegalInfo Info, bool korean)
{
if (ParseSettings.AllowGen2Crystal(Info.Korean) && Info.Game.Contains(GameVersion.C))
if (ParseSettings.AllowGen2Crystal(korean) && Info.Game.Contains(GameVersion.C))
yield return GameVersion.C;
yield return GameVersion.GS;
}

View file

@ -6,22 +6,23 @@ namespace PKHeX.Core
/// <summary>
/// Level Up Learn Movepool Information
/// </summary>
public abstract class Learnset
public sealed class Learnset
{
/// <summary>
/// Amount of moves present.
/// </summary>
protected int Count;
/// <summary>
/// Moves that can be learned.
/// </summary>
protected int[] Moves;
private readonly int[] Moves;
/// <summary>
/// Levels at which a move at a given index can be learned.
/// </summary>
protected int[] Levels;
private readonly int[] Levels;
public Learnset(int[] moves, int[] levels)
{
Moves = moves;
Levels = levels;
}
/// <summary>
/// Returns the moves a Pokémon can learn between the specified level range.
@ -147,7 +148,7 @@ namespace PKHeX.Core
return Math.Max(end - 4, 1);
}
private Dictionary<int, int> Learn;
private Dictionary<int, int>? Learn;
private Dictionary<int, int> GetDictionary()
{
@ -165,7 +166,7 @@ namespace PKHeX.Core
/// <returns>Level the move is learned at. If the result is below 0, the move cannot be learned by leveling up.</returns>
public int GetLevelLearnMove(int move)
{
return (Learn ?? (Learn = GetDictionary())).TryGetValue(move, out var level) ? level : -1;
return (Learn ??= GetDictionary()).TryGetValue(move, out var level) ? level : -1;
}
/// <summary>Returns the level that a Pokémon can learn the specified move.</summary>

View file

@ -1,35 +0,0 @@
namespace PKHeX.Core
{
/// <summary>
/// Level Up Learn Movepool Information (Generation 1/2)
/// </summary>
public sealed class Learnset1 : Learnset
{
private Learnset1(byte[] data, ref int offset)
{
int end = offset; // scan for count
while (data[end] != 0)
end += 2;
Count = (end - offset) / 2;
Moves = new int[Count];
Levels = new int[Count];
for (int i = 0; i < Moves.Length; i++)
{
Levels[i] = data[offset++];
Moves[i] = data[offset++];
}
++offset;
}
public static Learnset[] GetArray(byte[] input, int maxSpecies)
{
var data = new Learnset[maxSpecies + 1];
int offset = 0;
for (int s = 0; s < data.Length; s++)
data[s] = new Learnset1(input, ref offset);
return data;
}
}
}

View file

@ -1,35 +0,0 @@
using System;
using System.IO;
namespace PKHeX.Core
{
/// <summary>
/// Level Up Learn Movepool Information
/// </summary>
public sealed class Learnset6 : Learnset
{
private Learnset6(byte[] data)
{
if (data.Length < 4 || data.Length % 4 != 0)
{ Count = 0; Levels = Moves = Array.Empty<int>(); return; }
Count = (data.Length / 4) - 1;
Moves = new int[Count];
Levels = new int[Count];
using var ms = new MemoryStream(data);
using var br = new BinaryReader(ms);
for (int i = 0; i < Count; i++)
{
Moves[i] = br.ReadInt16();
Levels[i] = br.ReadInt16();
}
}
public static Learnset[] GetArray(byte[][] entries)
{
Learnset[] data = new Learnset[entries.Length];
for (int i = 0; i < data.Length; i++)
data[i] = new Learnset6(entries[i]);
return data;
}
}
}

View file

@ -0,0 +1,77 @@
using System;
namespace PKHeX.Core
{
/// <summary>
/// Unpacks <see cref="Learnset"/> data from legality binary inputs.
/// </summary>
public static class LearnsetReader
{
private static readonly Learnset EMPTY = new Learnset(Array.Empty<int>(), Array.Empty<int>());
public static Learnset[] GetArray(byte[] input, int maxSpecies)
{
var data = new Learnset[maxSpecies + 1];
int offset = 0;
for (int s = 0; s < data.Length; s++)
data[s] = ReadLearnset8(input, ref offset);
return data;
}
public static Learnset[] GetArray(byte[][] entries)
{
Learnset[] data = new Learnset[entries.Length];
for (int i = 0; i < data.Length; i++)
data[i] = ReadLearnset16(entries[i]);
return data;
}
/// <summary>
/// Reads a Level up move pool definition from a contiguous chunk of GB era ROM data.
/// </summary>
/// <remarks>Moves and Levels are 8-bit</remarks>
private static Learnset ReadLearnset8(byte[] data, ref int offset)
{
int end = offset; // scan for count
if (data[end] == 0)
{
++offset;
return EMPTY;
}
do { end += 2; } while (data[end] != 0);
var Count = (end - offset) / 2;
var Moves = new int[Count];
var Levels = new int[Count];
for (int i = 0; i < Moves.Length; i++)
{
Levels[i] = data[offset++];
Moves[i] = data[offset++];
}
++offset;
return new Learnset(Moves, Levels);
}
/// <summary>
/// Reads a Level up move pool definition from a single move pool definition.
/// </summary>
/// <remarks>Count of moves, followed by Moves and Levels which are 16-bit</remarks>
private static Learnset ReadLearnset16(byte[] data)
{
if (data.Length < 4 || data.Length % 4 != 0)
return EMPTY;
var Count = (data.Length / 4) - 1;
var Moves = new int[Count];
var Levels = new int[Count];
for (int i = 0; i < Count; i++)
{
int ofs = i * 4;
Moves[i] = BitConverter.ToInt16(data, ofs);
Levels[i] = BitConverter.ToInt16(data, ofs + 2);
}
return new Learnset(Moves, Levels);
}
}
}

View file

@ -11,13 +11,14 @@ namespace PKHeX.Core
public List<int> LevelUpEggMoves { get; } = new List<int>();
public List<int> EventEggMoves { get; } = new List<int>();
public List<int> IncenseMoves { get; } = new List<int>();
public MoveParseSource Source { get; set; }
public readonly MoveParseSource Source;
public readonly bool IsGen2Pkm;
public LearnInfo(PKM pkm)
public LearnInfo(PKM pkm, MoveParseSource source)
{
IsGen2Pkm = pkm.Format == 2 || pkm.VC2;
Source = source;
}
}

View file

@ -30,49 +30,42 @@ namespace PKHeX.Core
case 3:
return EggMovesRS[species].Moves;
case 4:
switch (version)
return version switch
{
case GameVersion.HG:
case GameVersion.SS:
return EggMovesHGSS[species].Moves;
default:
return EggMovesDPPt[species].Moves;
}
GameVersion.HG => EggMovesHGSS[species].Moves,
GameVersion.SS => EggMovesHGSS[species].Moves,
_ => EggMovesDPPt[species].Moves
};
case 5:
return EggMovesBW[species].Moves;
case 6: // entries per species
switch (version)
return version switch
{
case GameVersion.OR:
case GameVersion.AS:
return EggMovesAO[species].Moves;
default:
return EggMovesXY[species].Moves;
}
GameVersion.OR => EggMovesAO[species].Moves,
GameVersion.AS => EggMovesAO[species].Moves,
_ => EggMovesXY[species].Moves
};
case 7: // entries per form if required
switch (version)
return version switch
{
case GameVersion.US:
case GameVersion.UM:
return GetFormEggMoves(species, formnum, EggMovesUSUM);
default:
return GetFormEggMoves(species, formnum, EggMovesSM);
}
GameVersion.US => GetFormEggMoves(species, formnum, EggMovesUSUM),
GameVersion.UM => GetFormEggMoves(species, formnum, EggMovesUSUM),
_ => GetFormEggMoves(species, formnum, EggMovesSM)
};
case 8:
switch (version)
return version switch
{
default:
return GetFormEggMoves(species, formnum, EggMovesSWSH);
}
_ => GetFormEggMoves(species, formnum, EggMovesSWSH)
};
default:
return Array.Empty<int>();
}
}
private static int[] GetFormEggMoves(int species, int formnum, EggMoves[] table)
private static int[] GetFormEggMoves(int species, int formnum, IReadOnlyList<EggMoves7> table)
{
var entry = table[species];
if (formnum > 0 && AlolanOriginForms.Contains(species))

View file

@ -239,7 +239,7 @@ namespace PKHeX.Core
};
}
private static Learnset GetDeoxysLearn3(int form, GameVersion ver = Any)
private static Learnset? GetDeoxysLearn3(int form, GameVersion ver = Any)
{
const int index = (int)Species.Deoxys;
if (ver == Any)
@ -266,7 +266,7 @@ namespace PKHeX.Core
version = (GameVersion)pkm.Version;
return Generation switch
{
1 => (IEnumerable<int>) GetMovesLevelUp1(species, form, lvl, minlvlG1, version),
1 => GetMovesLevelUp1(species, form, lvl, minlvlG1, version),
2 => GetMovesLevelUp2(species, form, lvl, minlvlG2, pkm.Korean, pkm.LearnMovesNew2Disallowed(), version),
3 => GetMovesLevelUp3(species, form, lvl, version),
4 => GetMovesLevelUp4(species, form, lvl, version),
@ -274,7 +274,7 @@ namespace PKHeX.Core
6 => GetMovesLevelUp6(species, form, lvl, version),
7 => GetMovesLevelUp7(species, form, lvl, MoveReminder, version),
8 => GetMovesLevelUp8(species, form, lvl, MoveReminder, version),
_ => Array.Empty<int>()
_ => (IEnumerable<int>)Array.Empty<int>()
};
}

View file

@ -13,13 +13,10 @@ namespace PKHeX.Core
/// <returns><see cref="IEnumerable{Frame}"/> to yield possible encounter details for further filtering</returns>
public static IEnumerable<Frame> GetFrames(PIDIV pidiv, PKM pk)
{
if (pidiv.RNG == null)
return Enumerable.Empty<Frame>();
FrameGenerator info = new FrameGenerator(pidiv, pk);
if (info.FrameType == FrameType.None)
if (pk.Version == (int)GameVersion.CXD)
return Enumerable.Empty<Frame>();
info.Nature = pk.EncryptionConstant % 25;
var info = new FrameGenerator(pk) {Nature = pk.EncryptionConstant % 25};
// gather possible nature determination seeds until a same-nature PID breaks the unrolling
var seeds = pk.Species == 201 && pk.FRLG // reversed await case
@ -280,12 +277,12 @@ namespace PKHeX.Core
if (!sync && !reg) // doesn't generate nature frame
continue;
uint prev = pidiv.RNG.Prev(s);
uint prev = RNG.LCRNG.Prev(s);
if (info.AllowLeads && reg) // check for failed sync
{
var failsync = (info.DPPt ? prev >> 31 : (prev >> 16) & 1) != 1;
if (failsync)
yield return info.GetFrame(pidiv.RNG.Prev(prev), LeadRequired.SynchronizeFail);
yield return info.GetFrame(RNG.LCRNG.Prev(prev), LeadRequired.SynchronizeFail);
}
if (sync)
yield return info.GetFrame(prev, LeadRequired.Synchronize);
@ -298,7 +295,7 @@ namespace PKHeX.Core
else
{
if (info.Safari3)
prev = pidiv.RNG.Prev(prev); // wasted RNG call
prev = RNG.LCRNG.Prev(prev); // wasted RNG call
yield return info.GetFrame(prev, LeadRequired.None);
}
}
@ -382,7 +379,7 @@ namespace PKHeX.Core
if (nature != info.Nature)
continue;
var prev = pidiv.RNG.Prev(s);
var prev = RNG.LCRNG.Prev(s);
var proc = prev >> 16;
bool charmProc = (info.DPPt ? proc / 0x5556 : proc % 3) != 0; // 2/3 odds
if (!charmProc)

View file

@ -1,4 +1,6 @@
namespace PKHeX.Core
using System;
namespace PKHeX.Core
{
public sealed class FrameGenerator
{
@ -23,13 +25,13 @@
};
/// <summary>
/// Gets the Search Criteria parameters necessary for generating <see cref="SeedInfo"/> and <see cref="Frame"/> objects.
/// Gets the Search Criteria parameters necessary for generating <see cref="SeedInfo"/> and <see cref="Frame"/> objects for Gen3/4 mainline games.
/// </summary>
/// <param name="pidiv">Info used to determine the <see cref="FrameType"/>.</param>
/// <param name="pk"><see cref="PKM"/> object containing various accessible information required for the encounter.</param>
/// <returns>Object containing search criteria to be passed by reference to search/filter methods.</returns>
public FrameGenerator(PIDIV pidiv, PKM pk)
public FrameGenerator(PKM pk)
{
RNG = RNG.LCRNG;
var ver = (GameVersion)pk.Version;
switch (ver)
{
@ -41,7 +43,6 @@
case GameVersion.E:
DPPt = false;
FrameType = FrameType.MethodH;
RNG = pidiv.RNG;
Safari3 = pk.Ball == 5 && !pk.FRLG;
if (ver != GameVersion.E)
@ -68,7 +69,7 @@
DPPt = true;
AllowLeads = true;
FrameType = FrameType.MethodJ;
RNG = pidiv.RNG;
RNG = RNG.LCRNG;
return;
// Method K
@ -77,8 +78,10 @@
DPPt = false;
AllowLeads = true;
FrameType = FrameType.MethodK;
RNG = pidiv.RNG;
RNG = RNG.LCRNG;
return;
default:
throw new ArgumentException(nameof(ver));
}
}

View file

@ -21,7 +21,7 @@ namespace PKHeX.Core
yield return new SeedInfo { Seed = seed };
var s1 = seed;
var s2 = pidiv.RNG.Prev(s1);
var s2 = RNG.LCRNG.Prev(s1);
while (true)
{
var a = s2 >> 16;
@ -39,8 +39,8 @@ namespace PKHeX.Core
break;
}
s1 = pidiv.RNG.Prev(s2);
s2 = pidiv.RNG.Prev(s1);
s1 = RNG.LCRNG.Prev(s2);
s2 = RNG.LCRNG.Prev(s1);
yield return new SeedInfo { Seed = s1, Charm3 = charm3 };
}
@ -59,7 +59,7 @@ namespace PKHeX.Core
yield return new SeedInfo { Seed = seed };
var s1 = seed;
var s2 = pidiv.RNG.Prev(s1);
var s2 = RNG.LCRNG.Prev(s1);
while (true)
{
var a = s2 >> 16;
@ -77,8 +77,8 @@ namespace PKHeX.Core
}
}
s1 = pidiv.RNG.Prev(s2);
s2 = pidiv.RNG.Prev(s1);
s1 = RNG.LCRNG.Prev(s2);
s2 = RNG.LCRNG.Prev(s1);
yield return new SeedInfo { Seed = s1 };
}

View file

@ -2,13 +2,24 @@ namespace PKHeX.Core
{
public sealed class TeamLock
{
public int Species;
public string Comment;
public NPCLock[] Locks;
public readonly int Species;
public readonly string Comment;
public readonly NPCLock[] Locks;
internal TeamLock Clone()
internal TeamLock Clone() => new TeamLock(Species, Comment, (NPCLock[])Locks.Clone());
public TeamLock(int species, NPCLock[] locks)
{
return new TeamLock { Comment = Comment, Locks = (NPCLock[])Locks.Clone() };
Species = species;
Locks = locks;
Comment = string.Empty;
}
public TeamLock(int species, string comment, NPCLock[] locks)
{
Species = species;
Locks = locks;
Comment = comment;
}
}
}

View file

@ -76,7 +76,7 @@ namespace PKHeX.Core
/// <param name="frame">Frame at which the search starts/continues at.</param>
/// <param name="prior">Prior <see cref="NPCLock"/> data. If this is the last lock in the CPU Team, this is null.</param>
/// <returns>True if the <see cref="Specifications"/> are valid.</returns>
private bool FindLockSeed(int frame = 0, NPCLock prior = null)
private bool FindLockSeed(int frame = 0, NPCLock? prior = null)
{
if (Locks.Count == 0) // full team reverse-generated
return VerifyNPC(frame);
@ -102,7 +102,7 @@ namespace PKHeX.Core
/// <param name="current">Current lock criteria to satisfy. Used to find valid <see cref="SeedFrame"/> results to yield.</param>
/// <param name="prior">Prior lock criteria. Used for determining when the traversal stops.</param>
/// <returns>List of possible locks for the provided input.</returns>
private IEnumerable<SeedFrame> GetPossibleLocks(int ctr, NPCLock current, NPCLock prior)
private IEnumerable<SeedFrame> GetPossibleLocks(int ctr, NPCLock current, NPCLock? prior)
{
if (prior?.Shadow != false)
return GetSingleLock(ctr, current);

View file

@ -10,7 +10,6 @@ namespace PKHeX.Core
/// </summary>
public static class MethodFinder
{
private static readonly PIDIV NonMatch = new PIDIV {NoSeed = true, Type = PIDType.None};
/// <summary>
/// Analyzes a <see cref="PKM"/> to find a matching PIDIV method.
@ -99,7 +98,7 @@ namespace PKHeX.Core
var ivD = D >> 16 & 0x7FFF;
if (iv2 == ivD) // ABCD
{
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_1};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_1};
return true;
}
@ -107,7 +106,7 @@ namespace PKHeX.Core
var ivE = E >> 16 & 0x7FFF;
if (iv2 == ivE) // ABCE
{
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_4};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_4};
return true;
}
}
@ -122,7 +121,7 @@ namespace PKHeX.Core
var ivE = E >> 16 & 0x7FFF;
if (iv2 == ivE) // ABDE
{
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_2};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_2};
return true;
}
}
@ -142,7 +141,7 @@ namespace PKHeX.Core
var ivE = E >> 16 & 0x7FFF;
if (iv2 != ivE)
continue;
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_3};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_3};
return true;
}
return GetNonMatch(out pidiv);
@ -168,7 +167,7 @@ namespace PKHeX.Core
var ivD = D >> 16 & 0x7FFF;
if (iv2 == ivD) // BACD
{
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_1_Unown};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_1_Unown};
return true;
}
@ -176,7 +175,7 @@ namespace PKHeX.Core
var ivE = E >> 16 & 0x7FFF;
if (iv2 == ivE) // BACE
{
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_4_Unown};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_4_Unown};
return true;
}
}
@ -191,7 +190,7 @@ namespace PKHeX.Core
var ivE = E >> 16 & 0x7FFF;
if (iv2 == ivE) // BADE
{
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_2_Unown};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_2_Unown};
return true;
}
}
@ -211,7 +210,7 @@ namespace PKHeX.Core
var ivE = E >> 16 & 0x7FFF;
if (iv2 != ivE)
continue;
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_3_Unown};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_3_Unown};
return true;
}
return GetNonMatch(out pidiv);
@ -230,7 +229,7 @@ namespace PKHeX.Core
if (iv1 != ivC)
continue;
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.Method_1_Roamer};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.Method_1_Roamer};
return true;
}
return GetNonMatch(out pidiv);
@ -272,7 +271,7 @@ namespace PKHeX.Core
}
pidiv = new PIDIVTSV
{
OriginSeed = RNG.XDRNG.Prev(A), RNG = RNG.XDRNG, Type = PIDType.CXDAnti,
OriginSeed = RNG.XDRNG.Prev(A), RNG = RNGType.XDRNG, Type = PIDType.CXDAnti,
TSV1 = tsv1, TSV2 = tsv2,
};
return true;
@ -280,7 +279,7 @@ namespace PKHeX.Core
continue;
}
pidiv = new PIDIV {OriginSeed = RNG.XDRNG.Prev(A), RNG = RNG.XDRNG, Type = PIDType.CXD};
pidiv = new PIDIV {OriginSeed = RNG.XDRNG.Prev(A), RNG = RNGType.XDRNG, Type = PIDType.CXD};
return true;
}
return GetNonMatch(out pidiv);
@ -315,7 +314,7 @@ namespace PKHeX.Core
if (seed >> 16 != pk.SID)
continue;
pidiv = new PIDIV {OriginSeed = RNG.XDRNG.Prev(seed), RNG = RNG.XDRNG, Type = PIDType.Channel};
pidiv = new PIDIV {OriginSeed = RNG.XDRNG.Prev(seed), RNG = RNGType.XDRNG, Type = PIDType.Channel};
return true;
}
return GetNonMatch(out pidiv);
@ -333,7 +332,7 @@ namespace PKHeX.Core
if (!IVsMatch(C >> 16, D >> 16, IVs))
continue;
pidiv = new PIDIV {OriginSeed = seed, RNG = RNG.LCRNG, Type = PIDType.G4MGAntiShiny};
pidiv = new PIDIV {OriginSeed = seed, RNG = RNGType.LCRNG, Type = PIDType.G4MGAntiShiny};
return true;
}
return GetNonMatch(out pidiv);
@ -375,7 +374,7 @@ namespace PKHeX.Core
if (nature + rate != pid)
break;
pidiv = new PIDIV {NoSeed = true, RNG = RNG.LCRNG, Type = PIDType.CuteCharm};
pidiv = new PIDIV {NoSeed = true, RNG = RNGType.LCRNG, Type = PIDType.CuteCharm};
return true;
case 1: // female
if (pid >= 25)
@ -383,7 +382,7 @@ namespace PKHeX.Core
if (254 <= getRatio()) // no modification for PID
break;
pidiv = new PIDIV {NoSeed = true, RNG = RNG.LCRNG, Type = PIDType.CuteCharm};
pidiv = new PIDIV {NoSeed = true, RNG = RNGType.LCRNG, Type = PIDType.CuteCharm};
return true;
}
return GetNonMatch(out pidiv);
@ -426,7 +425,7 @@ namespace PKHeX.Core
continue;
s = RNG.LCRNG.Reverse(lower, 2); // unroll one final time to get the origin seed
pidiv = new PIDIV {OriginSeed = s, RNG = RNG.LCRNG, Type = PIDType.ChainShiny};
pidiv = new PIDIV {OriginSeed = s, RNG = RNGType.LCRNG, Type = PIDType.ChainShiny};
return true;
}
return GetNonMatch(out pidiv);
@ -478,11 +477,11 @@ namespace PKHeX.Core
if ((sn & 0xFFFF0000) != 0)
continue;
// shift from unrestricted enum val to restricted enum val
pidiv = new PIDIV {OriginSeed = sn, RNG = RNG.LCRNG, Type = --type };
pidiv = new PIDIV {OriginSeed = sn, RNG = RNGType.LCRNG, Type = --type };
return true;
}
// no restricted seed found, thus unrestricted
pidiv = new PIDIV {OriginSeed = s, RNG = RNG.LCRNG, Type = type};
pidiv = new PIDIV {OriginSeed = s, RNG = RNGType.LCRNG, Type = type};
return true;
}
return GetNonMatch(out pidiv);
@ -506,7 +505,7 @@ namespace PKHeX.Core
if (!(gender == 0 && IsAzurillEdgeCaseM(pk, nature, oldpid)))
return GetNonMatch(out pidiv);
}
pidiv = new PIDIV {NoSeed = true, RNG = RNG.LCRNG, Type = PIDType.Pokewalker};
pidiv = new PIDIV {NoSeed = true, RNG = RNGType.LCRNG, Type = PIDType.Pokewalker};
return true;
}
@ -541,7 +540,7 @@ namespace PKHeX.Core
if (!LockFinder.IsColoStarterValid(pk.Species, ref origin, pk.TID, pk.SID, pk.PID, iv1, iv2))
continue;
pidiv = new PIDIV { OriginSeed = origin, RNG = RNG.XDRNG, Type = PIDType.CXD_ColoStarter };
pidiv = new PIDIV { OriginSeed = origin, RNG = RNGType.XDRNG, Type = PIDType.CXD_ColoStarter };
return true;
}
return GetNonMatch(out pidiv);
@ -555,7 +554,7 @@ namespace PKHeX.Core
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool GetNonMatch(out PIDIV pidiv)
{
pidiv = NonMatch;
pidiv = PIDIV.None;
return false;
}
@ -622,7 +621,7 @@ namespace PKHeX.Core
private static PIDIV AnalyzeGB(PKM _)
{
// not implemented; correlation between IVs and RNG hasn't been converted to code.
return NonMatch;
return PIDIV.None;
}
private static IEnumerable<uint> GetSeedsFromPID(RNG method, uint a, uint b)
@ -760,7 +759,7 @@ namespace PKHeX.Core
var C = RNG.XDRNG.Advance(A, 7);
yield return new PIDIV { OriginSeed = RNG.XDRNG.Prev(C), RNG = RNG.XDRNG, Type = PIDType.CXD };
yield return new PIDIV { OriginSeed = RNG.XDRNG.Prev(C), RNG = RNGType.XDRNG, Type = PIDType.CXD };
}
}
@ -778,7 +777,7 @@ namespace PKHeX.Core
// check for valid encounter slot info
if (!IsPokeSpotActivation(slot, seed, out uint s))
continue;
yield return new PIDIV {OriginSeed = s, RNG = RNG.XDRNG, Type = PIDType.PokeSpot};
yield return new PIDIV {OriginSeed = s, RNG = RNGType.XDRNG, Type = PIDType.PokeSpot};
}
}

View file

@ -2,8 +2,10 @@
{
public class PIDIV
{
public static readonly PIDIV None = new PIDIV { NoSeed = true, Type = PIDType.None };
/// <summary> The RNG that generated the PKM from the <see cref="OriginSeed"/> </summary>
public RNG RNG;
public RNGType RNG;
/// <summary> The RNG seed which immediately generates the PIDIV (starting with PID or IVs, whichever comes first). </summary>
public uint OriginSeed;

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace PKHeX.Core
@ -219,4 +220,26 @@ namespace PKHeX.Core
}
}
}
public enum RNGType
{
None,
LCRNG,
XDRNG,
ARNG,
}
public static class RNGTypeUtil
{
public static RNG GetRNG(this RNGType type)
{
return type switch
{
RNGType.LCRNG => RNG.LCRNG,
RNGType.XDRNG => RNG.XDRNG,
RNGType.ARNG => RNG.ARNG,
_ => throw new ArgumentException(nameof(type))
};
}
}
}

View file

@ -1,25 +1,18 @@
using System;
using System.IO;
using System.Linq;
namespace PKHeX.Core
{
public abstract class EggMoves
{
protected int Count;
public int[] Moves;
public int FormTableIndex;
public readonly int[] Moves;
protected EggMoves(int[] moves) => Moves = moves;
public bool GetHasEggMove(int move) => Moves.Contains(move);
}
public sealed class EggMoves2 : EggMoves
{
private EggMoves2(byte[] data)
{
Count = data.Length;
Moves = data.Select(i => (int) i).ToArray();
}
private EggMoves2(byte[] data) : base(data.Select(i => (int)i).ToArray()) { }
public static EggMoves[] GetArray(byte[] data, int count)
{
@ -42,46 +35,56 @@ namespace PKHeX.Core
public sealed class EggMoves6 : EggMoves
{
private EggMoves6(byte[] data)
private static readonly EggMoves6 None = new EggMoves6(Array.Empty<int>());
private EggMoves6(int[] moves) : base(moves) { }
private static EggMoves6 Get(byte[] data)
{
if (data.Length < 2 || data.Length % 2 != 0)
{ Count = 0; Moves = Array.Empty<int>(); return; }
return None;
using BinaryReader br = new BinaryReader(new MemoryStream(data));
Moves = new int[Count = br.ReadUInt16()];
for (int i = 0; i < Count; i++)
Moves[i] = br.ReadUInt16();
int count = BitConverter.ToInt16(data, 0);
var moves = new int[count];
for (int i = 0; i < moves.Length; i++)
moves[i] = BitConverter.ToInt16(data, 2 + (i * 2));
return new EggMoves6(moves);
}
public static EggMoves[] GetArray(byte[][] entries)
public static EggMoves6[] GetArray(byte[][] entries)
{
EggMoves[] data = new EggMoves[entries.Length];
EggMoves6[] data = new EggMoves6[entries.Length];
for (int i = 0; i < data.Length; i++)
data[i] = new EggMoves6(entries[i]);
data[i] = Get(entries[i]);
return data;
}
}
public sealed class EggMoves7 : EggMoves
{
private EggMoves7(byte[] data)
private static readonly EggMoves7 None = new EggMoves7(Array.Empty<int>());
public readonly int FormTableIndex;
private EggMoves7(int[] moves, int formIndex = 0) : base(moves) => FormTableIndex = formIndex;
private static EggMoves7 Get(byte[] data)
{
if (data.Length < 2 || data.Length % 2 != 0)
{ Count = 0; Moves = Array.Empty<int>(); return; }
return None;
using var br = new BinaryReader(new MemoryStream(data));
FormTableIndex = br.ReadUInt16();
Count = br.ReadUInt16();
Moves = new int[Count];
for (int i = 0; i < Count; i++)
Moves[i] = br.ReadUInt16();
int formIndex = BitConverter.ToInt16(data, 0);
int count = BitConverter.ToInt16(data, 2);
var moves = new int[count];
for (int i = 0; i < moves.Length; i++)
moves[i] = BitConverter.ToInt16(data, 4 + (i * 2));
return new EggMoves7(moves, formIndex);
}
public static EggMoves[] GetArray(byte[][] entries)
public static EggMoves7[] GetArray(byte[][] entries)
{
EggMoves[] data = new EggMoves[entries.Length];
EggMoves7[] data = new EggMoves7[entries.Length];
for (int i = 0; i < data.Length; i++)
data[i] = new EggMoves7(entries[i]);
data[i] = Get(entries[i]);
return data;
}
}

View file

@ -10,14 +10,12 @@
{
public static int GetLocation(this ILocation encounter)
{
if (encounter == null)
return -1;
return encounter.Location != 0
? encounter.Location
: encounter.EggLocation;
}
internal static string GetEncounterLocation(this ILocation Encounter, int gen, int version = -1)
internal static string? GetEncounterLocation(this ILocation Encounter, int gen, int version = -1)
{
int loc = Encounter.GetLocation();
if (loc < 0)

View file

@ -1,4 +1,5 @@
using static PKHeX.Core.LegalityCheckStrings;
using System;
using static PKHeX.Core.LegalityCheckStrings;
namespace PKHeX.Core
{

View file

@ -109,7 +109,7 @@ namespace PKHeX.Core
case (int)Species.Spewpa:
if (pkm.AltForm > 17) // Fancy & Pokéball
return GetInvalid(LFormVivillonEventPre);
if (!Legal.CheckVivillonPattern(pkm.AltForm, pkm.Country, pkm.Region))
if (!Legal.CheckVivillonPattern(pkm.AltForm, (byte)pkm.Country, (byte)pkm.Region))
data.AddLine(Get(LFormVivillonInvalid, Severity.Fishy));
break;
case (int)Species.Vivillon:
@ -119,7 +119,7 @@ namespace PKHeX.Core
return GetInvalid(LFormVivillonInvalid);
return GetValid(LFormVivillon);
}
if (!Legal.CheckVivillonPattern(pkm.AltForm, pkm.Country, pkm.Region))
if (!Legal.CheckVivillonPattern(pkm.AltForm, (byte)pkm.Country, (byte)pkm.Region))
data.AddLine(Get(LFormVivillonInvalid, Severity.Fishy));
break;

View file

@ -47,8 +47,8 @@ namespace PKHeX.Core
private void VerifyIVsMystery(LegalityAnalysis data, MysteryGift g)
{
int[] IVs = g.IVs;
if (IVs == null)
var IVs = g.IVs;
if (IVs.Length == 0)
return;
var ivflag = Array.Find(IVs, iv => (byte)(iv - 0xFC) < 3);

View file

@ -13,6 +13,8 @@ namespace PKHeX.Core
{
protected override CheckIdentifier Identifier => CheckIdentifier.Memory;
private static readonly CheckResult NONE = new CheckResult(CheckIdentifier.Memory);
public override void Verify(LegalityAnalysis data)
{
if (data.pkm.Format < 6)
@ -161,7 +163,7 @@ namespace PKHeX.Core
private bool VerifyHistoryUntradedHandler(PKM pkm, out CheckResult result)
{
result = null;
result = NONE;
if (pkm.CurrentHandler != 0) // Badly edited; PKHeX doesn't trip this.
result = GetInvalid(LMemoryHTFlagInvalid);
else if (pkm.HT_Friendship != 0)
@ -176,7 +178,7 @@ namespace PKHeX.Core
private bool VerifyHistoryUntradedEvolution(PKM pkm, IReadOnlyList<EvoCriteria>[] chain, out CheckResult result)
{
result = null;
result = NONE;
// Handling Trainer string is empty implying it has not been traded.
// If it must be trade evolved, flag it.

View file

@ -185,8 +185,8 @@ namespace PKHeX.Core
data.AddLine(GetInvalid(LEggPP, Egg));
var EncounterMatch = data.EncounterOriginal;
var HatchCycles = (EncounterMatch as EncounterStatic)?.EggCycles;
if (HatchCycles == 0 || HatchCycles == null)
var HatchCycles = EncounterMatch is EncounterStatic s ? s.EggCycles : 0;
if (HatchCycles == 0) // no value set
HatchCycles = pkm.PersonalInfo.HatchCycles;
if (pkm.CurrentFriendship > HatchCycles)
data.AddLine(GetInvalid(LEggHatchCycles, Egg));

View file

@ -1,5 +1,6 @@
using System;
using static PKHeX.Core.LegalityCheckStrings;
using static PKHeX.Core.LanguageID;
namespace PKHeX.Core
{
@ -57,9 +58,9 @@ namespace PKHeX.Core
if (ParseSettings.CheckWordFilter && pkm.IsNicknamed)
{
if (WordFilter.IsFiltered(nickname, out string bad))
data.AddLine(GetInvalid($"Wordfilter: {bad}"));
data.AddLine(GetInvalid($"Word Filter: {bad}"));
if (TrainerNameVerifier.ContainsTooManyNumbers(nickname, data.Info.Generation))
data.AddLine(GetInvalid("Wordfilter: Too many numbers."));
data.AddLine(GetInvalid("Word Filter: Too many numbers."));
}
}
@ -82,7 +83,7 @@ namespace PKHeX.Core
}
if (nickname.Length > Legal.GetMaxLengthNickname(data.Info.Generation, (LanguageID)pkm.Language))
{
var severe = data.EncounterOriginal.EggEncounter && pkm.WasTradedEgg && nickname.Length <= Legal.GetMaxLengthNickname(data.Info.Generation, LanguageID.English)
var severe = data.EncounterOriginal.EggEncounter && pkm.WasTradedEgg && nickname.Length <= Legal.GetMaxLengthNickname(data.Info.Generation, English)
? Severity.Fishy
: Severity.Invalid;
data.AddLine(Get(LNickLengthLong, severe));
@ -105,7 +106,7 @@ namespace PKHeX.Core
return false;
}
private bool IsNicknameValid(PKM pkm, IEncounterable EncounterMatch, string nickname)
private static bool IsNicknameValid(PKM pkm, IEncounterable EncounterMatch, string nickname)
{
if (SpeciesName.GetSpeciesNameGeneration(pkm.Species, pkm.Language, pkm.Format) == nickname)
return true;
@ -205,7 +206,7 @@ namespace PKHeX.Core
}
}
private void VerifyTrade12(LegalityAnalysis data, EncounterTrade t)
private static void VerifyTrade12(LegalityAnalysis data, EncounterTrade t)
{
if (t.TID != 0) // Gen2 Trade
return; // already checked all relevant properties when fetching with getValidEncounterTradeVC2
@ -214,7 +215,7 @@ namespace PKHeX.Core
data.AddLine(GetInvalid(LEncTradeChangedOT, CheckIdentifier.Trainer));
}
private void VerifyTrade3(LegalityAnalysis data, EncounterTrade t)
private static void VerifyTrade3(LegalityAnalysis data, EncounterTrade t)
{
var pkm = data.pkm;
int lang = pkm.Language;
@ -223,7 +224,7 @@ namespace PKHeX.Core
VerifyTrade(data, t, lang);
}
private void VerifyTrade4(LegalityAnalysis data, EncounterTrade t)
private static void VerifyTrade4(LegalityAnalysis data, EncounterTrade t)
{
var pkm = data.pkm;
if (pkm.TID == 1000)
@ -249,7 +250,7 @@ namespace PKHeX.Core
if (lang == 1 && (pkm.Version == (int)GameVersion.D || pkm.Version == (int)GameVersion.P))
{
// DP English origin are Japanese lang
if (pkm.OT_Name != t.TrainerNames[1]) // not japanese
if (pkm.OT_Name != t.GetOT(1)) // not japanese
lang = 2; // English
}
break;
@ -259,9 +260,9 @@ namespace PKHeX.Core
private static void FlagKoreanIncompatibleSameGenTrade(LegalityAnalysis data, PKM pkm, int lang)
{
if (pkm.Format != 4 || lang != (int)LanguageID.Korean)
if (pkm.Format != 4 || lang != (int)Korean)
return; // transferred or not appropriate
if (ParseSettings.ActiveTrainer.Language != (int)LanguageID.Korean && ParseSettings.ActiveTrainer.Language >= 0)
if (ParseSettings.ActiveTrainer.Language != (int)Korean && ParseSettings.ActiveTrainer.Language >= 0)
data.AddLine(GetInvalid(string.Format(LTransferOriginFInvalid0_1, L_XKorean, L_XKoreanNon), CheckIdentifier.Language));
}
@ -279,49 +280,49 @@ namespace PKHeX.Core
private static int DetectTradeLanguageG3DANTAEJynx(PKM pk, int currentLanguageID)
{
if (currentLanguageID != (int)LanguageID.Italian)
if (currentLanguageID != (int)Italian)
return currentLanguageID;
if (pk.Version == (int)GameVersion.LG)
currentLanguageID = (int)LanguageID.English; // translation error; OT was not localized => same as English
currentLanguageID = (int)English; // translation error; OT was not localized => same as English
return currentLanguageID;
}
private static int DetectTradeLanguageG4MeisterMagikarp(PKM pkm, EncounterTrade t, int currentLanguageID)
{
if (currentLanguageID == (int)LanguageID.English)
return (int)LanguageID.German;
if (currentLanguageID == (int)English)
return (int)German;
// All have German, regardless of origin version.
var lang = DetectTradeLanguage(pkm.OT_Name, t, currentLanguageID);
if (lang == (int)LanguageID.English) // possible collision with FR/ES/DE. Check nickname
return pkm.Nickname == t.Nicknames[(int)LanguageID.French] ? (int)LanguageID.French : (int)LanguageID.Spanish; // Spanish is same as English
if (lang == (int)English) // possible collision with FR/ES/DE. Check nickname
return pkm.Nickname == t.Nicknames[(int)French] ? (int)French : (int)Spanish; // Spanish is same as English
return lang;
}
private static int DetectTradeLanguageG4SurgePikachu(PKM pkm, EncounterTrade t, int currentLanguageID)
{
if (currentLanguageID == (int)LanguageID.French)
return (int)LanguageID.English;
if (currentLanguageID == (int)French)
return (int)English;
// All have English, regardless of origin version.
var lang = DetectTradeLanguage(pkm.OT_Name, t, currentLanguageID);
if (lang == 2) // possible collision with ES/IT. Check nickname
return pkm.Nickname == t.Nicknames[(int)LanguageID.Italian] ? (int)LanguageID.Italian : (int)LanguageID.Spanish;
return pkm.Nickname == t.Nicknames[(int)Italian] ? (int)Italian : (int)Spanish;
return lang;
}
private void VerifyTrade5(LegalityAnalysis data, EncounterTrade t)
private static void VerifyTrade5(LegalityAnalysis data, EncounterTrade t)
{
var pkm = data.pkm;
int lang = pkm.Language;
// Trades for JPN games have language ID of 0, not 1.
if (pkm.BW)
{
if (pkm.Format == 5 && lang == (int)LanguageID.Japanese)
data.AddLine(GetInvalid(string.Format(LOTLanguage, 0, LanguageID.Japanese), CheckIdentifier.Language));
if (pkm.Format == 5 && lang == (int)Japanese)
data.AddLine(GetInvalid(string.Format(LOTLanguage, 0, Japanese), CheckIdentifier.Language));
lang = Math.Max(lang, 1);
VerifyTrade(data, t, lang);

View file

@ -58,8 +58,8 @@
ActiveTrainer = sav;
if (sav.Generation >= 3)
return AllowGBCartEra = false;
string path = sav.FileName;
bool vc = path.EndsWith("dat");
var path = sav.FileName;
bool vc = path?.EndsWith("dat") ?? false;
return AllowGBCartEra = !vc; // physical cart selected
}
}

View file

@ -14,7 +14,7 @@
public RibbonResult(string prop, bool invalid = true)
{
Name = RibbonStrings.GetName(prop) ?? prop;
Name = RibbonStrings.GetName(prop);
Invalid = invalid;
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -20,10 +21,7 @@ namespace PKHeX.Core
string[] split = line.Split('\t');
if (split.Length != 2)
continue;
if (RibbonNames.ContainsKey(split[0]))
RibbonNames[split[0]] = split[1];
else
RibbonNames.Add(split[0], split[1]);
RibbonNames[split[0]] = split[1];
}
}
@ -34,9 +32,9 @@ namespace PKHeX.Core
/// <returns>Ribbon display name</returns>
public static string GetName(string propertyName)
{
if (RibbonNames.TryGetValue(propertyName, out string value))
return value;
return null;
if (!RibbonNames.TryGetValue(propertyName, out string value))
throw new ArgumentException(propertyName);
return value;
}
}
}

View file

@ -63,8 +63,10 @@ namespace PKHeX.Core
if (encounterContent is IRibbonSetEvent4 event4)
RibbonNames = RibbonNames.Except(event4.RibbonNames());
foreach (object RibbonValue in RibbonNames.Select(RibbonName => ReflectUtil.GetValue(pkm, RibbonName)))
foreach (var RibbonValue in RibbonNames.Select(RibbonName => ReflectUtil.GetValue(pkm, RibbonName)))
{
if (RibbonValue is null)
continue;
if (HasFlag(RibbonValue) || HasCount(RibbonValue))
return true;
@ -334,7 +336,7 @@ namespace PKHeX.Core
yield break;
var names = set1.RibbonNames();
var sb = set1.RibbonBits();
var eb = (encounterContent as IRibbonSetEvent3).RibbonBits();
var eb = encounterContent is IRibbonSetEvent3 e3 ? e3.RibbonBits() : new bool[sb.Length];
if (pkm.Gen3)
{
@ -360,7 +362,7 @@ namespace PKHeX.Core
yield break;
var names = set2.RibbonNames();
var sb = set2.RibbonBits();
var eb = (encounterContent as IRibbonSetEvent4).RibbonBits();
var eb = encounterContent is IRibbonSetEvent4 e4 ? e4.RibbonBits() : new bool[sb.Length];
if (encounterContent is EncounterStatic s && s.RibbonWishing)
eb[1] = true; // require Wishing Ribbon

View file

@ -8,243 +8,140 @@ namespace PKHeX.Core
{
private class CountryTable
{
public byte CountryID;
public byte BaseForm;
public FormSubregionTable[] SubRegionForms;
public readonly byte BaseForm;
public readonly byte CountryID;
public readonly FormSubregionTable[] SubRegionForms;
internal CountryTable(byte form, byte country, params FormSubregionTable[] subs)
{
BaseForm = form;
CountryID = country;
SubRegionForms = subs;
}
}
private class FormSubregionTable
{
public byte Form;
public int[] Regions;
public readonly byte Form;
public readonly byte[] Regions;
internal FormSubregionTable(byte form, byte[] regions)
{
Form = form;
Regions = regions;
}
}
private static readonly int[][] VivillonCountryTable =
private static readonly byte[][] VivillonCountryTable =
{
//missing ID 051,068,102,127,160,186
/* 0 Icy Snow */ new[] { 018, 076, 096, 100, 107 },
/* 1 Polar */ new[] { 010, 018, 020, 049, 076, 096, 100, 107 },
/* 2 Tundra */ new[] { 001, 081, 096, },
/* 3 Continental */ new[] { 010, 067, 073, 074, 075, 077, 078, 084, 087, 094, 096, 097, 100, 107, 136},
/* 4 Garden */ new[] { 065, 082, 095, 097, 101, 110, 125},
/* 5 Elegant */ new[] { 001 },
/* 6 Meadow */ new[] { 066, 077, 078, 083, 086, 088, 105, 108, 122},
/* 7 Modern */ new[] { 018, 049},
/* 8 Marine */ new[] { 020, 064, 066, 070, 071, 073, 077, 078, 079, 080, 083, 089, 090, 091, 098, 099, 103, 105, 123, 124, 126, 184, 185},
/* 9 Archipelago */ new[] { 008, 009, 011, 012, 013, 017, 021, 023, 024, 028, 029, 032, 034, 035, 036, 037, 038, 043, 044, 045, 047, 048, 049, 052, 085, 104,},
/*10 High Plains */ new[] { 018, 036, 049, 100, 113},
/*11 Sandstorm */ new[] { 072, 109, 118, 119, 120, 121, 168, 174},
/*12 River */ new[] { 065, 069, 085, 093, 104, 105, 114, 115, 116, 117},
/*13 Monsoon */ new[] { 001, 128, 144, 169},
/*14-Savanna */ new[] { 010, 015, 016, 041, 042, 050},
/*15 Sun */ new[] { 036, 014, 019, 026, 030, 033, 036, 039, 065, 092, 106, 111, 112},
/*16 Ocean */ new[] { 049, 077},
/*17 Jungle */ new[] { 016, 021, 022, 025, 027, 031, 040, 046, 052, 169, 153, 156},
/* 0 Icy Snow */ new byte[] { 018, 076, 096, 100, 107 },
/* 1 Polar */ new byte[] { 010, 018, 020, 049, 076, 096, 100, 107 },
/* 2 Tundra */ new byte[] { 001, 081, 096, },
/* 3 Continental */ new byte[] { 010, 067, 073, 074, 075, 077, 078, 084, 087, 094, 096, 097, 100, 107, 136},
/* 4 Garden */ new byte[] { 065, 082, 095, 097, 101, 110, 125},
/* 5 Elegant */ new byte[] { 001 },
/* 6 Meadow */ new byte[] { 066, 077, 078, 083, 086, 088, 105, 108, 122},
/* 7 Modern */ new byte[] { 018, 049},
/* 8 Marine */ new byte[] { 020, 064, 066, 070, 071, 073, 077, 078, 079, 080, 083, 089, 090, 091, 098, 099, 103, 105, 123, 124, 126, 184, 185},
/* 9 Archipelago */ new byte[] { 008, 009, 011, 012, 013, 017, 021, 023, 024, 028, 029, 032, 034, 035, 036, 037, 038, 043, 044, 045, 047, 048, 049, 052, 085, 104,},
/*10 High Plains */ new byte[] { 018, 036, 049, 100, 113},
/*11 Sandstorm */ new byte[] { 072, 109, 118, 119, 120, 121, 168, 174},
/*12 River */ new byte[] { 065, 069, 085, 093, 104, 105, 114, 115, 116, 117},
/*13 Monsoon */ new byte[] { 001, 128, 144, 169},
/*14-Savanna */ new byte[] { 010, 015, 016, 041, 042, 050},
/*15 Sun */ new byte[] { 036, 014, 019, 026, 030, 033, 036, 039, 065, 092, 106, 111, 112},
/*16 Ocean */ new byte[] { 049, 077},
/*17 Jungle */ new byte[] { 016, 021, 022, 025, 027, 031, 040, 046, 052, 169, 153, 156},
};
private static readonly CountryTable[] RegionFormTable =
{
new CountryTable{
CountryID = 001, // Japan
BaseForm = 05, // Elegant
SubRegionForms = new[]
{
new FormSubregionTable { Form = 02, Regions = new[] {03,04} },
new FormSubregionTable { Form = 13, Regions = new[] {48} },
}
},
new CountryTable{
CountryID = 049, // USA
BaseForm = 07, // Modern
SubRegionForms = new[]
{
new FormSubregionTable { Form = 01, Regions = new[] {03,09,21,23,24,32,33,36,40,41,48,50} },
new FormSubregionTable { Form = 09, Regions = new[] {53} },
new FormSubregionTable { Form = 10, Regions = new[] {06,07,08,15,28,34,35,39,46,49} },
}
},
new CountryTable{
CountryID = 018, // Canada
BaseForm = 01, // Polar
SubRegionForms = new[]
{
new FormSubregionTable { Form = 00, Regions = new[] {12,13,14} },
new FormSubregionTable { Form = 07, Regions = new[] {05} },
new FormSubregionTable { Form = 10, Regions = new[] {04} },
}
},
new CountryTable{
CountryID = 016, // Brazil
BaseForm = 14, // Savanna
SubRegionForms = new[]
{
new FormSubregionTable { Form = 17, Regions = new[] {03,06} },
}
},
new CountryTable{
CountryID = 010, // Argentina
BaseForm = 14, // Savanna
SubRegionForms = new[]
{
new FormSubregionTable { Form = 01, Regions = new[] {21,24} },
new FormSubregionTable { Form = 03, Regions = new[] {16} },
}
},
new CountryTable{
CountryID = 020, // Chile
BaseForm = 08, // Marine
SubRegionForms = new[]
{
new FormSubregionTable { Form = 01, Regions = new[] {12} },
}
},
new CountryTable{
CountryID = 036, // Mexico
BaseForm = 15, // Sun
SubRegionForms = new[]
{
new FormSubregionTable { Form = 09, Regions = new[] {32} },
new FormSubregionTable { Form = 10, Regions = new[] {04,08,09,12,15,19,20,23,26,27,29} },
}
},
new CountryTable{
CountryID = 052, // Venezuela
BaseForm = 09, // Archipelago
SubRegionForms = new[]
{
new FormSubregionTable { Form = 17, Regions = new[] {17} },
}
},
new CountryTable{
CountryID = 065, // Australia
BaseForm = 09, // River
SubRegionForms = new[]
{
new FormSubregionTable { Form = 04, Regions = new[] {07} },
new FormSubregionTable { Form = 15, Regions = new[] {04} },
}
},
new CountryTable{
CountryID = 066, // Austria
BaseForm = 08, // Marine
SubRegionForms = new[]
{
new FormSubregionTable { Form = 06, Regions = new[] {10} },
}
},
new CountryTable{
CountryID = 073, // Czecg Republic
BaseForm = 08, // Marine
SubRegionForms = new[]
{
new FormSubregionTable { Form = 03, Regions = new[] {03} },
}
},
new CountryTable{
CountryID = 076, // Finland
BaseForm = 00, // Icy Snow
SubRegionForms = new[]
{
new FormSubregionTable { Form = 01, Regions = new[] {27} },
}
},
new CountryTable{
CountryID = 077, // France
BaseForm = 06, // Meadow
SubRegionForms = new[]
{
new FormSubregionTable { Form = 03, Regions = new[] {18} },
new FormSubregionTable { Form = 08, Regions = new[] {04,06,08,19} },
new FormSubregionTable { Form = 16, Regions = new[] {27} },
}
},
new CountryTable{
CountryID = 078, // Germany
BaseForm = 03, // Continental
SubRegionForms = new[]
{
new FormSubregionTable { Form = 06, Regions = new[] {04,13} },
new FormSubregionTable { Form = 08, Regions = new[] {05} },
}
},
new CountryTable{
CountryID = 078, // Italy
BaseForm = 08, // Marine
SubRegionForms = new[]
{
new FormSubregionTable { Form = 06, Regions = new[] {04,06} },
}
},
new CountryTable{
CountryID = 085, // Lesotho
BaseForm = 09, // Archipelago ??
SubRegionForms = new[]
{
new FormSubregionTable { Form = 12, Regions = new[] {04} },
}
},
new CountryTable{
CountryID = 096, // Norway
BaseForm = 03, // Continental ??
SubRegionForms = new[]
{
new FormSubregionTable { Form = 00, Regions = new[] {11} },
new FormSubregionTable { Form = 01, Regions = new[] {12,15,16,17,20,22} },
new FormSubregionTable { Form = 02, Regions = new[] {13,14} },
}
},
new CountryTable{
CountryID = 097, // Poland
BaseForm = 03, // Continental
SubRegionForms = new[]
{
new FormSubregionTable { Form = 04, Regions = new[] {11} },
}
},
new CountryTable{
CountryID = 100, // Russia
BaseForm = 01, // Polar
SubRegionForms = new[]
{
new FormSubregionTable { Form = 00, Regions = new[] {14,22,34,38,40,52,66,88} },
new FormSubregionTable { Form = 03, Regions = new[] {29,46,51,69} },
new FormSubregionTable { Form = 10, Regions = new[] {20,24,25,28,33,71,73} },
}
},
new CountryTable{
CountryID = 104, //South Africa
BaseForm = 12, // River ??
SubRegionForms = new[]
{
new FormSubregionTable { Form = 03, Regions = new[] {03,05} },
}
},
new CountryTable{
CountryID = 105, // Spain
BaseForm = 08, // Marine
SubRegionForms = new[]
{
new FormSubregionTable { Form = 06, Regions = new[] {11} },
new FormSubregionTable { Form = 12, Regions = new[] {07} },
}
},
new CountryTable{
CountryID = 107, // Sweden
BaseForm = 03, // Continental
SubRegionForms = new[]
{
new FormSubregionTable { Form = 00, Regions = new[] {11,21} },
new FormSubregionTable { Form = 01, Regions = new[] {09,13} },
}
},
new CountryTable{
CountryID = 169, // India
BaseForm = 13, // Monsoon ??
SubRegionForms = new[]
{
new FormSubregionTable { Form = 17, Regions = new[] {12} },
}
},
new CountryTable(05, 1, // Japan: Elegant
new FormSubregionTable(02, new byte[] {03,04}),
new FormSubregionTable(13, new byte[] {48})),
new CountryTable(07, 49, // USA: Modern
new FormSubregionTable(01, new byte[] {03,09,21,23,24,32,33,36,40,41,48,50}),
new FormSubregionTable(09, new byte[] {53}),
new FormSubregionTable(10, new byte[] {06,07,08,15,28,34,35,39,46,49})),
new CountryTable(01, 18, // Canada: Polar
new FormSubregionTable(00, new byte[] {12,13,14}),
new FormSubregionTable(07, new byte[] {05}),
new FormSubregionTable(10, new byte[] {04})),
new CountryTable(14, 16, // Brazil: Savanna
new FormSubregionTable(17, new byte[] {03,06})),
new CountryTable(14, 10, // Argentina: Savanna
new FormSubregionTable(01, new byte[] {21,24}),
new FormSubregionTable(03, new byte[] {16})),
new CountryTable(08, 20, // Chile: Marine
new FormSubregionTable(01, new byte[] {12})),
new CountryTable(15, 36, // Mexico: Sun
new FormSubregionTable(09, new byte[] {32}),
new FormSubregionTable(10, new byte[] {04,08,09,12,15,19,20,23,26,27,29})),
new CountryTable(09, 52, // Venezuela: Archipelago
new FormSubregionTable(17, new byte[] {17})),
new CountryTable(09, 65, // Australia: River
new FormSubregionTable(04, new byte[] {07}),
new FormSubregionTable(15, new byte[] {04})),
new CountryTable(08, 66, // Austria: Marine
new FormSubregionTable(06, new byte[] {10})),
new CountryTable(08, 73, // Czech Republic: Marine
new FormSubregionTable(03, new byte[] {03})),
new CountryTable(00, 76, // Finland: Icy Snow
new FormSubregionTable(01, new byte[] {27})),
new CountryTable(06, 77, // France: Meadow
new FormSubregionTable(03, new byte[] {18}),
new FormSubregionTable(08, new byte[] {04,06,08,19}),
new FormSubregionTable(16, new byte[] {27})),
new CountryTable(03, 078, // Germany: Continental
new FormSubregionTable(06, new byte[] {04,13}),
new FormSubregionTable(08, new byte[] {05})),
new CountryTable(08, 83, // Italy: Marine
new FormSubregionTable(06, new byte[] {04,06})),
new CountryTable(09, 85, // Lesotho: Archipelago ??
new FormSubregionTable(12, new byte[] {04})),
new CountryTable(03, 96, // Norway: Continental ??
new FormSubregionTable(00, new byte[] {11}),
new FormSubregionTable(01, new byte[] {12,15,16,17,20,22}),
new FormSubregionTable(02, new byte[] {13,14})),
new CountryTable(03, 97, // Poland: Continental
new FormSubregionTable(04, new byte[] {11})),
new CountryTable(01, 100, // Russia: Polar
new FormSubregionTable(00, new byte[] {14,22,34,38,40,52,66,88}),
new FormSubregionTable(03, new byte[] {29,46,51,69}),
new FormSubregionTable(10, new byte[] {20,24,25,28,33,71,73})),
new CountryTable(12, 104, // South Affrica: River ??
new FormSubregionTable(03, new byte[] {03,05})),
new CountryTable(08, 105, // Spain: Marine
new FormSubregionTable(06, new byte[] {11}),
new FormSubregionTable(12, new byte[] {07})),
new CountryTable(03, 107, // Sweden: Continental
new FormSubregionTable(00, new byte[] {11,21}),
new FormSubregionTable(01, new byte[] {09,13})),
new CountryTable(13, 169, // India: Monsoon ??
new FormSubregionTable(17, new byte[] {12})),
};
/// <summary>
@ -254,7 +151,7 @@ namespace PKHeX.Core
/// <param name="country">Country ID</param>
/// <param name="region">Console Region ID</param>
/// <returns></returns>
public static bool CheckVivillonPattern(int form, int country, int region)
public static bool CheckVivillonPattern(int form, byte country, byte region)
{
if (!VivillonCountryTable[form].Contains(country))
return false; // Country mismatch
@ -274,7 +171,7 @@ namespace PKHeX.Core
/// </summary>
/// <param name="country">Country ID</param>
/// <param name="region">Console Region ID</param>
public static int GetVivillonPattern(int country, int region)
public static int GetVivillonPattern(byte country, byte region)
{
var ct = Array.Find(RegionFormTable, t => t.CountryID == country);
if (ct == default(CountryTable)) // empty = no forms referenced
@ -289,7 +186,7 @@ namespace PKHeX.Core
return ct.BaseForm;
}
private static int GetVivillonPattern(int country)
private static int GetVivillonPattern(byte country)
{
var form = Array.FindIndex(VivillonCountryTable, z => z.Contains(country));
return Math.Max(0, form);

View file

@ -18,6 +18,8 @@ namespace PKHeX.Core
/// </summary>
private static readonly Dictionary<string, string> Lookup = new Dictionary<string, string>(INIT_COUNT);
private const string NoMatch = "";
/// <summary>
/// Checks to see if a phrase contains filtered content.
/// </summary>
@ -28,7 +30,7 @@ namespace PKHeX.Core
{
if (string.IsNullOrWhiteSpace(message) || message.Length <= 1)
{
regMatch = null;
regMatch = NoMatch;
return false;
}
@ -37,7 +39,7 @@ namespace PKHeX.Core
lock (dictLock)
{
if (Lookup.TryGetValue(msg, out regMatch))
return regMatch != null;
return !ReferenceEquals(regMatch, NoMatch);
}
// not in dictionary, check patterns
@ -58,7 +60,7 @@ namespace PKHeX.Core
{
if ((Lookup.Count & ~MAX_COUNT) != 0)
Lookup.Clear(); // reset
Lookup.Add(msg, regMatch = null);
Lookup.Add(msg, regMatch = NoMatch);
}
return false;
}

View file

@ -1,9 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
public abstract class DataMysteryGift : MysteryGift
{
public readonly byte[] Data;
protected DataMysteryGift(byte[] data) => Data = data;
public override int GetHashCode()
{
int hash = 17;
foreach (var b in Data)
hash = (hash * 31) + b;
return hash;
}
/// <summary>
/// Creates a deep copy of the <see cref="MysteryGift"/> object data.
/// </summary>
/// <returns></returns>
public override MysteryGift Clone()
{
byte[] data = (byte[])Data.Clone();
var result = GetMysteryGift(data);
if (result == null)
throw new ArgumentException(nameof(MysteryGift));
return result;
}
public override bool Empty => Data.IsRangeAll(0, 0, Data.Length);
}
/// <summary>
/// Mystery Gift Template File
/// </summary>
@ -16,7 +46,7 @@ namespace PKHeX.Core
/// <returns>A boolean indicating whether or not the given length is valid for a mystery gift.</returns>
public static bool IsMysteryGift(long len) => MGSizes.Contains((int)len);
private static readonly HashSet<int> MGSizes = new HashSet<int>{WC6.SizeFull, WC6.Size, PGF.Size, PGT.Size, PCD.Size };
private static readonly HashSet<int> MGSizes = new HashSet<int>{ WC6Full.Size, WC6.Size, PGF.Size, PGT.Size, PCD.Size };
/// <summary>
/// Converts the given data to a <see cref="MysteryGift"/>.
@ -25,7 +55,7 @@ namespace PKHeX.Core
/// <param name="ext">Extension of the file from which the <paramref name="data"/> was retrieved.</param>
/// <returns>An instance of <see cref="MysteryGift"/> representing the given data, or null if <paramref name="data"/> or <paramref name="ext"/> is invalid.</returns>
/// <remarks>This overload differs from <see cref="GetMysteryGift(byte[])"/> by checking the <paramref name="data"/>/<paramref name="ext"/> combo for validity. If either is invalid, a null reference is returned.</remarks>
public static MysteryGift GetMysteryGift(byte[] data, string ext)
public static DataMysteryGift? GetMysteryGift(byte[] data, string ext)
{
if (ext == null)
return GetMysteryGift(data);
@ -35,10 +65,12 @@ namespace PKHeX.Core
case WB7.SizeFull when ext == ".wb7full":
case WB7.Size when ext == ".wb7":
return new WB7(data);
case WC7.SizeFull when ext == ".wc7full":
case WC7Full.Size when ext == ".wc7full":
return new WC7Full(data).Gift;
case WC7.Size when ext == ".wc7":
return new WC7(data);
case WC6.SizeFull when ext == ".wc6full":
case WC6Full.Size when ext == ".wc6full":
return new WC6Full(data).Gift;
case WC6.Size when ext == ".wc6":
return new WC6(data);
case WR7.Size when ext == ".wr7":
@ -60,15 +92,15 @@ namespace PKHeX.Core
/// </summary>
/// <param name="data">Raw data of the mystery gift.</param>
/// <returns>An instance of <see cref="MysteryGift"/> representing the given data, or null if <paramref name="data"/> is invalid.</returns>
public static MysteryGift GetMysteryGift(byte[] data)
public static DataMysteryGift? GetMysteryGift(byte[] data)
{
switch (data.Length)
{
case WC6.SizeFull:
case WC6Full.Size:
// Check WC7 size collision
if (data[0x205] == 0) // 3 * 0x46 for gen6, now only 2.
return new WC7(data);
return new WC6(data);
return new WC7Full(data).Gift;
return new WC6Full(data).Gift;
case WC6.Size:
// Check year for WC7 size collision
if (BitConverter.ToUInt32(data, 0x4C) / 10000 < 2000)
@ -85,7 +117,6 @@ namespace PKHeX.Core
public string Extension => GetType().Name.ToLower();
public string FileName => $"{CardHeader}.{Extension}";
public byte[] Data { get; set; }
public abstract int Format { get; }
public PKM ConvertToPKM(ITrainerInfo SAV) => ConvertToPKM(SAV, EncounterCriteria.Unrestricted);
@ -107,11 +138,7 @@ namespace PKHeX.Core
/// Creates a deep copy of the <see cref="MysteryGift"/> object data.
/// </summary>
/// <returns></returns>
public MysteryGift Clone()
{
byte[] data = (byte[])Data.Clone();
return GetMysteryGift(data);
}
public abstract MysteryGift Clone();
/// <summary>
/// Gets a friendly name for the underlying <see cref="MysteryGift"/> type.
@ -139,7 +166,7 @@ namespace PKHeX.Core
public abstract bool IsPokémon { get; set; }
public virtual int Quantity { get => 1; set { } }
public virtual bool Empty => Data.All(z => z == 0);
public virtual bool Empty => false;
public virtual bool IsBP { get => false; set { } }
public virtual int BP { get => 0; set { } }
@ -148,19 +175,11 @@ namespace PKHeX.Core
public virtual int BeanCount { get => 0; set { } }
public virtual string CardHeader => (CardID > 0 ? $"Card #: {CardID:0000}" : "N/A") + $" - {CardTitle.Replace('\u3000',' ').Trim()}";
public override int GetHashCode()
{
int hash = 17;
foreach (var b in Data)
hash = (hash * 31) + b;
return hash;
}
// Search Properties
public virtual int[] Moves { get => Array.Empty<int>(); set { } }
public virtual int[] RelearnMoves { get => Array.Empty<int>(); set { } }
public virtual int[] IVs { get => null; set { } }
public virtual int[] IVs { get => Array.Empty<int>(); set { } }
public virtual bool IsShiny => false;
public virtual bool IsEgg { get => false; set { } }
public virtual int HeldItem { get => -1; set { } }

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