2017-07-25 07:28:43 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
|
|
namespace PKHeX.Core
|
|
|
|
|
{
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Miscellaneous setup utility for legality checking <see cref="IEncounterable"/> data sources.
|
|
|
|
|
/// </summary>
|
2017-07-25 07:28:43 +00:00
|
|
|
|
internal static class EncounterUtil
|
|
|
|
|
{
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the relevant <see cref="EncounterStatic"/> objects that appear in the relevant game.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="source">Table of valid encounters that appear for the game pairing</param>
|
|
|
|
|
/// <param name="game">Game to filter for</param>
|
|
|
|
|
/// <returns>Array of encounter objects that are encounterable on the input game</returns>
|
|
|
|
|
internal static EncounterStatic[] GetStaticEncounters(IEnumerable<EncounterStatic> source, GameVersion game)
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
2017-07-30 19:31:17 +00:00
|
|
|
|
return source.Where(s => s.Version.Contains(game)).ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the <see cref="EncounterArea"/> data for the input game via the program's resource streams.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="game">Game to fetch for</param>
|
|
|
|
|
/// <remarks> <see cref="EncounterSlot.SlotNumber"/> data is not marked, as the RNG seed is 64 bits (permitting sufficient randomness).</remarks>
|
|
|
|
|
/// <returns>Array of areas that are encounterable on the input game.</returns>
|
|
|
|
|
internal static EncounterArea[] GetEncounterTables(GameVersion game)
|
|
|
|
|
{
|
|
|
|
|
switch (game)
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
|
|
|
|
case GameVersion.B: return GetEncounterTables("51", "b");
|
|
|
|
|
case GameVersion.W: return GetEncounterTables("51", "w");
|
|
|
|
|
case GameVersion.B2: return GetEncounterTables("52", "b2");
|
|
|
|
|
case GameVersion.W2: return GetEncounterTables("52", "w2");
|
|
|
|
|
case GameVersion.X: return GetEncounterTables("xy", "x");
|
|
|
|
|
case GameVersion.Y: return GetEncounterTables("xy", "y");
|
|
|
|
|
case GameVersion.AS: return GetEncounterTables("ao", "a");
|
|
|
|
|
case GameVersion.OR: return GetEncounterTables("ao", "o");
|
|
|
|
|
case GameVersion.SN: return GetEncounterTables("sm", "sn");
|
|
|
|
|
case GameVersion.MN: return GetEncounterTables("sm", "mn");
|
2017-09-01 05:37:28 +00:00
|
|
|
|
case GameVersion.US: return GetEncounterTables("uu", "us");
|
|
|
|
|
case GameVersion.UM: return GetEncounterTables("uu", "um");
|
2017-07-25 07:28:43 +00:00
|
|
|
|
}
|
|
|
|
|
return null; // bad request
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Direct fetch for <see cref="EncounterArea"/> data; can also be used to fetch supplementary encounter streams.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="ident">Unpacking identification ASCII characters (first two bytes of binary)</param>
|
|
|
|
|
/// <param name="resource">Resource name (will be prefixed with "encounter_"</param>
|
|
|
|
|
/// <returns>Array of encounter areas</returns>
|
2017-07-25 07:28:43 +00:00
|
|
|
|
internal static EncounterArea[] GetEncounterTables(string ident, string resource)
|
|
|
|
|
{
|
|
|
|
|
byte[] mini = Util.GetBinaryResource($"encounter_{resource}.pkl");
|
|
|
|
|
return EncounterArea.GetArray(Data.UnpackMini(mini, ident));
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Combines <see cref="EncounterArea"/> slot arrays with the same <see cref="EncounterArea.Location"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="tables">Input encounter areas to combine</param>
|
|
|
|
|
/// <returns>Combined Array of encounter areas. No duplicate location IDs will be present.</returns>
|
2017-07-25 07:28:43 +00:00
|
|
|
|
internal static EncounterArea[] AddExtraTableSlots(params EncounterArea[][] tables)
|
|
|
|
|
{
|
|
|
|
|
return tables.SelectMany(s => s).GroupBy(l => l.Location)
|
|
|
|
|
.Select(t => t.Count() == 1
|
|
|
|
|
? t.First() // only one table, just return the area
|
|
|
|
|
: new EncounterArea { Location = t.First().Location, Slots = t.SelectMany(s => s.Slots).ToArray() })
|
|
|
|
|
.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Marks Encounter Slots for party lead's ability slot influencing.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>Magnet Pull attracts Steel type slots, and Static attracts Electric</remarks>
|
|
|
|
|
/// <param name="Areas">Encounter Area array for game</param>
|
|
|
|
|
/// <param name="t">Personal data for use with a given species' type</param>
|
2017-12-03 20:20:49 +00:00
|
|
|
|
internal static void MarkEncountersStaticMagnetPull(IEnumerable<EncounterArea> Areas, PersonalTable t)
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (EncounterArea Area in Areas)
|
2017-12-01 06:34:57 +00:00
|
|
|
|
foreach (var grp in Area.Slots.GroupBy(z => z.Type))
|
2017-12-03 20:20:49 +00:00
|
|
|
|
MarkEncountersStaticMagnetPull(grp, t);
|
|
|
|
|
}
|
|
|
|
|
internal static void MarkEncountersStaticMagnetPull(IEnumerable<EncounterSlot> grp, PersonalTable t)
|
|
|
|
|
{
|
|
|
|
|
GetStaticMagnet(t, grp, out List<EncounterSlot> s, out List<EncounterSlot> m);
|
|
|
|
|
for (var i = 0; i < s.Count; i++)
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
2017-12-03 20:20:49 +00:00
|
|
|
|
var slot = s[i];
|
|
|
|
|
slot.Permissions.StaticIndex = i;
|
|
|
|
|
slot.Permissions.StaticCount = s.Count;
|
|
|
|
|
}
|
|
|
|
|
for (var i = 0; i < m.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var slot = m[i];
|
|
|
|
|
slot.Permissions.MagnetPullIndex = i;
|
2017-12-14 00:17:02 +00:00
|
|
|
|
slot.Permissions.MagnetPullCount = m.Count;
|
2017-12-03 20:20:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
internal static void MarkEncountersStaticMagnetPullPermutation(IEnumerable<EncounterSlot> grp, PersonalTable t, List<EncounterSlot> permuted)
|
|
|
|
|
{
|
|
|
|
|
GetStaticMagnet(t, grp, out List<EncounterSlot> s, out List<EncounterSlot> m);
|
|
|
|
|
|
|
|
|
|
// Apply static/magnet values; if any permutation has a unique slot combination, add it to the slot list.
|
|
|
|
|
for (int i = 0; i < s.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var slot = s[i];
|
|
|
|
|
if (slot.Permissions.StaticIndex >= 0) // already has unique data
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
2017-12-03 20:20:49 +00:00
|
|
|
|
if (slot.IsMatchStatic(i, s.Count))
|
|
|
|
|
continue; // same values, no permutation
|
|
|
|
|
if (permuted.Any(z => z.SlotNumber == slot.SlotNumber && z.IsMatchStatic(i, s.Count) && z.Species == slot.Species))
|
|
|
|
|
continue; // same values, previously permuted
|
|
|
|
|
|
|
|
|
|
s[i] = slot = slot.Clone();
|
|
|
|
|
permuted.Add(slot);
|
2017-07-25 07:28:43 +00:00
|
|
|
|
}
|
2017-12-03 20:20:49 +00:00
|
|
|
|
slot.Permissions.StaticIndex = i;
|
|
|
|
|
slot.Permissions.StaticCount = s.Count;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < m.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var slot = m[i];
|
|
|
|
|
if (slot.Permissions.MagnetPullIndex >= 0) // already has unique data
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
2017-12-03 20:20:49 +00:00
|
|
|
|
if (slot.IsMatchStatic(i, m.Count))
|
|
|
|
|
continue; // same values, no permutation
|
|
|
|
|
if (permuted.Any(z => z.SlotNumber == slot.SlotNumber && z.IsMatchMagnet(i, m.Count) && z.Species == slot.Species))
|
|
|
|
|
continue; // same values, previously permuted
|
|
|
|
|
|
|
|
|
|
m[i] = slot = slot.Clone();
|
|
|
|
|
permuted.Add(slot);
|
2017-07-25 07:28:43 +00:00
|
|
|
|
}
|
2017-12-03 20:20:49 +00:00
|
|
|
|
slot.Permissions.MagnetPullIndex = i;
|
|
|
|
|
slot.Permissions.MagnetPullCount = m.Count;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private static void GetStaticMagnet(PersonalTable t, IEnumerable<EncounterSlot> grp, out List<EncounterSlot> s, out List<EncounterSlot> m)
|
|
|
|
|
{
|
|
|
|
|
const int steel = (int)MoveType.Steel;
|
|
|
|
|
const int electric = (int)MoveType.Electric + 1; // offset by 1 in gen3/4 for the ??? type
|
|
|
|
|
s = new List<EncounterSlot>();
|
|
|
|
|
m = new List<EncounterSlot>();
|
|
|
|
|
foreach (EncounterSlot Slot in grp)
|
|
|
|
|
{
|
|
|
|
|
var types = t[Slot.Species].Types;
|
|
|
|
|
if (types[0] == steel || types[1] == steel)
|
|
|
|
|
m.Add(Slot);
|
|
|
|
|
if (types[0] == electric || types[1] == electric)
|
|
|
|
|
s.Add(Slot);
|
2017-07-25 07:28:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sets the <see cref="EncounterStatic.Generation"/> value, for use in determining split-generation origins.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>Only used for Gen 1 & 2, as <see cref="PKM.Version"/> data is not present.</remarks>
|
|
|
|
|
/// <param name="Encounters">Ingame encounter data</param>
|
|
|
|
|
/// <param name="Generation">Generation number to set</param>
|
|
|
|
|
internal static void MarkEncountersGeneration(IEnumerable<EncounterStatic> Encounters, int Generation)
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (EncounterStatic Encounter in Encounters)
|
|
|
|
|
Encounter.Generation = Generation;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sets the <see cref="EncounterSlot1.Version"/> value, for use in determining split-generation origins.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>Only used for Gen 1 & 2, as <see cref="PKM.Version"/> data is not present.</remarks>
|
|
|
|
|
/// <param name="Areas">Ingame encounter data</param>
|
|
|
|
|
/// <param name="Version">Version ID to set</param>
|
|
|
|
|
internal static void MarkEncountersVersion(IEnumerable<EncounterArea> Areas, GameVersion Version)
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
|
|
|
|
foreach (EncounterArea Area in Areas)
|
|
|
|
|
foreach (var Slot in Area.Slots.OfType<EncounterSlot1>())
|
|
|
|
|
Slot.Version = Version;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sets the <see cref="EncounterStatic.Generation"/> value, for use in determining split-generation origins.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>Only used for Gen 1 & 2, as <see cref="PKM.Version"/> data is not present.</remarks>
|
|
|
|
|
/// <param name="Areas">Ingame encounter data</param>
|
|
|
|
|
/// <param name="Generation">Generation number to set</param>
|
2017-07-25 07:28:43 +00:00
|
|
|
|
internal static void MarkEncountersGeneration(IEnumerable<EncounterArea> Areas, int Generation)
|
|
|
|
|
{
|
|
|
|
|
foreach (EncounterArea Area in Areas)
|
|
|
|
|
foreach (EncounterSlot Slot in Area.Slots)
|
|
|
|
|
Slot.Generation = Generation;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-30 19:31:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Groups areas by location id, raw data has areas with different slots but the same location id.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>Similar to <see cref="AddExtraTableSlots"/>, this method combines a single array.</remarks>
|
|
|
|
|
/// <param name="Areas">Ingame encounter data</param>
|
2017-07-25 07:28:43 +00:00
|
|
|
|
internal static void ReduceAreasSize(ref EncounterArea[] Areas)
|
|
|
|
|
{
|
|
|
|
|
Areas = Areas.GroupBy(a => a.Location).Select(a => new EncounterArea
|
|
|
|
|
{
|
|
|
|
|
Location = a.First().Location,
|
|
|
|
|
Slots = a.SelectMany(m => m.Slots).ToArray()
|
|
|
|
|
}).ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-26 02:16:50 +00:00
|
|
|
|
internal static T[] ConcatAll<T>(params T[][] arr) => arr.SelectMany(z => z).ToArray();
|
|
|
|
|
|
|
|
|
|
internal static void MarkEncounterAreaArray(params EncounterArea[][] areas)
|
2017-07-25 07:28:43 +00:00
|
|
|
|
{
|
2017-11-26 02:16:50 +00:00
|
|
|
|
foreach (var area in areas)
|
|
|
|
|
MarkEncounterAreas(area);
|
|
|
|
|
}
|
|
|
|
|
internal static void MarkEncounterAreas(params EncounterArea[] areas)
|
|
|
|
|
{
|
|
|
|
|
foreach (var area in areas)
|
|
|
|
|
foreach (var slot in area.Slots)
|
|
|
|
|
slot.Area = area;
|
2017-07-25 07:28:43 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|