Offload EncounterSlot loading logic to reduce complexity (#2980)

* Rework gen1 slot loading

Slot templates are precomputed from ROM data and just loaded straight in, with tight coupling to the encounter area (grouped by slot types).

* Revise fuzzy met check for underleveled wild evos

Example: Level 23 poliwhirl in RBY as a level 50 poliwhirl, will assume the chain is 25-50 for poliwhirl (as poliwag evolves at 25). Instead of revising the origin chain, just ignore the evo min level in the comparison.

Previous commit fixed it for gen1.

* Rework gen2-4 slot loading

Gen4 not finished, Type Encounter data and some edge encounters not recognizing yet...

* Add feebas slots for old/good encounters

* Begin moving properties

Great news! Gen5-7 need to be de-dumbed like Gen1-4.

Then I can remove the bang (!) on the Area accessor and ensure that it's never null!

* Split off XD pokespot slot encounter table type

* Set area in constructor

* Deduplicate g3 roaming encounters

* Deduplicate xd encounter locations (rebattle)

Only difference is met location; no need to create 500 extra encounter objects. A simple contains check is ok (rarely in gen3 format).

* Make all slots have a readonly reference to their parent area

* Minor clean

* Remove "Safari" slot type flag

Can be determined via other means (generation-location), allows us to reduce the size of SlotType member to a byte

Output of slot binaries didn't preserve the Safari flag anyway.

* Update SlotType.cs

* Handle type encounters correctly

* Merge safari area into regular xy area

* Merge dexnav accessor logic

* fix some logic so that tests pass again

rearrange g5 dw init to be done outside of static constructor (initializer instead)
PIDGenerator: friend safari slots now generate with required flawless IV count

* Add cianwood tentacool gift encounter

* Remove unnecessary abstractions

Fake area just returned a slot; since Slots have a non-null reference to the area, we can just return the slot and use the API to grab a list of possible slots for the chain.

Increase restrictiveness of location/type get-set operations

* Minor tweaks, pass parameters

DexNav observed state isn't necessary to use, only need to see if it's possible to dexnav. Now that we have metadata for slots, we can.

* Remove unused legality tables
This commit is contained in:
Kurt 2020-08-30 10:23:22 -07:00 committed by GitHub
parent c1bc371d76
commit 6ee7a8724b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
104 changed files with 1082 additions and 3827 deletions

View file

@ -40,7 +40,7 @@ namespace PKHeX.Core
Beast = 26,
}
public static partial class Extensions
public static class BallExtensions
{
/// <summary>
/// Checks if the <see cref="ball"/> is an Apricorn Ball (HG/SS)
@ -48,5 +48,23 @@ namespace PKHeX.Core
/// <param name="ball">Ball ID</param>
/// <returns>True if Apricorn, false if not.</returns>
public static bool IsApricornBall(this Ball ball) => Ball.Fast <= ball && ball <= Ball.Moon;
public static Ball GetRequiredBallValueWild(int gen, int loc)
{
return gen switch
{
// For Gen3 Safari Zones, we've already deferred partial match encounters.
3 when Locations.IsSafariZoneLocation3(loc) => Ball.Safari,
// For Gen4 Safari Zones and BCC, we've already deferred partial match encounters.
4 when Locations.IsSafariZoneLocation4(loc) => Ball.Safari,
4 when Locations.BugCatchingContest4 == loc => Ball.Sport,
// Poké Pelago
7 when loc == 30016 => Ball.Poke,
_ => Ball.None,
};
}
}
}

View file

@ -6,71 +6,19 @@ namespace PKHeX.Core
/// <summary>
/// Represents an Area where <see cref="PKM"/> can be encountered, which contains a Location ID and <see cref="EncounterSlot"/> data.
/// </summary>
public class EncounterArea
public abstract class EncounterArea
{
public int Location;
public int Location { get; protected set; }
public SlotType Type { get; protected set; } = SlotType.Any;
public EncounterSlot[] Slots = Array.Empty<EncounterSlot>();
/// <summary>
/// Gets the encounter areas for species with same level range and same slot type at same location
/// </summary>
/// <param name="species">List of species that exist in the Area.</param>
/// <param name="lvls">Paired min and max levels of the encounter slots.</param>
/// <param name="location">Location index of the encounter area.</param>
/// <param name="t">Encounter slot type of the encounter area.</param>
/// <returns>Encounter area with slots</returns>
public static TArea[] GetSimpleEncounterArea<TArea, TSlot>(int[] species, int[] lvls, int location, SlotType t)
where TArea : EncounterArea, new()
where TSlot : EncounterSlot, new()
{
if ((lvls.Length & 1) != 0) // levels data not paired; expect multiple of 2
throw new ArgumentException(nameof(lvls));
var count = species.Length * (lvls.Length / 2);
var slots = new TSlot[count];
int ctr = 0;
foreach (var s in species)
{
for (int i = 0; i < lvls.Length;)
{
slots[ctr++] = new TSlot
{
LevelMin = lvls[i++],
LevelMax = lvls[i++],
Species = s,
Type = t
};
}
}
return new[] { new TArea { Location = location, Slots = slots } };
}
/// <summary>
/// Gets the slots contained in the area that match the provided data.
/// </summary>
/// <param name="pkm">Pokémon Data</param>
/// <param name="chain">Evolution lineage</param>
/// <returns>Enumerable list of encounters</returns>
public virtual IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
foreach (var slot in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (!slot.IsLevelWithinRange(pkm.Met_Level))
break;
if (slot.Form != evo.Form && !Legal.WildChangeFormAfter.Contains(slot.Species))
break;
yield return slot;
break;
}
}
}
public abstract IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain);
/// <summary>
/// Checks if the provided met location ID matches the parameters for the area.

View file

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -10,100 +8,35 @@ namespace PKHeX.Core
/// </summary>
public sealed class EncounterArea1 : EncounterArea
{
private static EncounterSlot1[] ReadSlots1FishingYellow(byte[] data, ref int ofs, int count, SlotType t, int rate)
{
// Convert byte to actual number
byte[] levels = { 0xFF, 0x15, 0x67, 0x1D, 0x3B, 0x5C, 0x72, 0x16, 0x71, 0x18, 0x00, 0x6D, 0x80, };
byte[] g1DexIDs = { 0x47, 0x6E, 0x18, 0x9B, 0x17, 0x4E, 0x8A, 0x5C, 0x5D, 0x9D, 0x9E, 0x1B, 0x85, 0x16, 0x58, 0x59, };
int[] speciesIDs = { 060, 061, 072, 073, 090, 098, 099, 116, 117, 118, 119, 120, 129, 130, 147, 148, };
public readonly int Rate;
public static EncounterArea1[] GetAreas(byte[][] input, GameVersion game)
{
var result = new EncounterArea1[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea1(input[i], game);
return result;
}
private EncounterArea1(byte[] data, GameVersion game)
{
Location = data[0];
// 1 byte unused
Type = (SlotType)data[2];
Rate = data[3];
int count = (data.Length - 4) / 4;
var slots = new EncounterSlot1[count];
for (int slot = 0; slot < count; slot++)
for (int i = 0; i < slots.Length; i++)
{
int species = speciesIDs[Array.IndexOf(g1DexIDs, data[ofs++])];
int lvl = Array.IndexOf(levels, data[ofs++]) * 5;
slots[slot] = new EncounterSlot1(species, lvl, lvl, rate, t, slot);
int offset = 4 + (4 * i);
int species = data[offset + 0];
int slotNum = data[offset + 1];
int min = data[offset + 2];
int max = data[offset + 3];
slots[i] = new EncounterSlot1(this, species, min, max, slotNum, game);
}
return slots;
}
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot"/> information from Generation 1 Grass/Water data.
/// </summary>
/// <param name="data">Input raw data.</param>
/// <param name="count">Count of areas in the binary.</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea1[] GetArray1GrassWater(byte[] data, int count)
{
EncounterArea1[] areas = new EncounterArea1[count];
for (int i = 0; i < areas.Length; i++)
{
int ptr = BitConverter.ToInt16(data, i * 2);
var grass = GetSlots1GrassWater(data, ref ptr, SlotType.Grass);
var water = GetSlots1GrassWater(data, ref ptr, SlotType.Surf);
areas[i] = new EncounterArea1
{
Location = i,
Slots = ArrayUtil.ConcatAll(grass, water),
};
}
return areas.Where(area => area.Slots.Length != 0).ToArray();
}
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot"/> information from Pokémon Yellow (Generation 1) Fishing data.
/// </summary>
/// <param name="data">Input raw data.</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea1[] GetArray1FishingYellow(byte[] data)
{
const int size = 9;
int count = data.Length / size;
EncounterArea1[] areas = new EncounterArea1[count];
for (int i = 0; i < count; i++)
{
int ofs = (i * size) + 1;
areas[i] = new EncounterArea1
{
Location = data[(i * size) + 0],
Slots = ReadSlots1FishingYellow(data, ref ofs, 4, SlotType.Super_Rod, -1)
};
}
return areas;
}
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot"/> information from Generation 1 Fishing data.
/// </summary>
/// <param name="data">Input raw data.</param>
/// <param name="count">Count of areas in the binary.</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea1[] GetArray1Fishing(byte[] data, int count)
{
EncounterArea1[] areas = new EncounterArea1[count];
for (int i = 0; i < areas.Length; i++)
{
int loc = data[(i * 3) + 0];
int ptr = BitConverter.ToInt16(data, (i * 3) + 1);
areas[i] = new EncounterArea1
{
Location = loc,
Slots = GetSlots1Fishing(data, ptr)
};
}
return areas;
}
private static EncounterSlot1[] GetSlots1GrassWater(byte[] data, ref int ofs, SlotType t)
{
int rate = data[ofs++];
return rate == 0 ? Array.Empty<EncounterSlot1>() : EncounterSlot1.ReadSlots(data, ref ofs, 10, t, rate);
}
private static EncounterSlot1[] GetSlots1Fishing(byte[] data, int ofs)
{
int count = data[ofs++];
return EncounterSlot1.ReadSlots(data, ref ofs, count, SlotType.Super_Rod, -1);
Slots = slots;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
@ -116,7 +49,7 @@ namespace PKHeX.Core
if (slot.Species != evo.Species)
continue;
if (!slot.IsLevelWithinRange(evo.MinLevel, evo.Level))
if (slot.LevelMin > evo.Level)
break;
if (slot.Form != evo.Form)
break;

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
@ -10,274 +9,68 @@ namespace PKHeX.Core
/// </summary>
public sealed class EncounterArea2 : EncounterArea
{
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot2"/> information from Generation 2 Grass/Water data.
/// </summary>
/// <param name="data">Input raw data.</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea2[] GetArray2GrassWater(byte[] data)
private static readonly byte[] BCC_SlotRates = { 20, 20, 10, 10, 05, 05, 10, 10, 05, 05 };
private static readonly byte[] RatesGrass = { 30, 30, 20, 10, 5, 4, 1 };
private static readonly byte[] RatesSurf = { 60, 30, 10 };
internal readonly EncounterTime Time;
public readonly int Rate;
public readonly IReadOnlyList<byte> Rates;
public static EncounterArea2[] GetAreas(byte[][] input, GameVersion game)
{
int ofs = 0;
var areas = new List<EncounterArea2>();
areas.AddRange(GetAreas2(data, ref ofs, SlotType.Grass, 3, 7)); // Johto Grass
areas.AddRange(GetAreas2(data, ref ofs, SlotType.Surf, 1, 3)); // Johto Water
areas.AddRange(GetAreas2(data, ref ofs, SlotType.Grass, 3, 7)); // Kanto Grass
areas.AddRange(GetAreas2(data, ref ofs, SlotType.Surf, 1, 3)); // Kanto Water
areas.AddRange(GetAreas2(data, ref ofs, SlotType.Swarm, 3, 7)); // Swarm
areas.AddRange(GetAreas2(data, ref ofs, SlotType.Special, 1, 3)); // Union Cave
return areas.ToArray();
var result = new EncounterArea2[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea2(input[i], game);
return result;
}
// Fishing Tables are not associated to a single map; a map picks a table to use.
// For all maps that use a table, create a new EncounterArea with reference to the table's slots.
private static readonly sbyte[] convMapIDtoFishLocationID =
private EncounterArea2(byte[] data, GameVersion game)
{
-1, 1, -1, 0, 3, 3, 3, -1, 10, 3, 2, -1, -1, 2, 3, 0,
-1, -1, 3, -1, -1, -1, 3, -1, -1, -1, -1, 0, -1, -1, 0, 9,
1, 0, 2, 2, -1, 3, 7, 3, -1, 3, 4, 8, 2, -1, 2, 1,
-1, 3, -1, -1, -1, -1, -1, 0, 2, 2, -1, -1, 3, 1, -1, -1,
-1, 2, -1, 2, -1, -1, -1, -1, -1, -1, 10, 10, -1, -1, -1, -1,
-1, 7, 0, 1, -1, 1, 1, 3, -1, -1, -1, 1, 1, 2, 3, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
Location = data[0];
Time = (EncounterTime)data[1];
var type = Type = (SlotType)data[2];
var rate = data[3];
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot2"/> information from Generation 2 Grass/Water data.
/// </summary>
/// <param name="data">Input raw data.</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea2[] GetArray2Fishing(byte[] data)
{
int ofs = 0;
var f = GetAreas2Fishing(data, ref ofs);
var areas = new List<EncounterArea2>();
for (int i = 0; i < convMapIDtoFishLocationID.Length; i++)
if (type > SlotType.Surf) // Not Grass/Surf
{
var loc = convMapIDtoFishLocationID[i];
if (loc == -1) // no table for map
continue;
areas.Add(new EncounterArea2 { Location = i, Slots = f[loc].Slots });
const int size = 5;
int count = (data.Length - 4) / size;
var rates = new byte[count];
for (int i = 0; i < rates.Length; i++)
rates[i] = data[4 + i];
Rates = rates;
Slots = ReadSlots(data, count, 4 + count, game);
}
// Some maps have two tables. Fortunately, there's only two. Add the second table.
areas.Add(new EncounterArea2 { Location = 0x1B, Slots = f[1].Slots }); // Olivine City (0: Harbor, 1: City)
areas.Add(new EncounterArea2 { Location = 0x2E, Slots = f[3].Slots }); // Silver Cave (2: Inside, 3: Outside)
return areas.ToArray();
}
public static EncounterArea2[] GetArray2Headbutt(byte[] data)
{
int ofs = 0;
return GetAreas2Headbutt(data, ref ofs).ToArray();
}
private static EncounterSlot2[] GetSlots2GrassWater(byte[] data, ref int ofs, SlotType t, int slotSets, int slotCount)
{
byte[] rates = new byte[slotSets];
for (int i = 0; i < rates.Length; i++)
rates[i] = data[ofs++];
var slots = EncounterSlot2.ReadSlots(data, ref ofs, slotSets * slotCount, t, rates[0]);
if (slotSets <= 1)
return slots;
for (int i = 0; i < slotCount; i++)
else
{
slots[i].Time = EncounterTime.Morning;
}
for (int r = 1; r < slotSets; r++)
{
for (int i = 0; i < slotCount; i++)
{
int index = i + (r * slotCount);
slots[index].Rate = rates[r];
slots[index].SlotNumber = i;
slots[index].Time = r == 1 ? EncounterTime.Day : EncounterTime.Night;
}
}
Rate = rate;
return slots;
const int size = 4;
int count = (data.Length - 4) / size;
Rates = type == SlotType.BugContest ? BCC_SlotRates : (type == SlotType.Grass) ? RatesGrass : RatesSurf;
Slots = ReadSlots(data, count, 4, game);
}
}
private static EncounterSlot2[] GetSlots2Fishing(byte[] data, ref int ofs, SlotType t)
private EncounterSlot2[] ReadSlots(byte[] data, int count, int start, GameVersion game)
{
// slot set ends with final slot having 0xFF 0x** 0x**
const int size = 3;
int end = ofs; // scan for count
while (data[end] != 0xFF)
end += size;
var count = ((end - ofs) / size) + 1;
var slots = new EncounterSlot2[count];
for (int i = 0; i < slots.Length; i++)
{
int rate = data[ofs++];
int species = data[ofs++];
int level = data[ofs++];
var type = species == 0 ? SlotType.Special : t; // day/night specific;
slots[i] = new EncounterSlot2(species, level, level, rate, type, i);
int offset = start + (4 * i);
int species = data[offset + 0];
int slotNum = data[offset + 1];
int min = data[offset + 2];
int max = data[offset + 3];
slots[i] = new EncounterSlot2(this, species, min, max, slotNum, game);
}
return slots;
}
private static EncounterSlot2[] GetSlots2Headbutt(byte[] data, ref int ofs, SlotType t)
{
// slot set ends in 0xFF
var slots = new List<EncounterSlot2>();
int tableCount = t == SlotType.Headbutt ? 2 : 1;
SlotType slottype = t;
int slot = 0;
while (tableCount != 0)
{
if (t == SlotType.Headbutt)
slottype = tableCount == 2 ? SlotType.Headbutt_Special : SlotType.Headbutt;
int rate = data[ofs++];
if (rate == 0xFF) // end of table
{
tableCount--;
continue;
}
int species = data[ofs++];
int level = data[ofs++];
slots.Add(new EncounterSlot2(species, level, level, rate, slottype, slot++));
}
return slots.ToArray();
}
private static IEnumerable<EncounterArea2> GetAreas2(byte[] data, ref int ofs, SlotType t, int slotSets, int slotCount)
{
var areas = new List<EncounterArea2>();
while (data[ofs] != 0xFF) // end
{
var location = data[ofs++] << 8 | data[ofs++];
var slots = GetSlots2GrassWater(data, ref ofs, t, slotSets, slotCount);
var area = new EncounterArea2
{
Location = location,
Slots = slots,
};
foreach (var slot in slots)
slot.Area = area;
areas.Add(area);
}
ofs++;
return areas;
}
private static List<EncounterArea2> GetAreas2Fishing(byte[] data, ref int ofs)
{
var areas = new List<EncounterArea2>();
while (ofs != 0x18C)
{
var old = GetSlots2Fishing(data, ref ofs, SlotType.Old_Rod);
var good = GetSlots2Fishing(data, ref ofs, SlotType.Good_Rod);
var super = GetSlots2Fishing(data, ref ofs, SlotType.Super_Rod);
areas.Add(new EncounterArea2
{
Slots = ArrayUtil.ConcatAll(old, good, super),
});
}
// Read TimeFishGroups
var dl = new List<SlotTemplate>();
while (ofs < data.Length)
dl.Add(new SlotTemplate(data[ofs++], data[ofs++]));
// Add TimeSlots
foreach (var area in areas)
{
var slots = area.Slots;
for (int i = 0; i < slots.Length; i++)
{
var slot = slots[i];
if (slot.Type != SlotType.Special)
continue;
Array.Resize(ref slots, slots.Length + 1);
Array.Copy(slots, i, slots, i + 1, slots.Length - i - 1); // shift slots down
slots[i + 1] = slot.Clone(); // differentiate copied slot
int index = slot.LevelMin * 2;
for (int j = 0; j < 2; j++) // load special slot info
{
var s = (EncounterSlot2)slots[i + j];
s.Species = dl[index + j].Species;
s.LevelMin = s.LevelMax = dl[index + j].Level;
s.Type = slots[i - 1].Type; // special slots are never first in a set, so copy previous type
s.Time = j == 0 ? EncounterTime.Morning | EncounterTime.Day : EncounterTime.Night;
}
}
area.Slots = slots;
}
return areas;
}
private readonly struct SlotTemplate
{
public readonly byte Species;
public readonly byte Level;
public SlotTemplate(byte species, byte level)
{
Species = species;
Level = level;
}
}
private static IEnumerable<EncounterArea2> GetAreas2Headbutt(byte[] data, ref int ofs)
{
// Read Location Table
var head = new List<EncounterArea2>();
var headID = new List<int>();
while (data[ofs] != 0xFF)
{
head.Add(new EncounterArea2
{
Location = (data[ofs++] << 8) | data[ofs++],
//Slots = null, // later
});
headID.Add(data[ofs++]);
}
ofs++;
var rock = new List<EncounterArea2>();
var rockID = new List<int>();
while (data[ofs] != 0xFF)
{
rock.Add(new EncounterArea2
{
Location = (data[ofs++] << 8) | data[ofs++],
//Slots = null, // later
});
rockID.Add(data[ofs++]);
}
ofs++;
ofs += 0x16; // jump over GetTreeMons
// Read ptr table
int[] ptr = new int[data.Length == 0x109 ? 6 : 9]; // GS : C
for (int i = 0; i < ptr.Length; i++)
ptr[i] = data[ofs++] | (data[ofs++] << 8);
int baseOffset = ptr.Min() - ofs;
// Read Tables
for (int i = 0; i < head.Count; i++)
{
int o = ptr[headID[i]] - baseOffset;
head[i].Slots = GetSlots2Headbutt(data, ref o, SlotType.Headbutt);
}
for (int i = 0; i < rock.Count; i++)
{
int o = ptr[rockID[i]] - baseOffset;
rock[i].Slots = GetSlots2Headbutt(data, ref o, SlotType.Rock_Smash);
}
return head.Concat(rock);
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
if (!(pkm is PK2 pk2) || pk2.CaughtData == 0)
@ -306,8 +99,7 @@ namespace PKHeX.Core
if (!slot.IsLevelWithinRange(lvl))
break;
var expect = ((EncounterSlot2)slot).Time;
if (!expect.Contains(time))
if (!Time.Contains(time))
break;
yield return slot;
@ -330,7 +122,7 @@ namespace PKHeX.Core
if (slot.Species != (int) Species.Unown || evo.Form >= 26) // Don't yield !? forms
break;
}
if (!slot.IsLevelWithinRange(evo.MinLevel, evo.Level))
if (slot.LevelMin > evo.Level)
break;
yield return slot;

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
@ -10,119 +9,96 @@ namespace PKHeX.Core
/// </summary>
public sealed class EncounterArea3 : EncounterArea
{
private static IEnumerable<EncounterSlot> GetSlots3(byte[] data, ref int ofs, int numslots, SlotType t)
public readonly int Rate;
internal EncounterArea3() { }
public static EncounterArea3[] GetAreas(byte[][] input, GameVersion game)
{
var slots = new List<EncounterSlot>();
int Ratio = data[ofs];
//1 byte padding
if (Ratio > 0)
ReadInSlots(data, ofs, numslots, t, slots);
ofs += 2 + (numslots * 4);
return slots;
var result = new EncounterArea3[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea3(input[i], game);
return result;
}
private static void ReadInSlots(byte[] data, int ofs, int numslots, SlotType t, List<EncounterSlot> slots)
public static EncounterArea3[] GetAreasSwarm(byte[][] input, GameVersion game)
{
for (int i = 0; i < numslots; i++)
{
int o = ofs + (i * 4);
int species = BitConverter.ToInt16(data, o + 4);
if (species <= 0)
continue;
var result = new EncounterArea3[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea3(input[i], game, false);
return result;
}
slots.Add(new EncounterSlot3
{
LevelMin = data[o + 2],
LevelMax = data[o + 3],
Species = species,
SlotNumber = i,
Type = t
});
private EncounterArea3(byte[] data, GameVersion game)
{
Location = data[0] | (data[1] << 8);
Type = (SlotType)data[2];
Rate = data[3];
Slots = ReadRegularSlots(data, game);
}
private EncounterArea3(byte[] data, GameVersion game, bool _)
{
Location = data[0] | (data[1] << 8);
Type = SlotType.Swarm | SlotType.Grass;
Rate = data[3];
Slots = ReadSwarmSlots(data, game);
}
private EncounterSlot3[] ReadRegularSlots(byte[] data, GameVersion game)
{
const int size = 10;
int count = (data.Length - 4) / size;
var slots = new EncounterSlot3[count];
for (int i = 0; i < slots.Length; i++)
{
int offset = 4 + (size * i);
int species = BitConverter.ToUInt16(data, offset + 0);
int form = data[offset + 2];
int slotNum = data[offset + 3];
int min = data[offset + 4];
int max = data[offset + 5];
int mpi = data[offset + 6];
int mpc = data[offset + 7];
int sti = data[offset + 8];
int stc = data[offset + 9];
slots[i] = new EncounterSlot3(this, species, form, min, max, slotNum, mpi, mpc, sti, stc, game);
}
}
private static IEnumerable<EncounterSlot> GetSlots3Fishing(byte[] data, ref int ofs, int numslots)
{
var slots = new List<EncounterSlot>();
int Ratio = data[ofs];
//1 byte padding
if (Ratio > 0)
ReadFishingSlots(data, ofs, numslots, slots);
ofs += 2 + (numslots * 4);
return slots;
}
private static void ReadFishingSlots(byte[] data, int ofs, int numslots, List<EncounterSlot> slots)
private EncounterSlot3[] ReadSwarmSlots(byte[] data, GameVersion game)
{
for (int i = 0; i < numslots; i++)
const int size = 14;
int count = (data.Length - 4) / size;
var slots = new EncounterSlot3[count];
for (int i = 0; i < slots.Length; i++)
{
int Species = BitConverter.ToInt16(data, ofs + 4 + (i * 4));
if (Species <= 0)
continue;
int offset = 4 + (size * i);
var slot = new EncounterSlot3
int species = BitConverter.ToUInt16(data, offset + 0);
// form always 0
int slotNum = data[offset + 3];
int min = data[offset + 4];
int max = data[offset + 5];
int[] moves =
{
LevelMin = data[ofs + 2 + (i * 4)],
LevelMax = data[ofs + 3 + (i * 4)],
Species = Species,
BitConverter.ToUInt16(data, offset + 6),
BitConverter.ToUInt16(data, offset + 8),
BitConverter.ToUInt16(data, offset + 10),
BitConverter.ToUInt16(data, offset + 12),
};
if (i < 2)
{
slot.Type = SlotType.Old_Rod;
slot.SlotNumber = i; // 0,1
}
else if (i < 5)
{
slot.Type = SlotType.Good_Rod;
slot.SlotNumber = i - 2; // 0,1,2
}
else
{
slot.Type = SlotType.Super_Rod;
slot.SlotNumber = i - 5; // 0,1,2,3,4
}
slots.Add(slot);
slots[i] = new EncounterSlot3Swarm(this, species, min, max, slotNum, game, moves);
}
}
private static EncounterArea3 GetArea3(byte[] data)
{
var HaveGrassSlots = data[1] == 1;
var HaveSurfSlots = data[2] == 1;
var HaveRockSmashSlots = data[3] == 1;
var HaveFishingSlots = data[4] == 1;
int offset = 5;
var slots = new List<EncounterSlot>();
if (HaveGrassSlots)
slots.AddRange(GetSlots3(data, ref offset, 12, SlotType.Grass));
if (HaveSurfSlots)
slots.AddRange(GetSlots3(data, ref offset, 5, SlotType.Surf));
if (HaveRockSmashSlots)
slots.AddRange(GetSlots3(data, ref offset, 5, SlotType.Rock_Smash));
if (HaveFishingSlots)
slots.AddRange(GetSlots3Fishing(data, ref offset, 10));
var area = new EncounterArea3
{
Location = data[0],
Slots = slots.ToArray()
};
foreach (var slot in area.Slots)
slot.Area = area;
return area;
}
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot"/> information from Generation 3 data.
/// </summary>
/// <param name="entries">Raw data, one byte array per encounter area</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea3[] GetArray3(byte[][] entries)
{
return entries.Select(GetArea3).Where(Area => Area.Slots.Length != 0).ToArray();
return slots;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
@ -165,7 +141,7 @@ namespace PKHeX.Core
if (slot.Form != evo.Form)
break;
if (!slot.IsLevelWithinRange(evo.MinLevel, evo.Level))
if (slot.LevelMin > evo.Level)
break;
yield return slot;

View file

@ -1,53 +0,0 @@
using System;
namespace PKHeX.Core
{
/// <inheritdoc />
/// <summary>
/// Base encounter class for manually repacked areas
/// </summary>
/// <remarks>
/// Encounter Data is stored in the following format: (u16 Location, n*[u16 Species/Form, u8 Min, u8 Max]), hence the 32bit name
/// </remarks>
public abstract class EncounterArea32 : EncounterArea
{
/// <summary>
/// Gets an array of areas from an array of raw area data
/// </summary>
/// <param name="entries">Simplified raw format of an Area</param>
/// <returns>Array of areas</returns>
public static A[] GetArray<A, S>(byte[][] entries)
where A : EncounterArea32, new()
where S : EncounterSlot, new()
{
var data = new A[entries.Length];
for (int i = 0; i < data.Length; i++)
{
var loc = data[i] = new A();
loc.LoadSlots<S>(entries[i]);
}
return data;
}
private void LoadSlots<S>(byte[] areaData) where S : EncounterSlot, new()
{
var count = (areaData.Length - 2) / 4;
Location = BitConverter.ToUInt16(areaData, 0);
Slots = new EncounterSlot[count];
for (int i = 0; i < Slots.Length; i++)
{
int ofs = 2 + (i * 4);
ushort SpecForm = BitConverter.ToUInt16(areaData, ofs);
Slots[i] = new S
{
Species = SpecForm & 0x7FF,
Form = SpecForm >> 11,
LevelMin = areaData[ofs + 2],
LevelMax = areaData[ofs + 3],
};
}
foreach (var slot in Slots)
slot.Area = this;
}
}
}

View file

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <inheritdoc />
/// <summary>
/// <see cref="GameVersion.XD"/> encounter area
/// </summary>
public sealed class EncounterArea3XD : EncounterArea
{
public EncounterArea3XD(int loc, int s0, int l0, int s1, int l1, int s2, int l2)
{
Location = loc;
Type = SlotType.Grass;
Slots = new[]
{
new EncounterSlot3PokeSpot(this, s0, 10, l0, 0),
new EncounterSlot3PokeSpot(this, s1, 10, l1, 1),
new EncounterSlot3PokeSpot(this, s2, 10, l2, 2),
};
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
if (pkm.Format != 3) // Met Location and Met Level are changed on PK3->PK4
return GetSlotsFuzzy(chain);
if (pkm.Met_Location != Location)
return Array.Empty<EncounterSlot>();
return GetSlotsMatching(chain, pkm.Met_Level);
}
private IEnumerable<EncounterSlot> GetSlotsMatching(IReadOnlyList<EvoCriteria> chain, int lvl)
{
foreach (var slot in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (slot.Form != evo.Form)
break;
if (!slot.IsLevelWithinRange(lvl))
break;
yield return slot;
break;
}
}
}
private IEnumerable<EncounterSlot> GetSlotsFuzzy(IReadOnlyList<EvoCriteria> chain)
{
foreach (var slot in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (slot.Form != evo.Form)
break;
if (slot.LevelMin > evo.Level)
break;
yield return slot;
break;
}
}
}
}
}

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
@ -8,57 +7,54 @@ namespace PKHeX.Core
/// <summary>
/// <see cref="GameVersion.Gen4"/> encounter area
/// </summary>
public abstract class EncounterArea4 : EncounterArea
public sealed class EncounterArea4 : EncounterArea
{
/// <summary>
/// Reads the GBA Pak Special slots, cloning <see cref="EncounterSlot"/> data from the area's base encounter slots.
/// </summary>
/// <remarks>
/// These special slots only contain the info of species id; the level is copied from the corresponding <see cref="slotnums"/> index.
/// </remarks>
/// <param name="data">Encounter binary data</param>
/// <param name="ofs">Offset to read from</param>
/// <param name="slotSize">DP/Pt slotSize = 4 bytes/entry, HG/SS slotSize = 2 bytes/entry</param>
/// <param name="ReplacedSlots">Slots from regular encounter table that end up replaced by in-game conditions</param>
/// <param name="slotnums">Slot indexes to replace with read species IDs</param>
/// <param name="t">Slot type of the special encounter</param>
protected static List<EncounterSlot4> GetSlots4GrassSlotReplace(byte[] data, int ofs, int slotSize, EncounterSlot[] ReplacedSlots, int[] slotnums, SlotType t = SlotType.Grass)
{
var slots = new List<EncounterSlot4>();
public readonly EncounterType TypeEncounter;
public readonly int Rate;
int numslots = slotnums.Length;
for (int i = 0; i < numslots; i++)
public static EncounterArea4[] GetAreas(byte[][] input, GameVersion game)
{
var result = new EncounterArea4[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea4(input[i], game);
return result;
}
private EncounterArea4(byte[] data, GameVersion game)
{
Location = data[0] | (data[1] << 8);
Type = (SlotType)data[2];
Rate = data[3];
TypeEncounter = (EncounterType) BitConverter.ToUInt16(data, 4);
Slots = ReadRegularSlots(data, game);
}
private EncounterSlot4[] ReadRegularSlots(byte[] data, GameVersion game)
{
const int size = 10;
int count = (data.Length - 6) / size;
var slots = new EncounterSlot4[count];
for (int i = 0; i < slots.Length; i++)
{
var baseSlot = ReplacedSlots[slotnums[i]];
if (baseSlot.LevelMin <= 0)
continue;
int offset = 6 + (size * i);
int species = BitConverter.ToUInt16(data, ofs + (i / (4 / slotSize) * slotSize));
if (species <= 0 || baseSlot.Species == species) // Empty or duplicate
continue;
int species = BitConverter.ToUInt16(data, offset + 0);
int form = data[offset + 2];
int slotNum = data[offset + 3];
int min = data[offset + 4];
int max = data[offset + 5];
var slot = (EncounterSlot4)baseSlot.Clone();
slot.Species = species;
slot.Type = t;
slot.SlotNumber = i;
slots.Add(slot);
int mpi = data[offset + 6];
int mpc = data[offset + 7];
int sti = data[offset + 8];
int stc = data[offset + 9];
slots[i] = new EncounterSlot4(this, species, form, min, max, slotNum, mpi, mpc, sti, stc, game);
}
return slots;
}
protected static IEnumerable<EncounterSlot4> MarkStaticMagnetExtras(IEnumerable<IEnumerable<List<EncounterSlot4>>> product)
{
var trackPermute = new List<EncounterSlot4>();
foreach (var p in product)
MarkStaticMagnetPermute(p.SelectMany(z => z), trackPermute);
return trackPermute;
}
protected static void MarkStaticMagnetPermute(IEnumerable<EncounterSlot4> grp, List<EncounterSlot4> trackPermute)
{
EncounterUtil.MarkEncountersStaticMagnetPullPermutation(grp, PersonalTable.HGSS, trackPermute);
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
if (pkm.Format != 4) // Met Location and Met Level are changed on PK4->PK5
@ -77,7 +73,7 @@ namespace PKHeX.Core
if (slot.Species != evo.Species)
continue;
if (slot.Form != evo.Form && !Legal.WildChangeFormAfter.Contains(slot.Species))
if (slot.Form != evo.Form && !Legal.WildChangeFormAfter.Contains(slot.Species) && slot.Species != (int)Species.Unown)
break;
if (!slot.IsLevelWithinRange(lvl))
break;
@ -97,9 +93,9 @@ namespace PKHeX.Core
if (slot.Species != evo.Species)
continue;
if (slot.Form != evo.Form && !Legal.WildChangeFormAfter.Contains(slot.Species))
if (slot.Form != evo.Form && !Legal.WildChangeFormAfter.Contains(slot.Species) && slot.Species != (int)Species.Unown)
break;
if (!slot.IsLevelWithinRange(evo.MinLevel, evo.Level))
if (slot.LevelMin > evo.Level)
break;
yield return slot;

View file

@ -1,204 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
/// <inheritdoc />
/// <summary>
/// <see cref="GameVersion.DPPt"/> encounter area
/// </summary>
public sealed class EncounterArea4DPPt : EncounterArea4
{
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot"/> information from Generation 4 Diamond, Pearl and Platinum data.
/// </summary>
/// <param name="entries">Raw data, one byte array per encounter area</param>
/// <param name="pt">Platinum flag (for Trophy Garden slot insertion)</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea4DPPt[] GetArray4DPPt(byte[][] entries, bool pt = false)
{
return entries.Select(z => GetArea4DPPt(z, pt)).Where(Area => Area.Slots.Length != 0).ToArray();
}
private static EncounterSlot4[] GetSlots4GrassDPPt(byte[] data, int ofs, int numslots, SlotType t)
{
var slots = new EncounterSlot4[numslots];
for (int i = 0; i < numslots; i++)
{
int o = ofs + (i * 8);
int level = data[o];
int species = BitConverter.ToInt32(data, o + 4);
slots[i] = new EncounterSlot4
{
LevelMax = level,
LevelMin = level,
Species = species,
SlotNumber = i,
Type = t
};
}
return slots;
}
private static IEnumerable<EncounterSlot4> GetSlots4WaterFishingDPPt(byte[] data, int ofs, int numslots, SlotType t)
{
var slots = new List<EncounterSlot4>();
for (int i = 0; i < numslots; i++)
{
// max, min, unused, unused, [32bit species]
int Species = BitConverter.ToInt32(data, ofs + 4 + (i * 8));
if (Species <= 0)
continue;
// Fishing and Surf slots without a species ID are not added
// DPPt does not have fishing or surf swarms, and does not have any Rock Smash encounters.
slots.Add(new EncounterSlot4
{
LevelMax = data[ofs + 0 + (i * 8)],
LevelMin = data[ofs + 1 + (i * 8)],
Species = Species,
SlotNumber = i,
Type = t
});
}
EncounterUtil.MarkEncountersStaticMagnetPull(slots, PersonalTable.HGSS);
return slots;
}
private static EncounterArea4DPPt GetArea4DPPt(byte[] data, bool pt = false)
{
var Slots = new List<EncounterSlot>();
int location = BitConverter.ToUInt16(data, 0x00);
var GrassRatio = BitConverter.ToInt32(data, 0x02);
if (GrassRatio > 0)
{
var GrassSlots = GetSlots4GrassDPPt(data, 0x06, 12, SlotType.Grass);
//Swarming slots replace slots 0 and 1
var swarm = GetSlots4GrassSlotReplace(data, 0x66, 4, GrassSlots, Legal.Slot4_Swarm, SlotType.Swarm);
//Morning and Night slots replace slots 2 and 3
var morning = GetSlots4GrassSlotReplace(data, 0x6E, 4, GrassSlots, Legal.Slot4_Time); // Morning
var night = GetSlots4GrassSlotReplace(data, 0x76, 4, GrassSlots, Legal.Slot4_Time); // Night
//Pokéradar slots replace slots 4,5,10 and 11
//Pokéradar is marked with different slot type because it have different PID-IV generationn
var radar = GetSlots4GrassSlotReplace(data, 0x7E, 4, GrassSlots, Legal.Slot4_Radar, SlotType.Pokeradar);
//24 bytes padding
//Dual Slots replace slots 8 and 9
var ruby = GetSlots4GrassSlotReplace(data, 0xA6, 4, GrassSlots, Legal.Slot4_Dual); // Ruby
var sapphire = GetSlots4GrassSlotReplace(data, 0xAE, 4, GrassSlots, Legal.Slot4_Dual); // Sapphire
var emerald = GetSlots4GrassSlotReplace(data, 0xB6, 4, GrassSlots, Legal.Slot4_Dual); // Emerald
var firered = GetSlots4GrassSlotReplace(data, 0xBE, 4, GrassSlots, Legal.Slot4_Dual); // FireRed
var leafgreen = GetSlots4GrassSlotReplace(data, 0xC6, 4, GrassSlots, Legal.Slot4_Dual); // LeafGreen
Slots.AddRange(GrassSlots);
Slots.AddRange(swarm);
Slots.AddRange(morning);
Slots.AddRange(night);
Slots.AddRange(radar);
Slots.AddRange(ruby);
Slots.AddRange(sapphire);
Slots.AddRange(emerald);
Slots.AddRange(firered);
Slots.AddRange(leafgreen);
// Permute Static-Magnet Pull combinations
// [None/Swarm]-[None/Morning/Night]-[None/Radar]-[None/R/S/E/F/L] [None/TrophyGarden]
// 2 * 3 * 2 * 6 = 72 different combinations of slots (more with trophy garden)
var regular = new List<List<EncounterSlot4>> { GrassSlots.Where(z => z.SlotNumber == 6 || z.SlotNumber == 7).ToList() }; // every other slot is in the product
var pair0 = new List<List<EncounterSlot4>> { GrassSlots.Where(z => Legal.Slot4_Swarm.Contains(z.SlotNumber)).ToList() };
var pair1 = new List<List<EncounterSlot4>> { GrassSlots.Where(z => Legal.Slot4_Time.Contains(z.SlotNumber)).ToList() };
var pair2 = new List<List<EncounterSlot4>> { GrassSlots.Where(z => Legal.Slot4_Radar.Contains(z.SlotNumber)).ToList() };
var pair3 = new List<List<EncounterSlot4>> { GrassSlots.Where(z => Legal.Slot4_Dual.Contains(z.SlotNumber)).ToList() };
if (swarm.Count != 0) pair0.Add(swarm);
if (morning.Count != 0) pair1.Add(morning); if (night.Count != 0) pair1.Add(night);
if (radar.Count != 0) pair2.Add(radar);
if (ruby.Count != 0) pair3.Add(ruby); if (sapphire.Count != 0) pair3.Add(sapphire); if (emerald.Count != 0) pair3.Add(emerald);
if (firered.Count != 0) pair3.Add(firered); if (leafgreen.Count != 0) pair3.Add(leafgreen);
if (location == 68) // Trophy Garden
{
// Occupy Slots 6 & 7
var species = pt ? Encounters4.TrophyPt : Encounters4.TrophyDP;
var slots = new List<EncounterSlot4>();
foreach (var s in species)
{
var slot = (EncounterSlot4)regular[0][0].Clone();
slot.Species = s;
slots.Add(slot);
slot = (EncounterSlot4)regular[0][1].Clone();
slot.Species = s;
slots.Add(slot);
}
Slots.AddRange(slots);
// get all permutations of trophy inhabitants
var trophy = regular[0].Concat(slots).ToArray();
for (int i = 0; i < trophy.Length; i++)
{
for (int j = i + 1; j < trophy.Length; j++)
regular.Add(new List<EncounterSlot4> { trophy[i], trophy[j] });
}
}
var set = new[] { regular, pair0, pair1, pair2, pair3 };
var product = set.CartesianProduct();
var extra = MarkStaticMagnetExtras(product);
Slots.AddRange(extra);
}
var SurfRatio = BitConverter.ToInt32(data, 0xCE);
if (SurfRatio > 0)
Slots.AddRange(GetSlots4WaterFishingDPPt(data, 0xD2, 5, SlotType.Surf));
//44 bytes padding
var OldRodRatio = BitConverter.ToInt32(data, 0x126);
if (OldRodRatio > 0)
Slots.AddRange(GetSlots4WaterFishingDPPt(data, 0x12A, 5, SlotType.Old_Rod));
var GoodRodRatio = BitConverter.ToInt32(data, 0x152);
if (GoodRodRatio > 0)
Slots.AddRange(GetSlots4WaterFishingDPPt(data, 0x156, 5, SlotType.Good_Rod));
var SuperRodRatio = BitConverter.ToInt32(data, 0x17E);
if (SuperRodRatio > 0)
Slots.AddRange(GetSlots4WaterFishingDPPt(data, 0x182, 5, SlotType.Super_Rod));
var Area4 = new EncounterArea4DPPt
{
Location = location,
Slots = Slots.ToArray()
};
foreach (var slot in Area4.Slots)
slot.Area = Area4;
return Area4;
}
private EncounterArea4DPPt Clone(int location) => new EncounterArea4DPPt { Slots = Slots, Location = location};
public EncounterArea4DPPt[] Clone(int[] locations)
{
var Areas = new EncounterArea4DPPt[locations.Length];
for (int i = 0; i < locations.Length; i++)
Areas[i] = Clone(locations[i]);
return Areas;
}
}
public static class DPEncounterExtensions
{
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item }));
}
}
}

View file

@ -1,206 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
/// <inheritdoc />
/// <summary>
/// <see cref="GameVersion.HGSS"/> encounter area
/// </summary>
public sealed class EncounterArea4HGSS : EncounterArea4
{
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot"/> information from Generation 4 Heart Gold and Soul Silver data.
/// </summary>
/// <param name="entries">Raw data, one byte array per encounter area</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea4HGSS[] GetArray4HGSS(byte[][] entries)
{
return entries.Select(GetArea4HGSS).Where(Area => Area.Slots.Length != 0).ToArray();
}
/// <summary>
/// Gets the encounter areas with <see cref="EncounterSlot"/> information from Generation 4 Heart Gold and Soul Silver Headbutt tree data.
/// </summary>
/// <param name="entries">Raw data, one byte array per encounter area</param>
/// <returns>Array of encounter areas.</returns>
public static EncounterArea4HGSS[] GetArray4HGSS_Headbutt(byte[][] entries)
{
return entries.Select(GetArea4HeadbuttHGSS).Where(Area => Area.Slots.Length != 0).ToArray();
}
private static EncounterSlot4[] GetSlots4GrassHGSS(byte[] data, int ofs, int numslots, SlotType t)
{
var slots = new EncounterSlot4[numslots * 3];
// First 36 slots are morning, day and night grass slots
// The order is 12 level values, 12 morning species, 12 day species and 12 night species
for (int i = 0; i < numslots; i++)
{
int level = data[ofs + i];
int species = BitConverter.ToUInt16(data, ofs + numslots + (i * 2));
slots[i] = new EncounterSlot4
{
LevelMin = level,
LevelMax = level,
Species = species,
SlotNumber = i,
Type = t
};
slots[numslots + i] = (EncounterSlot4)slots[i].Clone();
slots[numslots + i].Species = BitConverter.ToUInt16(data, ofs + (numslots * 3) + (i * 2));
slots[numslots + i].Type = t;
slots[(numslots * 2) + i] = (EncounterSlot4)slots[i].Clone();
slots[(numslots * 2) + i].Species = BitConverter.ToUInt16(data, ofs + (numslots * 5) + (i * 2));
slots[(numslots * 2) + i].Type = t;
}
return slots;
}
private static IEnumerable<EncounterSlot4> GetSlots4WaterFishingHGSS(byte[] data, int ofs, int numslots, SlotType t)
{
var slots = new List<EncounterSlot4>();
for (int i = 0; i < numslots; i++)
{
// min, max, [16bit species]
int Species = BitConverter.ToInt16(data, ofs + 2 + (i * 4));
if (t == SlotType.Rock_Smash && Species <= 0)
continue;
// Fishing and surf Slots without a species ID are added too; these are needed for the swarm encounters.
// These empty slots will will be deleted after we add swarm slots.
slots.Add(new EncounterSlot4
{
LevelMin = data[ofs + 0 + (i * 4)],
LevelMax = data[ofs + 1 + (i * 4)],
Species = Species,
SlotNumber = i,
Type = t
});
}
EncounterUtil.MarkEncountersStaticMagnetPull(slots, PersonalTable.HGSS);
return slots;
}
private static EncounterArea4HGSS GetArea4HGSS(byte[] data)
{
var Slots = new List<EncounterSlot4>();
var GrassRatio = data[0x02];
var SurfRatio = data[0x03];
var RockSmashRatio = data[0x04];
var OldRodRatio = data[0x05];
var GoodRodRatio = data[0x06];
var SuperRodRatio = data[0x07];
// 2 bytes padding
if (GrassRatio > 0)
{
// First 36 slots are morning, day and night grass slots
// The order is 12 level values, 12 morning species, 12 day species and 12 night species
var GrassSlots = GetSlots4GrassHGSS(data, 0x0A, 12, SlotType.Grass);
//Grass slots with species = 0 are added too, it is needed for the swarm encounters, it will be deleted after swarms are added
// Hoenn Sound and Sinnoh Sound replace slots 4 and 5
var hoenn = GetSlots4GrassSlotReplace(data, 0x5E, 2, GrassSlots, Legal.Slot4_Sound); // Hoenn
var sinnoh = GetSlots4GrassSlotReplace(data, 0x62, 2, GrassSlots, Legal.Slot4_Sound); // Sinnoh
Slots.AddRange(GrassSlots);
Slots.AddRange(hoenn);
Slots.AddRange(sinnoh);
// Static / Magnet Pull
var grass1 = GrassSlots.Take(12).ToList();
var grass2 = GrassSlots.Skip(12).Take(12).ToList();
var grass3 = GrassSlots.Skip(24).ToList();
// Swarm slots do not displace electric/steel types, with exception of SoulSilver Mawile (which doesn't displace) -- handle separately
foreach (var time in new[] { grass1, grass2, grass3 })
{
// non radio
var regular = time.Where(z => !Legal.Slot4_Sound.Contains(z.SlotNumber)).ToList(); // every other slot is in the product
var radio = new List<List<EncounterSlot4>> { time.Where(z => Legal.Slot4_Sound.Contains(z.SlotNumber)).ToList() };
if (hoenn.Count > 0)
radio.Add(hoenn);
if (sinnoh.Count > 0)
radio.Add(sinnoh);
var extra = new List<EncounterSlot4>();
foreach (var t in radio)
MarkStaticMagnetPermute(regular.Concat(t), extra);
Slots.AddRange(extra);
}
}
if (SurfRatio > 0)
Slots.AddRange(GetSlots4WaterFishingHGSS(data, 0x66, 5, SlotType.Surf));
if (RockSmashRatio > 0)
Slots.AddRange(GetSlots4WaterFishingHGSS(data, 0x7A, 2, SlotType.Rock_Smash));
if (OldRodRatio > 0)
Slots.AddRange(GetSlots4WaterFishingHGSS(data, 0x82, 5, SlotType.Old_Rod));
if (GoodRodRatio > 0)
Slots.AddRange(GetSlots4WaterFishingHGSS(data, 0x96, 5, SlotType.Good_Rod));
if (SuperRodRatio > 0)
Slots.AddRange(GetSlots4WaterFishingHGSS(data, 0xAA, 5, SlotType.Super_Rod));
// Last 6 bytes only have species ID info
if (data[0xC2] == 120) // Location = 182, 127, 130, 132, 167, 188, 210
Slots.AddRange(SlotsHGSS_Staryu);
var Area4 = new EncounterArea4HGSS
{
Location = BitConverter.ToUInt16(data, 0x00),
Slots = Slots.ToArray()
};
foreach (var slot in Area4.Slots)
slot.Area = Area4;
return Area4;
}
private static readonly EncounterSlot4[] SlotsHGSS_Staryu =
{
new EncounterSlot4 { Species = 120, LevelMin = 20, LevelMax = 20, Type = SlotType.Good_Rod },
new EncounterSlot4 { Species = 120, LevelMin = 40, LevelMax = 40, Type = SlotType.Super_Rod },
};
private static EncounterArea4HGSS GetArea4HeadbuttHGSS(byte[] data)
{
if (data.Length < 78)
return new EncounterArea4HGSS(); // bad data
//2 byte location ID (defer to end)
//4 bytes padding
var Slots = new List<EncounterSlot>();
// 00-11 Normal trees
// 12-17 Special trees
for (int i = 0; i < 18; i++)
{
int Species = BitConverter.ToInt16(data, 6 + (i * 4));
if (Species <= 0)
continue;
Slots.Add(new EncounterSlot4
{
Species = Species,
LevelMin = data[8 + (i * 4)],
LevelMax = data[9 + (i * 4)],
Type = i <= 11 ? SlotType.Headbutt : SlotType.Headbutt_Special
});
}
var Area = new EncounterArea4HGSS
{
Location = BitConverter.ToUInt16(data, 0),
Slots = Slots.ToArray()
};
foreach (var slot in Area.Slots)
slot.Area = Area;
return Area;
}
}
}

View file

@ -1,10 +1,68 @@
namespace PKHeX.Core
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <inheritdoc />
/// <summary>
/// <see cref="GameVersion.Gen5"/> encounter area
/// </summary>
public sealed class EncounterArea5 : EncounterArea32
public sealed class EncounterArea5 : EncounterArea
{
public static EncounterArea5[] GetAreas(byte[][] input, GameVersion game)
{
var result = new EncounterArea5[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea5(input[i], game);
return result;
}
private EncounterArea5(byte[] data, GameVersion game)
{
Location = data[0] | (data[1] << 8);
Type = (SlotType)data[2];
Slots = ReadSlots(data, game);
}
private EncounterSlot5[] ReadSlots(byte[] data, GameVersion game)
{
const int size = 4;
int count = (data.Length - 4) / size;
var slots = new EncounterSlot5[count];
for (int i = 0; i < slots.Length; i++)
{
int offset = 4 + (size * i);
ushort SpecForm = BitConverter.ToUInt16(data, offset);
int species = SpecForm & 0x3FF;
int form = SpecForm >> 11;
int min = data[offset + 2];
int max = data[offset + 3];
slots[i] = new EncounterSlot5(this, species, form, min, max, game);
}
return slots;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
foreach (var slot in Slots)
{
foreach (var evo in chain)
{
if (slot.Species != evo.Species)
continue;
if (!slot.IsLevelWithinRange(pkm.Met_Level))
break;
if (slot.Form != evo.Form && !Legal.WildChangeFormAfter.Contains(slot.Species))
break;
yield return slot;
break;
}
}
}
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -6,8 +7,43 @@ namespace PKHeX.Core
/// <summary>
/// <see cref="GameVersion.ORAS"/> encounter area
/// </summary>
public sealed class EncounterArea6AO : EncounterArea32
public sealed class EncounterArea6AO : EncounterArea
{
public static EncounterArea6AO[] GetAreas(byte[][] input, GameVersion game)
{
var result = new EncounterArea6AO[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea6AO(input[i], game);
return result;
}
private EncounterArea6AO(byte[] data, GameVersion game)
{
Location = data[0] | (data[1] << 8);
Type = (SlotType)data[2];
Slots = ReadSlots(data, game);
}
private EncounterSlot6AO[] ReadSlots(byte[] data, GameVersion game)
{
const int size = 4;
int count = (data.Length - 4) / size;
var slots = new EncounterSlot6AO[count];
for (int i = 0; i < slots.Length; i++)
{
int offset = 4 + (size * i);
ushort SpecForm = BitConverter.ToUInt16(data, offset);
int species = SpecForm & 0x3FF;
int form = SpecForm >> 11;
int min = data[offset + 2];
int max = data[offset + 3];
slots[i] = new EncounterSlot6AO(this, species, form, min, max, game);
}
return slots;
}
private const int FluteBoostMin = 4; // White Flute decreases levels.
private const int FluteBoostMax = 4; // Black Flute increases levels.
private const int DexNavBoost = 30; // Maximum DexNav chain
@ -23,7 +59,7 @@ namespace PKHeX.Core
if (slot.Species != evo.Species)
continue;
var boostMax = slot.Type != SlotType.Rock_Smash ? DexNavBoost : FluteBoostMax;
var boostMax = Type != SlotType.Rock_Smash ? DexNavBoost : FluteBoostMax;
const int boostMin = FluteBoostMin;
if (!slot.IsLevelWithinRange(pkm.Met_Level, boostMin, boostMax))
break;
@ -41,14 +77,17 @@ namespace PKHeX.Core
private static void MarkSlotDetails(PKM pkm, EncounterSlot6AO slot, EvoCriteria evo)
{
bool nav = slot.AllowDexNav && (pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4);
slot.DexNav = nav;
if (slot.LevelMin > evo.MinLevel)
slot.WhiteFlute = true;
if (slot.LevelMax + 1 <= evo.MinLevel && evo.MinLevel <= slot.LevelMax + FluteBoostMax)
slot.BlackFlute = true;
if (slot.LevelMax != evo.MinLevel && slot.AllowDexNav)
if (!slot.CanDexNav)
return;
if (slot.LevelMax != evo.MinLevel)
slot.DexNav = true;
if (pkm.RelearnMove1 != 0 || pkm.AbilityNumber == 4)
slot.DexNav = true;
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -6,8 +7,55 @@ namespace PKHeX.Core
/// <summary>
/// <see cref="GameVersion.XY"/> encounter area
/// </summary>
public sealed class EncounterArea6XY : EncounterArea32
public sealed class EncounterArea6XY : EncounterArea
{
public static EncounterArea6XY[] GetAreas(byte[][] input, GameVersion game)
{
var result = new EncounterArea6XY[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea6XY(input[i], game);
return result;
}
public EncounterArea6XY(ICollection<int> species)
{
Location = 148;
Type = SlotType.FriendSafari;
var slots = new EncounterSlot6XY[species.Count];
int ctr = 0;
foreach (var s in species)
slots[ctr++] = new EncounterSlot6XY(this, s, 0, 30, 30, GameVersion.XY);
Slots = slots;
}
private EncounterArea6XY(byte[] data, GameVersion game)
{
Location = data[0] | (data[1] << 8);
Type = (SlotType)data[2];
Slots = ReadSlots(data, game);
}
private EncounterSlot6XY[] ReadSlots(byte[] data, GameVersion game)
{
const int size = 4;
int count = (data.Length - 4) / size;
var slots = new EncounterSlot6XY[count];
for (int i = 0; i < slots.Length; i++)
{
int offset = 4 + (size * i);
ushort SpecForm = BitConverter.ToUInt16(data, offset);
int species = SpecForm & 0x3FF;
int form = SpecForm >> 11;
int min = data[offset + 2];
int max = data[offset + 3];
slots[i] = new EncounterSlot6XY(this, species, form, min, max, game);
}
return slots;
}
private const int RandomForm = 31;
private const int RandomFormVivillon = RandomForm - 1;

View file

@ -1,53 +0,0 @@
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
public class EncounterArea6XYFriendSafari : EncounterArea
{
public static bool WasFriendSafari(PKM pkm)
{
if (!pkm.XY)
return false;
if (pkm.Met_Location != 148)
return false;
if (pkm.Met_Level != 30)
return false;
if (pkm.Egg_Location != 0)
return false;
return true;
}
public static ILookup<int, EncounterSlot6XY> GetArea()
{
var area = new EncounterArea6XYFriendSafari { Location = 148 };
EncounterSlot6XY FriendSafariSlot(int d)
{
return new EncounterSlot6XY
{
Area = area,
Species = d,
LevelMin = 30,
LevelMax = 30,
Form = 0,
Type = SlotType.FriendSafari,
Version = GameVersion.XY,
};
}
area.Slots = Legal.FriendSafari.Select(FriendSafariSlot).ToArray();
return area.Slots.Cast<EncounterSlot6XY>().ToLookup(s => s.Species);
}
public static IEnumerable<EncounterSlot> GetValidSafariEncounters(PKM pkm)
{
var chain = EvolutionChain.GetValidPreEvolutions(pkm);
return GetValidSafariEncounters(chain);
}
public static IEnumerable<EncounterSlot> GetValidSafariEncounters(IReadOnlyList<DexLevel> chain)
{
var valid = chain.Where(d => d.Level >= 30);
return valid.SelectMany(z => Encounters6.FriendSafari[z.Species]);
}
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -6,8 +7,43 @@ namespace PKHeX.Core
/// <summary>
/// <see cref="GameVersion.Gen7"/> encounter area
/// </summary>
public sealed class EncounterArea7 : EncounterArea32
public sealed class EncounterArea7 : EncounterArea
{
public static EncounterArea7[] GetAreas(byte[][] input, GameVersion game)
{
var result = new EncounterArea7[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea7(input[i], game);
return result;
}
private EncounterArea7(byte[] data, GameVersion game)
{
Location = data[0] | (data[1] << 8);
Type = (SlotType)data[2];
Slots = ReadSlots(data, game);
}
private EncounterSlot7[] ReadSlots(byte[] data, GameVersion game)
{
const int size = 4;
int count = (data.Length - 4) / size;
var slots = new EncounterSlot7[count];
for (int i = 0; i < slots.Length; i++)
{
int offset = 4 + (size * i);
ushort SpecForm = BitConverter.ToUInt16(data, offset);
int species = SpecForm & 0x3FF;
int form = SpecForm >> 11;
int min = data[offset + 2];
int max = data[offset + 3];
slots[i] = new EncounterSlot7(this, species, form, min, max, game);
}
return slots;
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
foreach (var slot in Slots)

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
@ -6,8 +7,40 @@ namespace PKHeX.Core
/// <summary>
/// <see cref="GameVersion.GG"/> encounter area
/// </summary>
public sealed class EncounterArea7b : EncounterArea32
public sealed class EncounterArea7b : EncounterArea
{
public static EncounterArea7b[] GetAreas(byte[][] input, GameVersion game)
{
var result = new EncounterArea7b[input.Length];
for (int i = 0; i < input.Length; i++)
result[i] = new EncounterArea7b(input[i], game);
return result;
}
private EncounterArea7b(byte[] data, GameVersion game)
{
Location = data[0] | (data[1] << 8);
Slots = ReadSlots(data, game);
}
private EncounterSlot7b[] ReadSlots(byte[] data, GameVersion game)
{
const int size = 4;
int count = (data.Length - 2) / size;
var slots = new EncounterSlot7b[count];
for (int i = 0; i < slots.Length; i++)
{
int offset = 2 + (size * i);
ushort SpecForm = BitConverter.ToUInt16(data, offset);
int species = SpecForm & 0x3FF;
int min = data[offset + 2];
int max = data[offset + 3];
slots[i] = new EncounterSlot7b(this, species, min, max, game);
}
return slots;
}
private const int CatchComboBonus = 1;
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
namespace PKHeX.Core
{
@ -6,8 +7,56 @@ namespace PKHeX.Core
/// <summary>
/// <see cref="GameVersion.GO"/> encounter area for <see cref="GameVersion.GG"/>
/// </summary>
public sealed class EncounterArea7g : EncounterArea32
public sealed class EncounterArea7g : EncounterArea
{
internal static EncounterArea7g[] GetArea()
{
var area = new EncounterArea7g { Location = 50, Type = SlotType.GoPark };
static EncounterSlot GetSlot(EncounterArea7g area, int species, int form)
{
return new EncounterSlot7GO(area, species, form, 1, 40, GameVersion.GO);
}
var obtainable = Enumerable.Range(1, 150).Concat(Enumerable.Range(808, 2)); // count : 152
var AlolanKanto = new byte[]
{
// Level 1+
019, // Rattata
020, // Raticate
027, // Sandshrew
028, // Sandslash
037, // Vulpix
038, // Ninetales
050, // Diglett
051, // Dugtrio
052, // Meowth
053, // Persian
074, // Geodude
075, // Graveler
076, // Golem
088, // Grimer
089, // Muk
103, // Exeggutor
105, // Marowak
// Level 15+
026, // Raichu
};
var regular = obtainable.Select(z => GetSlot(area, z, 0));
var alolan = AlolanKanto.Select(z => GetSlot(area, z, 1));
var slots = regular.Concat(alolan).ToArray();
slots[slots.Length - 1].LevelMin = 15; // Raichu
slots[(int)Species.Mewtwo - 1].LevelMin = 15;
slots[(int)Species.Articuno - 1].LevelMin = 15;
slots[(int)Species.Zapdos - 1].LevelMin = 15;
slots[(int)Species.Moltres - 1].LevelMin = 15;
area.Slots = slots;
return new[] { area };
}
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain)
{
foreach (var slot in Slots)

View file

@ -198,19 +198,20 @@ namespace PKHeX.Core
/// Gets an array of areas from an array of raw area data
/// </summary>
/// <param name="entries">Simplified raw format of an Area</param>
/// <param name="game">Game of origin</param>
/// <returns>Array of areas</returns>
public static T[] GetArray<T>(byte[][] entries) where T : EncounterAreaSH, new()
public static T[] GetArray<T>(byte[][] entries, GameVersion game) where T : EncounterAreaSH, new()
{
T[] data = new T[entries.Length];
for (int i = 0; i < data.Length; i++)
{
var loc = data[i] = new T();
loc.LoadSlots(entries[i]);
loc.LoadSlots(entries[i], game);
}
return data;
}
private void LoadSlots(byte[] areaData)
private void LoadSlots(byte[] areaData, GameVersion game)
{
Location = areaData[0];
Slots = new EncounterSlot[areaData[1]];
@ -228,11 +229,9 @@ namespace PKHeX.Core
for (int i = 0; i < count; i++, ctr++, ofs += 2)
{
var specForm = BitConverter.ToUInt16(areaData, ofs);
Slots[ctr] = new EncounterSlot8(specForm, min, max, flags);
Slots[ctr] = new EncounterSlot8(this, specForm, min, max, flags, game);
}
} while (ctr != Slots.Length);
foreach (var slot in Slots)
slot.Area = this;
}
}
@ -271,7 +270,7 @@ namespace PKHeX.Core
public override string LongName => Weather == AreaWeather8.All ? wild : $"{wild} - {Weather.ToString().Replace("_", string.Empty)}";
public override int Generation => 8;
public EncounterSlot8(int specForm, int min, int max, AreaWeather8 weather)
public EncounterSlot8(EncounterAreaSH area, int specForm, int min, int max, AreaWeather8 weather, GameVersion game) : base(area)
{
Species = specForm & 0x7FF;
Form = specForm >> 11;
@ -279,6 +278,7 @@ namespace PKHeX.Core
LevelMax = max;
Weather = weather;
Version = game;
}
}
}

View file

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
namespace PKHeX.Core
{
/// <inheritdoc />
/// <summary>
/// Fake encounter area used to mock data
/// </summary>
public sealed class EncounterAreaFake : EncounterArea
{
public override IEnumerable<EncounterSlot> GetMatchingSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain) =>
Array.Empty<EncounterSlot>();
}
}

View file

@ -19,152 +19,6 @@ namespace PKHeX.Core
return source.Where(s => s.Version.Contains(game)).ToArray();
}
/// <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>
internal static A[] GetEncounterTables<A, S>(string ident, string resource)
where A : EncounterArea32, new()
where S : EncounterSlot, new()
{
byte[] mini = Util.GetBinaryResource($"encounter_{resource}.pkl");
return EncounterArea32.GetArray<A, S>(BinLinker.Unpack(mini, ident));
}
/// <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>
internal static T[] GetEncounterTables8<T>(string ident, string resource) where T : EncounterAreaSH, new()
{
byte[] mini = Util.GetBinaryResource($"encounter_{resource}.pkl");
return EncounterAreaSH.GetArray<T>(BinLinker.Unpack(mini, ident));
}
/// <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>
internal static T[] AddExtraTableSlots<T>(params T[][] tables) where T : EncounterArea, new()
{
return tables.SelectMany(s => s).GroupBy(l => l.Location)
.Select(t => t.Count() == 1
? t.First() // only one table, just return the area
: new T { Location = t.Key, Slots = t.SelectMany(s => s.Slots).ToArray() })
.ToArray();
}
/// <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>
internal static void MarkEncountersStaticMagnetPull<T>(IEnumerable<EncounterArea> areas, PersonalTable t)
where T : EncounterSlot, IMagnetStatic
{
foreach (EncounterArea area in areas)
{
foreach (var grp in area.Slots.Cast<T>().GroupBy(z => z.Type))
MarkEncountersStaticMagnetPull(grp, t);
}
}
internal static void MarkEncountersStaticMagnetPull<T>(IEnumerable<T> grp, PersonalTable t)
where T : EncounterSlot, IMagnetStatic
{
GetStaticMagnet(t, grp, out List<T> s, out List<T> m);
for (var i = 0; i < s.Count; i++)
{
var slot = s[i];
slot.StaticIndex = i;
slot.StaticCount = s.Count;
}
for (var i = 0; i < m.Count; i++)
{
var slot = m[i];
slot.MagnetPullIndex = i;
slot.MagnetPullCount = m.Count;
}
}
internal static void MarkEncountersStaticMagnetPullPermutation<T>(IEnumerable<T> grp, PersonalTable t, List<T> permuted)
where T : EncounterSlot, IMagnetStatic, INumberedSlot
{
GetStaticMagnet(t, grp, out List<T> s, out List<T> 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.StaticIndex >= 0) // already has unique data
{
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 = (T)slot.Clone();
permuted.Add(slot);
}
slot.StaticIndex = i;
slot.StaticCount = s.Count;
}
for (int i = 0; i < m.Count; i++)
{
var slot = m[i];
if (slot.MagnetPullIndex >= 0) // already has unique data
{
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 = (T)slot.Clone();
permuted.Add(slot);
}
slot.MagnetPullIndex = i;
slot.MagnetPullCount = m.Count;
}
}
private static void GetStaticMagnet<T>(PersonalTable t, IEnumerable<T> grp, out List<T> s, out List<T> m)
where T : EncounterSlot, IMagnetStatic
{
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<T>();
m = new List<T>();
foreach (T Slot in grp)
{
var p = t[Slot.Species];
if (p.IsType(steel))
m.Add(Slot);
if (p.IsType(electric))
s.Add(Slot);
}
}
/// <summary>
/// Sets the <see cref="EncounterSlot1.Version"/> value, for use in determining split-generation origins.
/// </summary>
/// <remarks>Only used for Gen 1 &amp; 2, as <see cref="PKM.Version"/> data is not present.</remarks>
/// <param name="areas">In-game encounter data</param>
/// <param name="game">Version ID to set</param>
internal static void MarkEncountersVersion(IEnumerable<EncounterArea> areas, GameVersion game)
{
foreach (EncounterArea area in areas)
{
foreach (var Slot in area.Slots)
Slot.Version = game;
}
}
/// <summary>
/// Sets the <see cref="IGenerationSet.Generation"/> value.
/// </summary>
@ -182,37 +36,8 @@ namespace PKHeX.Core
enc.Generation = generation;
}
/// <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{T}"/>, this method combines a single array.</remarks>
/// <param name="Areas">Ingame encounter data</param>
internal static void ReduceAreasSize<T>(ref T[] Areas) where T : EncounterArea, new()
{
Areas = Areas.GroupBy(a => a.Location).Select(a => new T
{
Location = a.Key,
Slots = a.SelectMany(m => m.Slots).ToArray()
}).ToArray();
}
internal static T[] ConcatAll<T>(params IEnumerable<T>[] arr) => arr.SelectMany(z => z).ToArray();
internal static void MarkEncounterAreaArray<T>(params T[][] areas) where T : EncounterArea
{
foreach (var area in areas)
MarkEncounterAreas(area);
}
private static void MarkEncounterAreas<T>(params T[] areas) where T : EncounterArea
{
foreach (var area in areas)
{
foreach (var slot in area.Slots)
slot.Area = area;
}
}
internal static EncounterStatic Clone(this EncounterStatic s, int location)
{
var result = s.Clone();
@ -255,11 +80,5 @@ namespace PKHeX.Core
}
string[] getNames(int i, IEnumerable<string[]> names) => names.Select(z => z.Length > i ? z[i] : string.Empty).ToArray();
}
internal static void MarkEncounterGame<T>(IEnumerable<T> table, GameVersion version) where T: IVersion, IVersionSet
{
foreach (var t in table.Where(z => ((IVersion)z).Version == GameVersion.Any))
((IVersionSet)t).Version = version;
}
}
}

View file

@ -1,5 +1,4 @@
using System;
using static PKHeX.Core.EncounterUtil;
using static PKHeX.Core.EncounterUtil;
using static PKHeX.Core.GameVersion;
namespace PKHeX.Core
@ -9,13 +8,16 @@ namespace PKHeX.Core
/// </summary>
internal static class Encounters1
{
internal static readonly EncounterArea1[] SlotsRBY;
internal static readonly EncounterStatic1[] StaticRBY;
private static readonly EncounterArea1[] SlotsR = Get("red", "g1", RD);
private static readonly EncounterArea1[] SlotsB = Get("blue", "g1", BU);
private static readonly EncounterArea1[] SlotsY = Get("yellow", "g1", YW);
internal static readonly EncounterArea1[] SlotsRBY = ArrayUtil.ConcatAll(SlotsR, SlotsB, SlotsY);
private static EncounterArea1[] Get(string name, string ident, GameVersion game) =>
EncounterArea1.GetAreas(BinLinker.Unpack(Util.GetBinaryResource($"encounter_{name}.pkl"), ident), game);
static Encounters1()
{
StaticRBY = Encounter_RBY;
SlotsRBY = GetAreas();
MarkEncountersGeneration(1, StaticRBY, TradeGift_RBY_NoTradeback, TradeGift_RBY_Tradeback);
var trades = ArrayUtil.ConcatAll(TradeGift_RBY_NoTradeback, TradeGift_RBY_Tradeback);
@ -23,34 +25,7 @@ namespace PKHeX.Core
t.TrainerNames = StringConverter12.G1TradeOTName;
}
private static EncounterArea1[] GetAreas()
{
var red_gw = EncounterArea1.GetArray1GrassWater(Util.GetBinaryResource("encounter_red.pkl"), 248);
var blu_gw = EncounterArea1.GetArray1GrassWater(Util.GetBinaryResource("encounter_blue.pkl"), 248);
var ylw_gw = EncounterArea1.GetArray1GrassWater(Util.GetBinaryResource("encounter_yellow.pkl"), 249);
var rb_fish = EncounterArea1.GetArray1Fishing(Util.GetBinaryResource("encounter_rb_f.pkl"), 33);
var ylw_fish = EncounterArea1.GetArray1FishingYellow(Util.GetBinaryResource("encounter_yellow_f.pkl"));
MarkEncountersVersion(red_gw, RD);
MarkEncountersVersion(blu_gw, BU);
MarkEncountersVersion(ylw_gw, YW);
MarkEncountersVersion(rb_fish, RB);
MarkEncountersVersion(ylw_fish, YW);
var table = AddExtraTableSlots(red_gw, blu_gw, ylw_gw, rb_fish, ylw_fish);
Array.Resize(ref table, table.Length + 1);
table[table.Length - 1] = FishOldGood_RBY;
foreach (var arr in table)
{
foreach (var slot in arr.Slots)
slot.Area = arr;
}
return table;
}
private static readonly EncounterStatic1[] Encounter_RBY =
internal static readonly EncounterStatic1[] StaticRBY =
{
// GameVersion is RBY for Pokemon with the same catch rate and initial moves in all games
// If there are any differences in moves or catch rate, they will be defined as different encounters (GameVersion)
@ -187,16 +162,5 @@ namespace PKHeX.Core
new EncounterTrade1(051, 05, RBY), // Dugtrio - Trade Lickitung (GSC 5)
new EncounterTrade1(047, 05, RBY), // Parasect - Trade Tangela (GSC 5)
});
private static readonly EncounterArea1 FishOldGood_RBY = new EncounterArea1
{
Location = -1,
Slots = new EncounterSlot[]
{
new EncounterSlot1(129, 05, 05, -1, SlotType.Old_Rod, 0) { Version = RBY }, // Magikarp
new EncounterSlot1(118, 10, 10, -1, SlotType.Good_Rod, 1) { Version = RBY }, // Goldeen
new EncounterSlot1(060, 10, 10, -1, SlotType.Good_Rod, 2) { Version = RBY }, // Poliwag
}
};
}
}

View file

@ -9,124 +9,28 @@ namespace PKHeX.Core
/// </summary>
internal static class Encounters2
{
internal static readonly EncounterArea2[] SlotsGSC, SlotsGS, SlotsC;
internal static readonly EncounterStatic2[] StaticGSC, StaticGS, StaticC;
private static readonly EncounterArea2[] SlotsG = Get("gold", "g2", GameVersion.GD);
private static readonly EncounterArea2[] SlotsS = Get("silver", "g2", GameVersion.SV);
internal static readonly EncounterArea2[] SlotsC = Get("crystal", "g2", GameVersion.C);
internal static readonly EncounterArea2[] SlotsGS = ArrayUtil.ConcatAll(SlotsG, SlotsS);
internal static readonly EncounterArea2[] SlotsGSC = ArrayUtil.ConcatAll(SlotsGS, SlotsC);
private static readonly TreesArea[] HeadbuttTreesC = TreesArea.GetArray(BinLinker.Unpack(Util.GetBinaryResource("trees_h_c.pkl"), "ch"));
private static EncounterArea2[] Get(string name, string ident, GameVersion game) =>
EncounterArea2.GetAreas(BinLinker.Unpack(Util.GetBinaryResource($"encounter_{name}.pkl"), ident), game);
static Encounters2()
{
StaticGS = Encounter_GS;
StaticC = Encounter_C;
StaticGSC = Encounter_GSC;
SlotsGS = GetTables2(GameVersion.GS);
SlotsC = GetTables2(GameVersion.C);
SlotsGSC = GetTables2(GameVersion.GSC);
MarkEncounterAreaArray(SlotsGS, SlotsC, SlotsGSC, EncounterSafari_GSC, EncounterBCC_GSC);
ReduceAreasSize(ref SlotsGS);
ReduceAreasSize(ref SlotsC);
ReduceAreasSize(ref SlotsGSC);
MarkEncountersGeneration(2, StaticGS, StaticC, StaticGSC, TradeGift_GSC);
MarkEncounterTradeStrings(TradeGift_GSC, TradeGift_GSC_OTs);
SlotsGSC.SetVersion(GameVersion.GSC);
SlotsGS.SetVersion(GameVersion.GS);
SlotsC.SetVersion(GameVersion.C);
StaticGSC.SetVersion(GameVersion.GSC);
StaticGS.SetVersion(GameVersion.GS);
StaticC.SetVersion(GameVersion.C);
TradeGift_GSC.SetVersion(GameVersion.GSC);
}
private static EncounterArea2[] GetTables2(GameVersion Version)
{
// Fishing
var f = EncounterArea2.GetArray2Fishing(Util.GetBinaryResource("encounter_gsc_f.pkl"));
var Slots = Array.Empty<EncounterArea2>();
if (Version.Contains(GameVersion.GS))
Slots = GetSlots_GS(f);
if (Version.Contains(GameVersion.C))
Slots = AddExtraTableSlots(Slots, GetSlots_C(f));
return Slots;
}
private static EncounterArea2[] GetSlots_GS(EncounterArea2[] f)
{
// Grass/Water
var g = EncounterArea2.GetArray2GrassWater(Util.GetBinaryResource("encounter_gold.pkl"));
var s = EncounterArea2.GetArray2GrassWater(Util.GetBinaryResource("encounter_silver.pkl"));
// Headbutt/Rock Smash
var h_g = EncounterArea2.GetArray2Headbutt(Util.GetBinaryResource("encounter_gold_h.pkl"));
var h_s = EncounterArea2.GetArray2Headbutt(Util.GetBinaryResource("encounter_silver_h.pkl"));
var safari_gs = EncounterSafari_GSC;
var bcc_gs = EncounterBCC_GSC;
MarkEncountersVersion(bcc_gs, GameVersion.GS);
MarkEncountersVersion(f, GameVersion.GS);
MarkEncountersVersion(g, GameVersion.GD);
MarkEncountersVersion(s, GameVersion.SV);
MarkEncountersVersion(h_g, GameVersion.GD);
MarkEncountersVersion(h_s, GameVersion.SV);
MarkEncountersVersion(safari_gs, GameVersion.GS);
return AddExtraTableSlots(g, s, h_g, h_s, f, bcc_gs, safari_gs);
}
private static EncounterArea2[] GetSlots_C(EncounterArea2[] f)
{
// Grass/Water
var c = EncounterArea2.GetArray2GrassWater(Util.GetBinaryResource("encounter_crystal.pkl"));
// Headbutt/Rock Smash
var h_c = EncounterArea2.GetArray2Headbutt(Util.GetBinaryResource("encounter_crystal_h.pkl"));
var safari_c = EncounterSafari_GSC;
var bcc_c = EncounterBCC_GSC;
MarkEncountersVersion(bcc_c, GameVersion.C);
MarkEncountersVersion(safari_c, GameVersion.C);
MarkEncountersVersion(f, GameVersion.C);
MarkEncountersVersion(c, GameVersion.C);
MarkEncountersVersion(h_c, GameVersion.C);
var extra = AddExtraTableSlots(c, h_c, f, bcc_c, safari_c);
return extra;
}
private static readonly EncounterArea2[] EncounterBCC_GSC = { new EncounterArea2 {
Location = 19,
Slots = new EncounterSlot[]
{
new EncounterSlot2(010, 07, 18, 20, SlotType.BugContest, 0), // Caterpie
new EncounterSlot2(013, 07, 18, 20, SlotType.BugContest, 1), // Weedle
new EncounterSlot2(011, 09, 18, 10, SlotType.BugContest, 2), // Metapod
new EncounterSlot2(014, 09, 18, 10, SlotType.BugContest, 3), // Kakuna
new EncounterSlot2(012, 12, 15, 05, SlotType.BugContest, 4), // Butterfree
new EncounterSlot2(015, 12, 15, 05, SlotType.BugContest, 5), // Beedrill
new EncounterSlot2(048, 10, 16, 10, SlotType.BugContest, 6), // Venonat
new EncounterSlot2(046, 10, 17, 10, SlotType.BugContest, 7), // Paras
new EncounterSlot2(123, 13, 14, 05, SlotType.BugContest, 8), // Scyther
new EncounterSlot2(127, 13, 14, 05, SlotType.BugContest, 9), // Pinsir
}
}};
private static readonly EncounterArea2[] EncounterSafari_GSC = { new EncounterArea2 {
Location = 81,
Slots = new EncounterSlot[]
{
new EncounterSlot2(129, 10, 10, 100, SlotType.Old_Rod_Safari, 0), // Magikarp
new EncounterSlot2(098, 10, 10, 100, SlotType.Old_Rod_Safari, 1), // Krabby
new EncounterSlot2(098, 20, 20, 100, SlotType.Good_Rod_Safari, 0), // Krabby
new EncounterSlot2(129, 20, 20, 100, SlotType.Good_Rod_Safari, 1), // Magikarp
new EncounterSlot2(222, 20, 20, 100, SlotType.Good_Rod_Safari, 2), // Corsola
new EncounterSlot2(120, 20, 20, 100, SlotType.Good_Rod_Safari, 3), // Staryu
new EncounterSlot2(098, 40, 40, 100, SlotType.Super_Rod_Safari, 0), // Krabby
new EncounterSlot2(222, 40, 40, 100, SlotType.Super_Rod_Safari, 1), // Corsola
new EncounterSlot2(120, 40, 40, 100, SlotType.Super_Rod_Safari, 2), // Staryu
new EncounterSlot2(121, 40, 40, 100, SlotType.Super_Rod_Safari, 3), // Kingler
}
}};
private static readonly EncounterStatic2[] Encounter_GSC_Common =
{
new EncounterStatic2(152, 05) { Location = 001, Version = GameVersion.GSC }, // Chikorita @ New Bark Town
@ -267,9 +171,13 @@ namespace PKHeX.Core
if (Area == null) // Failsafe, every area with headbutt encounters has a tree area
return TreeEncounterAvailable.Impossible;
var table = Area.GetTrees(encounter.Type);
var table = Area.GetTrees(encounter.Area.Type);
var trainerpivot = TID % 10;
return table[trainerpivot];
}
internal static readonly EncounterStatic2[] StaticGSC = Encounter_GSC;
internal static readonly EncounterStatic2[] StaticGS = Encounter_GS;
internal static readonly EncounterStatic2[] StaticC = Encounter_C;
}
}

View file

@ -1,6 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using static PKHeX.Core.EncounterUtil;
using static PKHeX.Core.EncounterUtil;
using static PKHeX.Core.Encounters3Teams;
namespace PKHeX.Core
@ -10,112 +8,30 @@ namespace PKHeX.Core
/// </summary>
internal static class Encounters3
{
internal static readonly EncounterArea3[] SlotsR, SlotsS, SlotsE;
internal static readonly EncounterArea3[] SlotsFR, SlotsLG;
internal static readonly EncounterStatic3[] StaticR, StaticS, StaticE;
internal static readonly EncounterStatic3[] StaticFR, StaticLG;
private const int SafariLocation_RSE = 57;
private const int SafariLocation_FRLG = 136;
private static readonly EncounterArea3[] SlotsSwarmRSE = Get("rse_swarm", "rs", GameVersion.RSE);
internal static readonly EncounterArea3[] SlotsR = ArrayUtil.ConcatAll(Get("r", "ru", GameVersion.R), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsS = ArrayUtil.ConcatAll(Get("s", "sa", GameVersion.S), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsE = ArrayUtil.ConcatAll(Get("e", "em", GameVersion.E), SlotsSwarmRSE);
internal static readonly EncounterArea3[] SlotsFR = Get("fr", "fr", GameVersion.FR);
internal static readonly EncounterArea3[] SlotsLG = Get("lg", "lg", GameVersion.LG);
private static EncounterArea3[] Get(string resource, string ident, GameVersion game)
=> EncounterArea3.GetAreas(BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident), game);
static Encounters3()
{
StaticR = GetEncounters(Encounter_RSE, GameVersion.R);
StaticS = GetEncounters(Encounter_RSE, GameVersion.S);
StaticE = GetEncounters(Encounter_RSE, GameVersion.E);
StaticFR = GetEncounters(Encounter_FRLG, GameVersion.FR);
StaticLG = GetEncounters(Encounter_FRLG, GameVersion.LG);
static EncounterArea3[] get(string resource, string ident)
=> EncounterArea3.GetArray3(BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident));
var R_Slots = get("r", "ru");
var S_Slots = get("s", "sa");
var E_Slots = get("e", "em");
var FR_Slots = get("fr", "fr");
var LG_Slots = get("lg", "lg");
MarkEncounterAreaArray(SlotsRSEAlt, SlotsFRLGUnown, SlotsXD);
ReduceAreasSize(ref R_Slots);
ReduceAreasSize(ref S_Slots);
ReduceAreasSize(ref E_Slots);
MarkG3Slots_FRLG(ref FR_Slots);
MarkG3Slots_FRLG(ref LG_Slots);
MarkG3SlotsSafariZones(ref R_Slots, SafariLocation_RSE);
MarkG3SlotsSafariZones(ref S_Slots, SafariLocation_RSE);
MarkG3SlotsSafariZones(ref E_Slots, SafariLocation_RSE);
MarkG3SlotsSafariZones(ref FR_Slots, SafariLocation_FRLG);
MarkG3SlotsSafariZones(ref LG_Slots, SafariLocation_FRLG);
MarkEncountersStaticMagnetPull<EncounterSlot3>(E_Slots, PersonalTable.E);
SlotsR = AddExtraTableSlots(R_Slots, SlotsRSEAlt);
SlotsS = AddExtraTableSlots(S_Slots, SlotsRSEAlt);
SlotsE = AddExtraTableSlots(E_Slots, SlotsRSEAlt);
SlotsFR = AddExtraTableSlots(FR_Slots, SlotsFRLGUnown);
SlotsLG = AddExtraTableSlots(LG_Slots, SlotsFRLGUnown);
MarkEncountersGeneration(3, StaticR, StaticS, StaticE, StaticFR, StaticLG, Encounter_CXD, TradeGift_RSE, TradeGift_FRLG);
MarkEncounterTradeStrings(TradeGift_RSE, TradeRSE);
MarkEncounterTradeStrings(TradeGift_FRLG, TradeFRLG);
SlotsRSEAlt.SetVersion(GameVersion.RSE);
SlotsFRLGUnown.SetVersion(GameVersion.FRLG);
SlotsR.SetVersion(GameVersion.R);
SlotsS.SetVersion(GameVersion.S);
SlotsE.SetVersion(GameVersion.E);
SlotsFR.SetVersion(GameVersion.FR);
SlotsLG.SetVersion(GameVersion.LG);
Encounter_RSE.SetVersion(GameVersion.RSE);
Encounter_FRLG.SetVersion(GameVersion.FRLG);
TradeGift_RSE.SetVersion(GameVersion.RSE);
TradeGift_FRLG.SetVersion(GameVersion.FRLG);
Encounter_Colo.SetVersion(GameVersion.COLO);
Encounter_XD.SetVersion(GameVersion.XD);
SlotsXD.SetVersion(GameVersion.XD);
}
private static void MarkG3Slots_FRLG(ref EncounterArea3[] Areas)
{
// Remove slots for unown, those slots does not contains alt form info, it will be added manually in SlotsRFLGAlt
// Group areas by location id, the raw data have areas with different slots but the same location id
Areas = Areas.Where(a => a.Location < 188 || a.Location > 194).GroupBy(a => a.Location).Select(a => new EncounterArea3
{
Location = a.First().Location,
Slots = a.SelectMany(m => m.Slots).ToArray()
}).ToArray();
}
private static void MarkG3SlotsSafariZones(ref EncounterArea3[] Areas, int location)
{
foreach (var Area in Areas.Where(a => a.Location == location))
{
foreach (EncounterSlot Slot in Area.Slots)
Slot.Type |= SlotType.Safari;
}
}
private static readonly int[] Roaming_MetLocation_FRLG =
{
//Route 1-25 encounter is possible either in grass or on water
101,102,103,104,105,106,107,108,109,110,
111,112,113,114,115,116,117,118,119,120,
121,122,123,124,125
};
private static readonly int[] Roaming_MetLocation_RSE =
{
//Roaming encounter is possible in tall grass and on water
//Route 101-138
16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47, 48, 49,
};
private static readonly EncounterStatic3[] Encounter_RSE_Roam =
{
new EncounterStatic3 { Species = 380, Level = 40, Version = GameVersion.S, Roaming = true }, // Latias
@ -230,8 +146,8 @@ namespace PKHeX.Core
new EncounterStatic3 { Species = 386, Level = 30, Location = 187, Version = GameVersion.LG, Form = 2, Fateful = true }, // Deoxys @ Birth Island
};
private static readonly EncounterStatic3[] Encounter_RSE = Encounter_RSE_Roam.SelectMany(e => e.Clone(Roaming_MetLocation_RSE)).Concat(Encounter_RSE_Regular).ToArray();
private static readonly EncounterStatic3[] Encounter_FRLG = Encounter_FRLG_Roam.SelectMany(e => e.Clone(Roaming_MetLocation_FRLG)).Concat(Encounter_FRLG_Stationary).ToArray();
private static readonly EncounterStatic3[] Encounter_RSE = ArrayUtil.ConcatAll(Encounter_RSE_Roam, Encounter_RSE_Regular);
private static readonly EncounterStatic3[] Encounter_FRLG = ArrayUtil.ConcatAll(Encounter_FRLG_Roam, Encounter_FRLG_Stationary);
private static readonly int[] TradeContest_Cool = { 30, 05, 05, 05, 05, 10 };
private static readonly int[] TradeContest_Beauty = { 05, 30, 05, 05, 05, 10 };
@ -273,89 +189,6 @@ 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 = { 117, 106, 073 }; /* Bide, Harden, Leech Seed */
private static readonly int[] MoveSwarmNuzleaf = { 106, 074, 267, 073 }; /* 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 =
{
// Swarm can be passed from R/S<->E via mixing records
// Encounter Percent is a 50% call
new EncounterArea3 {
Location = 17, // Route 102
Slots = new EncounterSlot[]
{
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 EncounterSlot[]
{
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 EncounterSlot[]
{
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 EncounterSlot[]
{
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 EncounterSlot[]
{
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
new EncounterArea3 {
Location = 34, // Route 119
Slots = new[]
{
new EncounterSlot3 { Species = 349, LevelMin = 20, LevelMax = 25, Type = SlotType.Swarm } // Feebas with any Rod (50%)
},},
};
private static readonly EncounterArea3[] SlotsFRLGUnown =
{
GetUnownArea(188, new[] { 00,00,00,00,00,00,00,00,00,00,00,27 }), // 188 = Monean Chamber
GetUnownArea(189, new[] { 02,02,02,03,03,03,07,07,07,20,20,14 }), // 189 = Liptoo Chamber
GetUnownArea(190, new[] { 13,13,13,13,18,18,18,18,08,08,04,04 }), // 190 = Weepth Chamber
GetUnownArea(191, new[] { 15,15,11,11,09,09,17,17,17,16,16,16 }), // 191 = Dilford Chamber
GetUnownArea(192, new[] { 24,24,19,19,06,06,06,05,05,05,10,10 }), // 192 = Scufib Chamber
GetUnownArea(193, new[] { 21,21,21,22,22,22,23,23,12,12,01,01 }), // 193 = Rixy Chamber
GetUnownArea(194, new[] { 25,25,25,25,25,25,25,25,25,25,25,26 }), // 194 = Viapois Chamber
};
private static EncounterArea3 GetUnownArea(int location, IReadOnlyList<int> SlotForms)
{
return new EncounterArea3
{
Location = location,
Slots = SlotForms.Select((_, i) => new EncounterSlot3
{
Species = 201, LevelMin = 25, LevelMax = 25, Type = SlotType.Grass,
SlotNumber = i,
Form = SlotForms[i]
}).ToArray()
};
}
#endregion
#region Colosseum
private static readonly EncounterStatic3[] Encounter_Colo =
{
@ -465,15 +298,6 @@ namespace PKHeX.Core
#region XD
private static readonly int[] MirorBXDLocations =
{
090, // Rock
091, // Oasis
092, // Cave
113, // Pyrite Town
059, // Realgam Tower
};
private static readonly EncounterStatic3[] Encounter_XD = new[]
{
new EncounterStatic3 { Fateful = true, Gift = true, Species = 133, Level = 10, Location = 000, Moves = new[] {044} }, // Eevee (Bite)
@ -597,44 +421,23 @@ namespace PKHeX.Core
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 =
internal static readonly EncounterArea3XD[] SlotsXD =
{
new EncounterArea3 { Location = 090, Slots = new[] // Rock
{
new EncounterSlot3PokeSpot(027, 10, 23, 0), // Sandshrew
new EncounterSlot3PokeSpot(207, 10, 20, 1), // Gligar
new EncounterSlot3PokeSpot(328, 10, 20, 2), // Trapinch
}
},
new EncounterArea3 { Location = 091, Slots = new[] // Oasis
{
new EncounterSlot3PokeSpot(187, 10, 20, 0), // Hoppip
new EncounterSlot3PokeSpot(231, 10, 20, 1), // Phanpy
new EncounterSlot3PokeSpot(283, 10, 20, 2), // Surskit
}
},
new EncounterArea3 { Location = 092, Slots = new[] // Cave
{
new EncounterSlot3PokeSpot(041, 10, 21, 0), // Zubat
new EncounterSlot3PokeSpot(304, 10, 21, 1), // Aron
new EncounterSlot3PokeSpot(194, 10, 21, 2), // Wooper
}
},
new EncounterArea3XD(90, 027, 23, 207, 20, 328, 20), // Rock (Sandshrew, Gligar, Trapinch)
new EncounterArea3XD(91, 187, 20, 231, 20, 283, 20), // Oasis (Hoppip, Phanpy, Surskit)
new EncounterArea3XD(92, 041, 21, 304, 21, 194, 21), // Cave (Zubat, Aron, Wooper)
};
internal static readonly EncounterStatic3[] Encounter_CXD = ArrayUtil.ConcatAll(Encounter_Colo, Encounter_XD);
private static IEnumerable<EncounterStatic3> CloneMirorB(EncounterStatic3 arg)
{
yield return arg;
if (!(arg is EncounterStaticShadow s))
yield break;
foreach (int loc in MirorBXDLocations)
yield return (EncounterStatic3)s.Clone(loc);
}
#endregion
internal static readonly EncounterStatic3[] StaticR = GetEncounters(Encounter_RSE, GameVersion.R);
internal static readonly EncounterStatic3[] StaticS = GetEncounters(Encounter_RSE, GameVersion.S);
internal static readonly EncounterStatic3[] StaticE = GetEncounters(Encounter_RSE, GameVersion.E);
internal static readonly EncounterStatic3[] StaticFR = GetEncounters(Encounter_FRLG, GameVersion.FR);
internal static readonly EncounterStatic3[] StaticLG = GetEncounters(Encounter_FRLG, GameVersion.LG);
}
}

File diff suppressed because it is too large Load diff

View file

@ -9,41 +9,14 @@ namespace PKHeX.Core
/// </summary>
public static class Encounters5
{
internal static readonly EncounterArea5[] SlotsB, SlotsW, SlotsB2, SlotsW2;
internal static readonly EncounterStatic5[] StaticB, StaticW, StaticB2, StaticW2;
internal static readonly EncounterArea5[] SlotsB = EncounterArea5.GetAreas(Get("b", "51"), GameVersion.B);
internal static readonly EncounterArea5[] SlotsW = EncounterArea5.GetAreas(Get("w", "51"), GameVersion.W);
internal static readonly EncounterArea5[] SlotsB2 = EncounterArea5.GetAreas(Get("b2", "52"), GameVersion.B2);
internal static readonly EncounterArea5[] SlotsW2 = EncounterArea5.GetAreas(Get("w2", "52"), GameVersion.W2);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
static Encounters5()
{
MarkG5DreamWorld(ref BW_DreamWorld);
MarkG5DreamWorld(ref B2W2_DreamWorld);
var staticbw = Encounter_BW.Concat(BW_DreamWorld).ToArray();
var staticb2w2 = Encounter_B2W2.Concat(B2W2_DreamWorld).ToArray();
StaticB = GetEncounters(staticbw, GameVersion.B);
StaticW = GetEncounters(staticbw, GameVersion.W);
StaticB2 = GetEncounters(staticb2w2, GameVersion.B2);
StaticW2 = GetEncounters(staticb2w2, GameVersion.W2);
var BSlots = GetEncounterTables<EncounterArea5, EncounterSlot5>("51", "b");
var WSlots = GetEncounterTables<EncounterArea5, EncounterSlot5>("51", "w");
var B2Slots = GetEncounterTables<EncounterArea5, EncounterSlot5>("52", "b2");
var W2Slots = GetEncounterTables<EncounterArea5, EncounterSlot5>("52", "w2");
MarkG5Slots(ref BSlots);
MarkG5Slots(ref WSlots);
MarkG5Slots(ref B2Slots);
MarkG5Slots(ref W2Slots);
MarkBWSwarmSlots(SlotsB_Swarm);
MarkBWSwarmSlots(SlotsW_Swarm);
MarkB2W2SwarmSlots(SlotsB2_Swarm);
MarkB2W2SwarmSlots(SlotsW2_Swarm);
MarkG5HiddenGrottoSlots(SlotsB2_HiddenGrotto);
MarkG5HiddenGrottoSlots(SlotsW2_HiddenGrotto);
MarkEncounterAreaArray(SlotsB_Swarm, SlotsW_Swarm, SlotsB2_Swarm, SlotsW2_Swarm, SlotsB2_HiddenGrotto, SlotsW2_HiddenGrotto, WhiteForestSlot);
SlotsB = AddExtraTableSlots(BSlots, SlotsB_Swarm);
SlotsW = AddExtraTableSlots(WSlots, SlotsW_Swarm, WhiteForestSlot);
SlotsB2 = AddExtraTableSlots(B2Slots, SlotsB2_Swarm, SlotsB2_HiddenGrotto);
SlotsW2 = AddExtraTableSlots(W2Slots, SlotsW2_Swarm, SlotsW2_HiddenGrotto);
MarkEncountersGeneration(5, StaticB, StaticW, StaticB2, StaticW2, TradeGift_BW, TradeGift_B2W2);
MarkEncounterTradeStrings(TradeGift_BW, TradeBW);
@ -53,52 +26,23 @@ namespace PKHeX.Core
BW_DreamWorld.SetVersion(GameVersion.BW);
B2W2_DreamWorld.SetVersion(GameVersion.B2W2);
SlotsB.SetVersion(GameVersion.B);
SlotsW.SetVersion(GameVersion.W);
SlotsB2.SetVersion(GameVersion.B2);
SlotsW2.SetVersion(GameVersion.W2);
Encounter_BW.SetVersion(GameVersion.BW);
Encounter_B2W2.SetVersion(GameVersion.B2W2);
TradeGift_BW.SetVersion(GameVersion.BW);
TradeGift_B2W2.SetVersion(GameVersion.B2W2);
}
private static void MarkBWSwarmSlots(EncounterArea5[] Areas)
{
foreach (EncounterSlot s in Areas.SelectMany(area => area.Slots))
{
s.LevelMin = 15; s.LevelMax = 55; s.Type = SlotType.Swarm;
}
}
private static void MarkB2W2SwarmSlots(EncounterArea5[] Areas)
{
foreach (EncounterSlot s in Areas.SelectMany(area => area.Slots))
{
s.LevelMin = 40; s.LevelMax = 55; s.Type = SlotType.Swarm;
}
}
private static void MarkG5HiddenGrottoSlots(EncounterArea5[] Areas)
{
foreach (EncounterSlot s in Areas[0].Slots) //Only 1 area
s.Type = SlotType.HiddenGrotto;
}
private static void MarkG5DreamWorld(ref EncounterStatic5[] t)
private static EncounterStatic5[] MarkG5DreamWorld(EncounterStatic5[] t)
{
// Split encounters with multiple permitted special moves -- a pkm can only be obtained with 1 of the special moves!
var list = new List<EncounterStatic5>();
foreach (EncounterStatic5 s in t)
{
s.Location = 075; // Entree Forest
var p = (PersonalInfoBW)PersonalTable.B2W2[s.Species];
s.Ability = p.HasHiddenAbility ? 4 : 1;
s.Shiny = Shiny.Never;
}
// Split encounters with multiple permitted special moves -- a pkm can only be obtained with 1 of the special moves!
var list = new List<EncounterStatic5>();
foreach (EncounterStatic5 s in t)
{
if (s.Moves.Count <= 1) // no special moves
{
list.Add(s);
@ -113,40 +57,7 @@ namespace PKHeX.Core
list.Add(clone);
}
}
t = list.ToArray();
}
private static void MarkG5Slots(ref EncounterArea5[] Areas)
{
foreach (var area in Areas)
{
int ctr = 0;
do
{
for (int i = 0; i < 12; i++)
area.Slots[ctr++].Type = SlotType.Grass; // Single
for (int i = 0; i < 12; i++)
area.Slots[ctr++].Type = SlotType.Grass; // Double
for (int i = 0; i < 12; i++)
area.Slots[ctr++].Type = SlotType.Grass; // Shaking
for (int i = 0; i < 5; i++) // 5
area.Slots[ctr++].Type = SlotType.Surf; // Surf
for (int i = 0; i < 5; i++) // 5
area.Slots[ctr++].Type = SlotType.Surf; // Surf Spot
for (int i = 0; i < 5; i++) // 5
area.Slots[ctr++].Type = SlotType.Super_Rod; // Fish
for (int i = 0; i < 5; i++) // 5
area.Slots[ctr++].Type = SlotType.Super_Rod; // Fish Spot
} while (ctr != area.Slots.Length);
area.Slots = area.Slots.Where(slot => slot.Species != 0).ToArray();
}
ReduceAreasSize(ref Areas);
return list.ToArray();
}
#region Dream Radar Tables
@ -360,7 +271,7 @@ namespace PKHeX.Core
new EncounterStatic5 { Species = 376, Level = 45, Moves = new[]{038}, Gender = 2, }, // Metagross
};
public static readonly EncounterStatic5[] BW_DreamWorld = DreamWorld_Common.Concat(new[]
public static readonly EncounterStatic5[] BW_DreamWorld = MarkG5DreamWorld(DreamWorld_Common.Concat(new[]
{
// Pleasant Forest
new EncounterStatic5 { Species = 029, Level = 10, Moves = new[]{010, 389, 162}, }, // Nidoran♀
@ -459,9 +370,9 @@ namespace PKHeX.Core
new EncounterStatic5 { Species = 242, Level = 10 }, // Blissey
new EncounterStatic5 { Species = 448, Level = 10, Moves = new[]{418}, Gender = 0, }, // Lucario
new EncounterStatic5 { Species = 189, Level = 27, Moves = new[]{206}, Gender = 0, }, // Jumpluff
}).ToArray();
}).ToArray());
public static readonly EncounterStatic5[] B2W2_DreamWorld = DreamWorld_Common.Concat(new[]
public static readonly EncounterStatic5[] B2W2_DreamWorld = MarkG5DreamWorld(DreamWorld_Common.Concat(new[]
{
// Pleasant Forest
new EncounterStatic5 { Species = 535, Level = 10, Moves = new[]{496, 414, 352}, }, // Tympole
@ -523,7 +434,7 @@ namespace PKHeX.Core
new EncounterStatic5 { Species = 390, Level = 10, Moves = new[]{252}, Gender = 0, }, // Chimchar
new EncounterStatic5 { Species = 393, Level = 10, Moves = new[]{297}, Gender = 0, }, // Piplup
new EncounterStatic5 { Species = 575, Level = 32, Moves = new[]{286}, Gender = 0, }, // Gothorita
}).ToArray();
}).ToArray());
#endregion
#region Static Encounter/Gift Tables
@ -750,233 +661,10 @@ namespace PKHeX.Core
internal static readonly EncounterTrade[] TradeGift_B2W2 = TradeGift_B2W2_Regular.Concat(TradeGift_B2W2_YancyCurtis).ToArray();
#endregion
#region Alt Slots
// White forest white version only
private static readonly int[] WhiteForest_GrassSpecies =
{
016, 029, 032, 043, 063, 066, 069, 081, 092, 111,
137, 175, 179, 187, 239, 240, 265, 270, 273, 280,
287, 293, 298, 304, 328, 371, 396, 403, 406, 440,
};
private static readonly int[] WhiteForest_SurfSpecies =
{
194, 270, 283, 341,
};
private static readonly EncounterArea5[] WhiteForestSlot = EncounterArea.GetSimpleEncounterArea<EncounterArea5, EncounterSlot5>(WhiteForest_GrassSpecies, new[] { 5, 5 }, 51, SlotType.Grass).Concat(
EncounterArea.GetSimpleEncounterArea<EncounterArea5, EncounterSlot5>(WhiteForest_SurfSpecies, new[] { 5, 5 }, 51, SlotType.Surf)).ToArray();
private static readonly EncounterArea5[] SlotsBW_Swarm =
{
// Level Range and Slot Type will be marked later
new EncounterArea5 { Location = 014, Slots = new[]{new EncounterSlot5 { Species = 083 }, }, }, // Farfetch'd @ Route 1
new EncounterArea5 { Location = 015, Slots = new[]{new EncounterSlot5 { Species = 360 }, }, }, // Wynaut @ Route 2
new EncounterArea5 { Location = 017, Slots = new[]{new EncounterSlot5 { Species = 449 }, }, }, // Hippopotas @ Route 4
new EncounterArea5 { Location = 018, Slots = new[]{new EncounterSlot5 { Species = 235 }, }, }, // Smeargle @ Route 5
new EncounterArea5 { Location = 020, Slots = new[]{new EncounterSlot5 { Species = 161 }, }, }, // Sentret @ Route 7
new EncounterArea5 { Location = 021, Slots = new[]{new EncounterSlot5 { Species = 453 }, }, }, // Croagunk @ Route 8
new EncounterArea5 { Location = 023, Slots = new[]{new EncounterSlot5 { Species = 236 }, }, }, // Tyrogue @ Route 10
new EncounterArea5 { Location = 025, Slots = new[]{new EncounterSlot5 { Species = 084 }, }, }, // Doduo @ Route 12
new EncounterArea5 { Location = 026, Slots = new[]{new EncounterSlot5 { Species = 353 }, }, }, // Shuppet @ Route 13
new EncounterArea5 { Location = 027, Slots = new[]{new EncounterSlot5 { Species = 193 }, }, }, // Yanma @ Route 14
new EncounterArea5 { Location = 028, Slots = new[]{new EncounterSlot5 { Species = 056 }, }, }, // Mankey @ Route 15
new EncounterArea5 { Location = 029, Slots = new[]{new EncounterSlot5 { Species = 204 }, }, }, // Pineco @ Route 16
new EncounterArea5 { Location = 031, Slots = new[]{new EncounterSlot5 { Species = 102 }, }, }, // Exeggcute @ Route 18
};
private static readonly EncounterArea5[] SlotsB_Swarm = SlotsBW_Swarm.Concat(new[] {
new EncounterArea5 { Location = 016, Slots = new[]{new EncounterSlot5 { Species = 313 }, }, }, // Volbeat @ Route 3
new EncounterArea5 { Location = 019, Slots = new[]{new EncounterSlot5 { Species = 311 }, }, }, // Plusle @ Route 6
new EncounterArea5 { Location = 022, Slots = new[]{new EncounterSlot5 { Species = 228 }, }, }, // Houndour @ Route 9
new EncounterArea5 { Location = 024, Slots = new[]{new EncounterSlot5 { Species = 285 }, }, }, // Shroomish @ Route 11
}).ToArray();
private static readonly EncounterArea5[] SlotsW_Swarm = SlotsBW_Swarm.Concat(new[] {
new EncounterArea5 { Location = 016, Slots = new[]{new EncounterSlot5 { Species = 314 }, }, }, // Illumise @ Route 3
new EncounterArea5 { Location = 019, Slots = new[]{new EncounterSlot5 { Species = 312 }, }, }, // Minun @ Route 6
new EncounterArea5 { Location = 022, Slots = new[]{new EncounterSlot5 { Species = 261 }, }, }, // Poochyena @ Route 9
new EncounterArea5 { Location = 024, Slots = new[]{new EncounterSlot5 { Species = 046 }, }, }, // Paras @ Route 11
}).ToArray();
private static readonly EncounterArea5[] SlotsB2W2_Swarm =
{
// Level Range and Slot Type will be marked later
new EncounterArea5 { Location = 014, Slots = new[]{new EncounterSlot5 { Species = 083 }, }, }, // Farfetch'd @ Route 1
new EncounterArea5 { Location = 018, Slots = new[]{new EncounterSlot5 { Species = 177 }, }, }, // Natu @ Route 5
new EncounterArea5 { Location = 020, Slots = new[]{new EncounterSlot5 { Species = 162 }, }, }, // Furret @ Route 7
new EncounterArea5 { Location = 021, Slots = new[]{new EncounterSlot5 { Species = 195 }, }, }, // Quagsire @ Route 8
new EncounterArea5 { Location = 022, Slots = new[]{new EncounterSlot5 { Species = 317 }, }, }, // Swalot @ Route 9
new EncounterArea5 { Location = 024, Slots = new[]{new EncounterSlot5 { Species = 284 }, }, }, // Masquerain @ Route 11
new EncounterArea5 { Location = 025, Slots = new[]{new EncounterSlot5 { Species = 084 }, }, }, // Doduo @ Route 12
new EncounterArea5 { Location = 026, Slots = new[]{new EncounterSlot5 { Species = 277 }, }, }, // Swellow @ Route 13
new EncounterArea5 { Location = 028, Slots = new[]{new EncounterSlot5 { Species = 022 }, }, }, // Fearow @ Route 15
new EncounterArea5 { Location = 029, Slots = new[]{new EncounterSlot5 { Species = 204 }, }, }, // Pineco @ Route 16
new EncounterArea5 { Location = 031, Slots = new[]{new EncounterSlot5 { Species = 187 }, }, }, // Hoppip @ Route 18
new EncounterArea5 { Location = 032, Slots = new[]{new EncounterSlot5 { Species = 097 }, }, }, // Hypno @ Dreamyard
new EncounterArea5 { Location = 034, Slots = new[]{new EncounterSlot5 { Species = 450 }, }, }, // Hippowdon @ Desert Resort
new EncounterArea5 { Location = 070, Slots = new[]{new EncounterSlot5 { Species = 079 }, }, }, // Slowpoke @ Abundant shrine
new EncounterArea5 { Location = 132, Slots = new[]{new EncounterSlot5 { Species = 332 }, }, }, // Cacturne @ Reaversal Mountian
};
private static readonly EncounterArea5[] SlotsB2_Swarm = SlotsB2W2_Swarm.Concat(new[] {
new EncounterArea5 { Location = 016, Slots = new[]{new EncounterSlot5 { Species = 313 }, }, }, // Volbeat @ Route 3
new EncounterArea5 { Location = 019, Slots = new[]{new EncounterSlot5 { Species = 311 }, }, }, // Plusle @ Route 6
new EncounterArea5 { Location = 125, Slots = new[]{new EncounterSlot5 { Species = 185 }, }, }, // Sudowoodo @ Route 20
new EncounterArea5 { Location = 127, Slots = new[]{new EncounterSlot5 { Species = 168 }, }, }, // Ariados @ Route 22
}).ToArray();
private static readonly EncounterArea5[] SlotsW2_Swarm = SlotsB2W2_Swarm.Concat(new[] {
new EncounterArea5 { Location = 016, Slots = new[]{new EncounterSlot5 { Species = 314 }, }, }, // Illumise @ Route 3
new EncounterArea5 { Location = 019, Slots = new[]{new EncounterSlot5 { Species = 312 }, }, }, // Minun @ Route 6
new EncounterArea5 { Location = 125, Slots = new[]{new EncounterSlot5 { Species = 122 }, }, }, // Mr. Mime @ Route 20
new EncounterArea5 { Location = 127, Slots = new[]{new EncounterSlot5 { Species = 166 }, }, }, // Ledian @ Route 22
}).ToArray();
private static readonly EncounterSlot[] SlotsB2W2_HiddenGrottoEncounterSlots =
{
// reference http://bulbapedia.bulbagarden.net/wiki/Hidden_Grotto
// Route 2
new EncounterSlot5 { Species = 029, LevelMin = 55, LevelMax = 60, }, // Nidoran♀
new EncounterSlot5 { Species = 032, LevelMin = 55, LevelMax = 60, }, // Nidoran♂
new EncounterSlot5 { Species = 210, LevelMin = 55, LevelMax = 60, }, // Granbull
new EncounterSlot5 { Species = 505, LevelMin = 55, LevelMax = 60, }, // Watchog
// Route 3
new EncounterSlot5 { Species = 310, LevelMin = 55, LevelMax = 60, }, // Manectric @ Dark Grass
new EncounterSlot5 { Species = 417, LevelMin = 55, LevelMax = 60, }, // Pachirisu @ Dark Grass
new EncounterSlot5 { Species = 523, LevelMin = 55, LevelMax = 60, }, // Zebstrika @ Dark Grass
new EncounterSlot5 { Species = 048, LevelMin = 55, LevelMax = 60, }, // Venonat @ Pond
new EncounterSlot5 { Species = 271, LevelMin = 55, LevelMax = 60, }, // Lombre @ Pond
new EncounterSlot5 { Species = 400, LevelMin = 55, LevelMax = 60, }, // Bibarel @ Pond
// Route 5
new EncounterSlot5 { Species = 510, LevelMin = 20, LevelMax = 25, }, // Liepard
new EncounterSlot5 { Species = 572, LevelMin = 20, LevelMax = 25, }, // Minccino
new EncounterSlot5 { Species = 590, LevelMin = 20, LevelMax = 25, }, // Foongus
// Route 6
new EncounterSlot5 { Species = 206, LevelMin = 25, LevelMax = 30, }, // Dunsparce @ Near PKM Breeder
new EncounterSlot5 { Species = 299, LevelMin = 25, LevelMax = 30, }, // Nosepass @ Mistralton Cave
new EncounterSlot5 { Species = 527, LevelMin = 25, LevelMax = 30, }, // Woobat @ Both
new EncounterSlot5 { Species = 590, LevelMin = 25, LevelMax = 30, }, // Foongus @ Both
// Route 7
new EncounterSlot5 { Species = 335, LevelMin = 30, LevelMax = 35, }, // Zangoose
new EncounterSlot5 { Species = 336, LevelMin = 30, LevelMax = 35, }, // Seviper
new EncounterSlot5 { Species = 505, LevelMin = 30, LevelMax = 35, }, // Watchog
new EncounterSlot5 { Species = 613, LevelMin = 30, LevelMax = 35, }, // Cubchoo
// Route 9
new EncounterSlot5 { Species = 089, LevelMin = 35, LevelMax = 40, }, // Muk
new EncounterSlot5 { Species = 510, LevelMin = 35, LevelMax = 40, }, // Liepard
new EncounterSlot5 { Species = 569, LevelMin = 35, LevelMax = 40, }, // Garbodor
new EncounterSlot5 { Species = 626, LevelMin = 35, LevelMax = 40, }, // Bouffalant
// Route 13
new EncounterSlot5 { Species = 114, LevelMin = 35, LevelMax = 40, }, // Tangela @ Gaint Chasm
new EncounterSlot5 { Species = 363, LevelMin = 35, LevelMax = 40, }, // Spheal @ Stairs
new EncounterSlot5 { Species = 425, LevelMin = 35, LevelMax = 40, }, // Drifloon @ Stairs
new EncounterSlot5 { Species = 451, LevelMin = 35, LevelMax = 40, }, // Skorupi @ Gaint Chasm
new EncounterSlot5 { Species = 590, LevelMin = 35, LevelMax = 40, }, // Foongus @ Both
// Route 18
new EncounterSlot5 { Species = 099, LevelMin = 55, LevelMax = 60, }, // Kingler
new EncounterSlot5 { Species = 149, LevelMin = 55, LevelMax = 60, }, // Dragonite
new EncounterSlot5 { Species = 222, LevelMin = 55, LevelMax = 60, }, // Corsola
new EncounterSlot5 { Species = 441, LevelMin = 55, LevelMax = 60, }, // Chatot
// Pinwheel Forest
new EncounterSlot5 { Species = 061, LevelMin = 55, LevelMax = 60, }, // Poliwhirl @ Outer
new EncounterSlot5 { Species = 198, LevelMin = 55, LevelMax = 60, }, // Murkrow @ Inner
new EncounterSlot5 { Species = 286, LevelMin = 55, LevelMax = 60, }, // Breloom @ Inner
new EncounterSlot5 { Species = 297, LevelMin = 55, LevelMax = 60, }, // Hariyama @ Outer
new EncounterSlot5 { Species = 308, LevelMin = 55, LevelMax = 60, }, // Medicham @ Outer
new EncounterSlot5 { Species = 371, LevelMin = 55, LevelMax = 60, }, // Bagon @ Outer
new EncounterSlot5 { Species = 591, LevelMin = 55, LevelMax = 60, }, // Amoonguss @ Inner
// Giant Chasm
new EncounterSlot5 { Species = 035, LevelMin = 45, LevelMax = 50, }, // Clefairy
new EncounterSlot5 { Species = 132, LevelMin = 45, LevelMax = 50, }, // Ditto
new EncounterSlot5 { Species = 215, LevelMin = 45, LevelMax = 50, }, // Sneasel
new EncounterSlot5 { Species = 375, LevelMin = 45, LevelMax = 50, }, // Metang
// Abundant Shrine
new EncounterSlot5 { Species = 037, LevelMin = 35, LevelMax = 40, }, // Vulpix @ Near Youngster
new EncounterSlot5 { Species = 055, LevelMin = 35, LevelMax = 40, }, // Golduck @ Shrine
new EncounterSlot5 { Species = 333, LevelMin = 35, LevelMax = 40, }, // Swablu @ Shrine
new EncounterSlot5 { Species = 436, LevelMin = 35, LevelMax = 40, }, // Bronzor @ Near Youngster
new EncounterSlot5 { Species = 591, LevelMin = 35, LevelMax = 40, }, // Amoonguss @ Both
// Lostlorn Forest
new EncounterSlot5 { Species = 127, LevelMin = 20, LevelMax = 25, }, // Pinsir
new EncounterSlot5 { Species = 214, LevelMin = 20, LevelMax = 25, }, // Heracross
new EncounterSlot5 { Species = 415, LevelMin = 20, LevelMax = 25, }, // Combee
new EncounterSlot5 { Species = 542, LevelMin = 20, LevelMax = 25, }, // Leavanny
// Route 22
new EncounterSlot5 { Species = 279, LevelMin = 40, LevelMax = 45, }, // Pelipper
new EncounterSlot5 { Species = 591, LevelMin = 40, LevelMax = 45, }, // Amoonguss
new EncounterSlot5 { Species = 619, LevelMin = 40, LevelMax = 45, }, // Mienfoo
// Route 23
new EncounterSlot5 { Species = 055, LevelMin = 50, LevelMax = 55, }, // Golduck
new EncounterSlot5 { Species = 207, LevelMin = 50, LevelMax = 55, }, // Gligar
new EncounterSlot5 { Species = 335, LevelMin = 50, LevelMax = 55, }, // Zangoose
new EncounterSlot5 { Species = 336, LevelMin = 50, LevelMax = 55, }, // Seviper
new EncounterSlot5 { Species = 359, LevelMin = 50, LevelMax = 55, }, // Absol
// Floccesy Ranch
new EncounterSlot5 { Species = 183, LevelMin = 10, LevelMax = 15, }, // Marill
new EncounterSlot5 { Species = 206, LevelMin = 10, LevelMax = 15, }, // Dunsparce
new EncounterSlot5 { Species = 507, LevelMin = 10, LevelMax = 15, }, // Herdier
// Funfest Missions
// todo : check the level
new EncounterSlot5 { Species = 133, LevelMin = 15, LevelMax = 60, }, // Eevee
new EncounterSlot5 { Species = 134, LevelMin = 15, LevelMax = 60, }, // Vaporeon
new EncounterSlot5 { Species = 135, LevelMin = 15, LevelMax = 60, }, // Jolteon
new EncounterSlot5 { Species = 136, LevelMin = 15, LevelMax = 60, }, // Flareon
new EncounterSlot5 { Species = 196, LevelMin = 15, LevelMax = 60, }, // Espeon
new EncounterSlot5 { Species = 197, LevelMin = 15, LevelMax = 60, }, // Umbreon
new EncounterSlot5 { Species = 470, LevelMin = 15, LevelMax = 60, }, // Leafeon
new EncounterSlot5 { Species = 471, LevelMin = 15, LevelMax = 60, }, // Glaceon
// Funfest Week 3
// new EncounterSlot5 { Species = 060, LevelMin = 15, LevelMax = 60, }, // Poliwag
new EncounterSlot5 { Species = 113, LevelMin = 15, LevelMax = 60, }, // Chansey
new EncounterSlot5 { Species = 176, LevelMin = 15, LevelMax = 60, }, // Togetic
new EncounterSlot5 { Species = 082, LevelMin = 15, LevelMax = 60, }, // Magneton
new EncounterSlot5 { Species = 148, LevelMin = 15, LevelMax = 60, }, // Dragonair
new EncounterSlot5 { Species = 372, LevelMin = 15, LevelMax = 60, }, // Shelgon
};
private static readonly EncounterArea5[] SlotsB2_HiddenGrotto =
{
new EncounterArea5
{
Location = 143, // Hidden Grotto
Slots = SlotsB2W2_HiddenGrottoEncounterSlots.Concat(new[]{
new EncounterSlot5 { Species = 015, LevelMin = 55, LevelMax = 60 }, // Beedrill @ Pinwheel Forest
new EncounterSlot5 { Species = 434, LevelMin = 15, LevelMax = 60 }, // Stunky from Funfest Missions
}).ToArray(),
}
};
private static readonly EncounterArea5[] SlotsW2_HiddenGrotto =
{
new EncounterArea5
{
Location = 143, // Hidden Grotto
Slots = SlotsB2W2_HiddenGrottoEncounterSlots.Concat(new[]{
new EncounterSlot5 { Species = 012, LevelMin = 55, LevelMax = 60 }, // Butterfree @ Pinwheel Forest
new EncounterSlot5 { Species = 431, LevelMin = 15, LevelMax = 60 }, // Glameow from Funfest Missions
}).ToArray(),
}
};
#endregion
internal static readonly EncounterStatic5[] StaticB = GetEncounters(ArrayUtil.ConcatAll(Encounter_BW, BW_DreamWorld), GameVersion.B);
internal static readonly EncounterStatic5[] StaticW = GetEncounters(ArrayUtil.ConcatAll(Encounter_BW, BW_DreamWorld), GameVersion.W);
internal static readonly EncounterStatic5[] StaticB2 = GetEncounters(ArrayUtil.ConcatAll(Encounter_B2W2, B2W2_DreamWorld), GameVersion.B2);
internal static readonly EncounterStatic5[] StaticW2 = GetEncounters(ArrayUtil.ConcatAll(Encounter_B2W2, B2W2_DreamWorld), GameVersion.W2);
}
}

View file

@ -9,151 +9,32 @@ namespace PKHeX.Core
/// </summary>
internal static class Encounters6
{
internal static readonly EncounterArea6XY[] SlotsX, SlotsY;
internal static readonly EncounterArea6AO[] SlotsA, SlotsO;
internal static readonly EncounterStatic[] StaticX, StaticY, StaticA, StaticO;
internal static readonly ILookup<int, EncounterSlot6XY> FriendSafari;
private static readonly EncounterArea6XY FriendSafari = new EncounterArea6XY(Legal.FriendSafari);
internal static readonly EncounterArea6XY[] SlotsX = ArrayUtil.ConcatAll(EncounterArea6XY.GetAreas(Get("x", "xy"), GameVersion.X), new[] { FriendSafari });
internal static readonly EncounterArea6XY[] SlotsY = ArrayUtil.ConcatAll(EncounterArea6XY.GetAreas(Get("y", "xy"), GameVersion.Y), new[] { FriendSafari });
internal static readonly EncounterArea6AO[] SlotsA = EncounterArea6AO.GetAreas(Get("a", "ao"), GameVersion.AS);
internal static readonly EncounterArea6AO[] SlotsO = EncounterArea6AO.GetAreas(Get("o", "ao"), GameVersion.OR);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
static Encounters6()
{
StaticX = GetEncounters(Encounter_XY, GameVersion.X);
StaticY = GetEncounters(Encounter_XY, GameVersion.Y);
StaticA = GetEncounters(Encounter_AO, GameVersion.AS);
StaticO = GetEncounters(Encounter_AO, GameVersion.OR);
var XSlots = GetEncounterTables<EncounterArea6XY, EncounterSlot6XY>("xy", "x");
var YSlots = GetEncounterTables<EncounterArea6XY, EncounterSlot6XY>("xy", "y");
MarkG6XYSlots(ref XSlots);
MarkG6XYSlots(ref YSlots);
MarkEncounterAreaArray(SlotsXYAlt);
SlotsX = AddExtraTableSlots(XSlots, SlotsXYAlt);
SlotsY = AddExtraTableSlots(YSlots, SlotsXYAlt);
SlotsA = GetEncounterTables<EncounterArea6AO, EncounterSlot6AO>("ao", "a");
SlotsO = GetEncounterTables<EncounterArea6AO, EncounterSlot6AO>("ao", "o");
MarkG6AOSlots(ref SlotsA);
MarkG6AOSlots(ref SlotsO);
MarkEncountersGeneration(6, StaticX, StaticY, StaticA, StaticO, TradeGift_XY, TradeGift_AO);
FriendSafari = EncounterArea6XYFriendSafari.GetArea();
MarkEncounterTradeStrings(TradeGift_XY, TradeXY);
MarkEncounterTradeStrings(TradeGift_AO, TradeAO);
SlotsXYAlt.SetVersion(GameVersion.XY);
SlotsX.SetVersion(GameVersion.X);
SlotsY.SetVersion(GameVersion.Y);
SlotsA.SetVersion(GameVersion.AS);
SlotsO.SetVersion(GameVersion.OR);
Encounter_XY.SetVersion(GameVersion.XY);
Encounter_AO.SetVersion(GameVersion.ORAS);
TradeGift_XY.SetVersion(GameVersion.XY);
TradeGift_AO.SetVersion(GameVersion.ORAS);
}
private static void MarkG6XYSlots(ref EncounterArea6XY[] Areas)
{
foreach (var area in Areas)
{
int slotct = area.Slots.Length;
for (int i = slotct - 15; i < slotct; i++)
area.Slots[i].Type = SlotType.Horde;
}
ReduceAreasSize(ref Areas);
}
private static void MarkG6AOSlots(ref EncounterArea6AO[] Areas)
{
foreach (var area in Areas)
{
for (int i = 32; i < 37; i++)
area.Slots[i].Type = SlotType.Rock_Smash;
int slotct = area.Slots.Length;
for (int i = slotct - 15; i < slotct; i++)
area.Slots[i].Type = SlotType.Horde;
for (int i = 0; i < slotct; i++)
((EncounterSlot6AO)area.Slots[i]).AllowDexNav = area.Slots[i].Type != SlotType.Rock_Smash;
}
ReduceAreasSize(ref Areas);
}
private const string tradeXY = "tradexy";
private const string tradeAO = "tradeao";
private static readonly string[][] TradeXY = Util.GetLanguageStrings8(tradeXY);
private static readonly string[][] TradeAO = Util.GetLanguageStrings8(tradeAO);
#region XY Alt Slots
private static readonly EncounterArea6XY[] SlotsXYAlt =
{
new EncounterArea6XY {
Location = 104, // Victory Road
Slots = new[]
{
// Drops
new EncounterSlot6XY { Species = 075, LevelMin = 57, LevelMax = 57, Form = 0 }, // Graveler
new EncounterSlot6XY { Species = 168, LevelMin = 58, LevelMax = 59, Form = 0 }, // Ariados
new EncounterSlot6XY { Species = 714, LevelMin = 57, LevelMax = 59, Form = 0 }, // Noibat
// Swoops
new EncounterSlot6XY { Species = 022, LevelMin = 57, LevelMax = 59, Form = 0 }, // Fearow
new EncounterSlot6XY { Species = 227, LevelMin = 57, LevelMax = 59, Form = 0 }, // Skarmory
new EncounterSlot6XY { Species = 635, LevelMin = 59, LevelMax = 59, Form = 0 }, // Hydreigon
},},
new EncounterArea6XY {
Location = 34, // Route 6
Slots = new[]
{
// Rustling Bush
new EncounterSlot6XY { Species = 543, LevelMin = 10, LevelMax = 12, Form = 0 }, // Venipede
new EncounterSlot6XY { Species = 531, LevelMin = 10, LevelMax = 12, Form = 0 }, // Audino
},},
new EncounterArea6XY { Location = 38, // Route 7
Slots = new[]
{
// Berry Field
new EncounterSlot6XY { Species = 165, LevelMin = 14, LevelMax = 15, Form = 0 }, // Ledyba
new EncounterSlot6XY { Species = 313, LevelMin = 14, LevelMax = 15, Form = 0 }, // Volbeat
new EncounterSlot6XY { Species = 314, LevelMin = 14, LevelMax = 15, Form = 0 }, // Illumise
new EncounterSlot6XY { Species = 412, LevelMin = 14, LevelMax = 15, Form = 0 }, // Burmy
new EncounterSlot6XY { Species = 415, LevelMin = 14, LevelMax = 15, Form = 0 }, // Combee
new EncounterSlot6XY { Species = 665, LevelMin = 14, LevelMax = 15, Form = 30 }, // Spewpa
},},
new EncounterArea6XY { Location = 88, // Route 18
Slots = new[]
{
// Rustling Bush
new EncounterSlot6XY { Species = 632, LevelMin = 44, LevelMax = 46, Form = 0 }, // Durant
new EncounterSlot6XY { Species = 631, LevelMin = 45, LevelMax = 45, Form = 0 }, // Heatmor
},},
new EncounterArea6XY { Location = 132, // Glittering Cave
Slots = new[]
{
// Drops
new EncounterSlot6XY { Species = 527, LevelMin = 15, LevelMax = 17, Form = 0 }, // Woobat
new EncounterSlot6XY { Species = 597, LevelMin = 15, LevelMax = 17, Form = 0 }, // Ferroseed
},},
new EncounterArea6XY { Location = 56, // Reflection Cave
Slots = new[]
{
// Drops
new EncounterSlot6XY { Species = 527, LevelMin = 21, LevelMax = 23, Form = 0 }, // Woobat
new EncounterSlot6XY { Species = 597, LevelMin = 21, LevelMax = 23, Form = 0 }, // Ferroseed
},},
new EncounterArea6XY { Location = 140, // Terminus Cave
Slots = new[]
{
// Drops
new EncounterSlot6XY { Species = 168, LevelMin = 44, LevelMax = 46, Form = 0 }, // Ariados
new EncounterSlot6XY { Species = 714, LevelMin = 44, LevelMax = 46, Form = 0 }, // Noibat
},},
};
#endregion
#region Static Encounter/Gift Tables
private static readonly EncounterStatic[] Encounter_XY =
{
@ -362,5 +243,10 @@ namespace PKHeX.Core
new EncounterTrade6(07,4,10,319) { Species = 222, Level = 50, Ability = 4, TID = 00325, IVs = new[] {31,-1,-1,-1,-1,31}, Gender = 1, Nature = Nature.Calm, }, // Corsola
};
#endregion
internal static readonly EncounterStatic[] StaticX = GetEncounters(Encounter_XY, GameVersion.X);
internal static readonly EncounterStatic[] StaticY = GetEncounters(Encounter_XY, GameVersion.Y);
internal static readonly EncounterStatic[] StaticA = GetEncounters(Encounter_AO, GameVersion.AS);
internal static readonly EncounterStatic[] StaticO = GetEncounters(Encounter_AO, GameVersion.OR);
}
}

View file

@ -1,5 +1,4 @@
using System.Linq;
using static PKHeX.Core.EncounterUtil;
using static PKHeX.Core.EncounterUtil;
namespace PKHeX.Core
{
@ -8,72 +7,25 @@ namespace PKHeX.Core
/// </summary>
internal static class Encounters7
{
internal static readonly EncounterArea7[] SlotsSN, SlotsMN, SlotsUS, SlotsUM;
internal static readonly EncounterStatic7[] StaticSN, StaticMN, StaticUS, StaticUM;
internal static readonly EncounterArea7[] SlotsSN = EncounterArea7.GetAreas(Get("sn", "sm"), GameVersion.SN);
internal static readonly EncounterArea7[] SlotsMN = EncounterArea7.GetAreas(Get("mn", "sm"), GameVersion.MN);
internal static readonly EncounterArea7[] SlotsUS = EncounterArea7.GetAreas(Get("us", "uu"), GameVersion.US);
internal static readonly EncounterArea7[] SlotsUM = EncounterArea7.GetAreas(Get("um", "uu"), GameVersion.UM);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
static Encounters7()
{
StaticSN = GetEncounters(Encounter_SM, GameVersion.SN);
StaticMN = GetEncounters(Encounter_SM, GameVersion.MN);
StaticUS = GetEncounters(Encounter_USUM, GameVersion.US);
StaticUM = GetEncounters(Encounter_USUM, GameVersion.UM);
var REG_SN = GetEncounterTables<EncounterArea7, EncounterSlot7>("sm", "sn");
var REG_MN = GetEncounterTables<EncounterArea7, EncounterSlot7>("sm", "mn");
var SOS_SN = GetEncounterTables<EncounterArea7, EncounterSlot7>("sm", "sn_sos");
var SOS_MN = GetEncounterTables<EncounterArea7, EncounterSlot7>("sm", "mn_sos");
MarkG7REGSlots(ref REG_SN);
MarkG7REGSlots(ref REG_MN);
MarkG7SMSlots(ref SOS_SN);
MarkG7SMSlots(ref SOS_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, EncounterSlot7>("uu", "us");
var REG_UM = GetEncounterTables<EncounterArea7, EncounterSlot7>("uu", "um");
var SOS_US = GetEncounterTables<EncounterArea7, EncounterSlot7>("uu", "us_sos");
var SOS_UM = GetEncounterTables<EncounterArea7, EncounterSlot7> ("uu", "um_sos");
MarkG7REGSlots(ref REG_US);
MarkG7REGSlots(ref REG_UM);
MarkG7SMSlots(ref SOS_US);
MarkG7SMSlots(ref SOS_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,
p_sn, p_mn,
p_us, p_um);
MarkEncountersGeneration(7, StaticSN, StaticMN, StaticUS, StaticUM, TradeGift_SM, TradeGift_USUM);
MarkEncounterTradeStrings(TradeGift_SM, TradeSM);
MarkEncounterTradeStrings(TradeGift_USUM, TradeUSUM);
SlotsSN.SetVersion(GameVersion.SN);
SlotsMN.SetVersion(GameVersion.MN);
SlotsUS.SetVersion(GameVersion.US);
SlotsUM.SetVersion(GameVersion.UM);
Encounter_SM.SetVersion(GameVersion.SM);
Encounter_USUM.SetVersion(GameVersion.USUM);
TradeGift_SM.SetVersion(GameVersion.SM);
TradeGift_USUM.SetVersion(GameVersion.USUM);
}
private static void MarkG7REGSlots(ref EncounterArea7[] Areas)
{
ReduceAreasSize(ref Areas);
}
private static void MarkG7SMSlots(ref EncounterArea7[] Areas)
{
foreach (EncounterSlot s in Areas.SelectMany(area => area.Slots))
s.Type = SlotType.SOS;
ReduceAreasSize(ref Areas);
}
private static readonly EncounterStatic7[] Encounter_SM = // @ a\1\5\5
{
// Gifts - 0.bin
@ -408,53 +360,9 @@ namespace PKHeX.Core
private static readonly string[][] TradeSM = Util.GetLanguageStrings10(tradeSM);
private static readonly string[][] TradeUSUM = Util.GetLanguageStrings10(tradeUSUM);
private static void InitializePelagoSM(int[] minLevels, out EncounterArea7[] sn, out EncounterArea7[] mn)
{
int[][] speciesSM =
{
new[] {627/*SN*/, 021, 041, 090, 278, 731}, // 1-7
new[] {064, 081, 092, 198, 426, 703}, // 11-17
new[] {060, 120, 127, 661, 709, 771}, // 21-27
new[] {227, 375, 707}, // 37-43
new[] {123, 131, 429, 587}, // 49-55
};
sn = GetPelagoArea(speciesSM, minLevels);
speciesSM[0][0] = 629; // Rufflet -> Vullaby
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
new[] {079, 120, 222, 122, 180, 124}, // 11-17
new[] {127, 177, 764, 163, 771, 701}, // 21-27
new[] {131, 354, 200, /* US */ 228}, // 37-43
new[] {209, 667, 357, 430}, // 49-55
};
us = GetPelagoArea(speciesUU, minLevels);
speciesUU[3][3] = 309; // Houndour -> Electrike
um = GetPelagoArea(speciesUU, minLevels);
}
private static EncounterArea7[] GetPelagoArea(int[][] species, int[] min)
{
// Species that appear at a lower level than the current table show up too.
var area = new EncounterArea7
{
Location = 30016,
Slots = species.SelectMany((_, i) =>
species.Take(1 + i).SelectMany(z => // grab current row & above
z.Select(s => new EncounterSlot7 // get slot data for each species
{
Species = s,
LevelMin = min[i],
LevelMax = min[i] + 6
}
))).ToArray(),
};
return new[] {area};
}
internal static readonly EncounterStatic7[] StaticSN = GetEncounters(Encounter_SM, GameVersion.SN);
internal static readonly EncounterStatic7[] StaticMN = GetEncounters(Encounter_SM, GameVersion.MN);
internal static readonly EncounterStatic7[] StaticUS = GetEncounters(Encounter_USUM, GameVersion.US);
internal static readonly EncounterStatic7[] StaticUM = GetEncounters(Encounter_USUM, GameVersion.UM);
}
}

View file

@ -7,20 +7,15 @@ namespace PKHeX.Core
{
internal static class Encounters7b
{
internal static readonly EncounterArea7b[] SlotsGP = GetEncounterTables<EncounterArea7b, EncounterSlot7b>("gg", "gp");
internal static readonly EncounterArea7b[] SlotsGE = GetEncounterTables<EncounterArea7b, EncounterSlot7b>("gg", "ge");
internal static readonly EncounterStatic[] StaticGP, StaticGE;
internal static readonly EncounterArea7g[] SlotsGO_GG = GetGoParkArea();
internal static readonly EncounterArea7b[] SlotsGP = EncounterArea7b.GetAreas(Get("gp", "gg"), GameVersion.GP);
internal static readonly EncounterArea7b[] SlotsGE = EncounterArea7b.GetAreas(Get("ge", "gg"), GameVersion.GE);
internal static readonly EncounterArea7g[] SlotsGO_GG = EncounterArea7g.GetArea();
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
static Encounters7b()
{
StaticGP = GetEncounters(Encounter_GG, GameVersion.GP);
StaticGE = GetEncounters(Encounter_GG, GameVersion.GE);
ManuallyAddRareSpawns(SlotsGP);
ManuallyAddRareSpawns(SlotsGE);
SlotsGP.SetVersion(GameVersion.GP);
SlotsGE.SetVersion(GameVersion.GE);
Encounter_GG.SetVersion(GameVersion.GG);
TradeGift_GG.SetVersion(GameVersion.GG);
MarkEncountersGeneration(7, StaticGP, StaticGE, TradeGift_GG);
@ -83,63 +78,7 @@ namespace PKHeX.Core
new EncounterTrade7b { Species = 074, Level = 16, Form = 1, TrainerNames = T8, TID7 = 551873, OTGender = 0, Shiny = Shiny.Random, IVs = new[] {31,31,-1,-1,-1,-1}, IsNicknamed = false }, // Geodude @ Vermilion City, AV rand [0-5)
};
private static EncounterArea7g[] GetGoParkArea()
{
var area = new EncounterArea7g { Location = 50 };
EncounterSlot GetSlot(int species, int form)
{
return new EncounterSlot7GO
{
Area = area,
Species = species,
LevelMin = 1,
LevelMax = 40,
Form = form,
Type = SlotType.GoPark,
Version = GameVersion.GO,
};
}
var obtainable = Enumerable.Range(1, 150).Concat(Enumerable.Range(808, 2)); // count : 152
var AlolanKanto = new byte[]
{
// Level 1+
019, // Rattata
020, // Raticate
027, // Sandshrew
028, // Sandslash
037, // Vulpix
038, // Ninetales
050, // Diglett
051, // Dugtrio
052, // Meowth
053, // Persian
074, // Geodude
075, // Graveler
076, // Golem
088, // Grimer
089, // Muk
103, // Exeggutor
105, // Marowak
// Level 15+
026, // Raichu
};
var regular = obtainable.Select(z => GetSlot(z, 0));
var alolan = AlolanKanto.Select(z => GetSlot(z, 1));
var slots = regular.Concat(alolan).ToArray();
slots[slots.Length - 1].LevelMin = 15; // Raichu
slots[(int)Species.Mewtwo - 1].LevelMin = 15;
slots[(int)Species.Articuno - 1].LevelMin = 15;
slots[(int)Species.Zapdos - 1].LevelMin = 15;
slots[(int)Species.Moltres - 1].LevelMin = 15;
area.Slots = slots;
return new[] {area};
}
private class RareSpawn
{
public readonly int Species;
@ -177,7 +116,7 @@ namespace PKHeX.Core
new RareSpawn(149, Sky),
};
private static void ManuallyAddRareSpawns(IEnumerable<EncounterArea> areas)
private static void ManuallyAddRareSpawns(IEnumerable<EncounterArea7b> areas)
{
foreach (var table in areas)
{
@ -188,13 +127,7 @@ namespace PKHeX.Core
var slots = table.Slots;
var first = slots[0];
var extra = species
.Select(z => new EncounterSlot7b
{
Area = table,
Species = z,
LevelMin = (z == 006 || z >= 144) ? 03 : first.LevelMin,
LevelMax = (z == 006 || z >= 144) ? 56 : first.LevelMax,
}).ToArray();
.Select(z => new EncounterSlot7b(table, z, (z == 006 || z >= 144) ? 03 : first.LevelMin, (z == 006 || z >= 144) ? 56 : first.LevelMax, GameVersion.GG)).ToArray();
int count = slots.Length;
Array.Resize(ref slots, count + extra.Length);
@ -202,5 +135,8 @@ namespace PKHeX.Core
table.Slots = slots;
}
}
internal static readonly EncounterStatic[] StaticGP = GetEncounters(Encounter_GG, GameVersion.GP);
internal static readonly EncounterStatic[] StaticGE = GetEncounters(Encounter_GG, GameVersion.GE);
}
}

View file

@ -15,19 +15,17 @@ namespace PKHeX.Core
/// </summary>
internal static class Encounters8
{
internal static readonly EncounterArea8[] SlotsSW_Symbol = GetEncounterTables8<EncounterArea8>("sw", "sw_symbol");
internal static readonly EncounterArea8[] SlotsSH_Symbol = GetEncounterTables8<EncounterArea8>("sh", "sh_symbol");
internal static readonly EncounterArea8[] SlotsSW_Hidden = GetEncounterTables8<EncounterArea8>("sw", "sw_hidden");
internal static readonly EncounterArea8[] SlotsSH_Hidden = GetEncounterTables8<EncounterArea8>("sh", "sh_hidden");
internal static readonly EncounterArea8[] SlotsSW, SlotsSH;
internal static readonly EncounterStatic[] StaticSW, StaticSH;
private static readonly EncounterArea8[] SlotsSW_Symbol = EncounterAreaSH.GetArray<EncounterArea8>(Get("sw_symbol", "sw"), SW);
private static readonly EncounterArea8[] SlotsSH_Symbol = EncounterAreaSH.GetArray<EncounterArea8>(Get("sh_symbol", "sh"), SH);
private static readonly EncounterArea8[] SlotsSW_Hidden = EncounterAreaSH.GetArray<EncounterArea8>(Get("sw_hidden", "sw"), SW);
private static readonly EncounterArea8[] SlotsSH_Hidden = EncounterAreaSH.GetArray<EncounterArea8>(Get("sh_hidden", "sh"), SH);
private static byte[][] Get(string resource, string ident) => BinLinker.Unpack(Util.GetBinaryResource($"encounter_{resource}.pkl"), ident);
internal static readonly EncounterArea8[] SlotsSW = ArrayUtil.ConcatAll(SlotsSW_Symbol, SlotsSW_Hidden);
internal static readonly EncounterArea8[] SlotsSH = ArrayUtil.ConcatAll(SlotsSH_Symbol, SlotsSH_Hidden);
static Encounters8()
{
SlotsSW = ArrayUtil.ConcatAll(SlotsSW_Symbol, SlotsSW_Hidden);
SlotsSH = ArrayUtil.ConcatAll(SlotsSH_Symbol, SlotsSH_Hidden);
SlotsSW.SetVersion(SW);
SlotsSH.SetVersion(SH);
foreach (var area in SlotsSW_Symbol)
area.PermitCrossover = true;
foreach (var area in SlotsSH_Symbol)
@ -46,12 +44,7 @@ namespace PKHeX.Core
Crystal_SWSH.SetVersion(SWSH);
MarkEncounterTradeStrings(TradeGift_SWSH, TradeSWSH);
StaticSW = GetEncounters(Encounter_SWSH, SW);
StaticSH = GetEncounters(Encounter_SWSH, SH);
// Include Nest Tables for both versions -- online play can share them across versions! In the IsMatch method we check if it's a valid share.
StaticSW = ArrayUtil.ConcatAll(Nest_Common, Nest_SW, Nest_SH, Dist_Common, Dist_SW, Dist_SH, GetEncounters(Crystal_SWSH, SW), StaticSW);
StaticSH = ArrayUtil.ConcatAll(Nest_Common, Nest_SW, Nest_SH, Dist_Common, Dist_SW, Dist_SH, GetEncounters(Crystal_SWSH, SH), StaticSH);
MarkEncountersGeneration(8, StaticSW, StaticSH, TradeGift_SWSH);
@ -610,5 +603,8 @@ namespace PKHeX.Core
};
internal static readonly EncounterTrade[] TradeGift_SWSH = TradeGift_Regular.Concat(TradeGift_R1).ToArray();
internal static readonly EncounterStatic[] StaticSW = ArrayUtil.ConcatAll(Nest_Common, Nest_SW, Nest_SH, Dist_Common, Dist_SW, Dist_SH, GetEncounters(Crystal_SWSH, SW), GetEncounters(Encounter_SWSH, SW));
internal static readonly EncounterStatic[] StaticSH = ArrayUtil.ConcatAll(Nest_Common, Nest_SW, Nest_SH, Dist_Common, Dist_SW, Dist_SH, GetEncounters(Crystal_SWSH, SH), GetEncounters(Encounter_SWSH, SH));
}
}

View file

@ -17,10 +17,10 @@ namespace PKHeX.Core
public int EggLocation { get => 0; set { } }
public override string ToString() => $"{(Species) Species} @ {LevelMin}-{LevelMax}";
internal EncounterArea? Area { get; set; }
public int Location { get => Area?.Location ?? 0; set { } }
internal readonly EncounterArea Area;
public int Location { get => Area.Location; set { } }
public SlotType Type { get; set; } = SlotType.Any;
protected EncounterSlot(EncounterArea area) => Area = area;
public EncounterSlot Clone() => (EncounterSlot)MemberwiseClone();
@ -67,9 +67,9 @@ namespace PKHeX.Core
{
get
{
if (Type == SlotType.Any)
if (Area!.Type == SlotType.Any)
return wild;
return $"{wild} {Type.ToString().Replace('_', ' ')}";
return $"{wild} {Area!.Type.ToString().Replace('_', ' ')}";
}
}
@ -93,7 +93,9 @@ namespace PKHeX.Core
pk.CurrentLevel = level;
pk.Version = (int)version;
pk.Nickname = SpeciesName.GetSpeciesNameGeneration(Species, lang, Generation);
pk.Ball = (int)Type.GetBall();
var ball = BallExtensions.GetRequiredBallValueWild(Generation, Location);
pk.Ball = (int)(ball == Ball.None ? Ball.Poke : ball);
pk.Language = lang;
pk.OT_Friendship = pk.PersonalInfo.BaseFriendship;
pk.AltForm = GetWildAltForm(pk, Form, sav);
@ -126,7 +128,7 @@ namespace PKHeX.Core
int nature = (int)criteria.GetNature(Nature.Random);
var ability = Util.Rand.Next(2);
if (Type == SlotType.HiddenGrotto) // don't force hidden for DexNav
if (Area!.Type == SlotType.HiddenGrotto) // don't force hidden for DexNav
ability = 2;
PIDGenerator.SetRandomWildPID(pk, pk.Format, nature, ability, gender);

View file

@ -8,39 +8,13 @@
public override int Generation => 1;
public int SlotNumber { get; set; }
public readonly int Rate;
public EncounterSlot1(int species, int min, int max, int rate, SlotType type, int slot)
public EncounterSlot1(EncounterArea1 area, int species, int min, int max, int slot, GameVersion game) : base(area)
{
Species = species;
LevelMin = min;
LevelMax = max;
Rate = rate;
Type = type;
SlotNumber = slot;
}
/// <summary>
/// Deserializes Gen1 Encounter Slots from data.
/// </summary>
/// <param name="data">Byte array containing complete slot data table.</param>
/// <param name="ofs">Offset to start reading from.</param>
/// <param name="count">Amount of slots to read.</param>
/// <param name="type">Type of encounter slot table.</param>
/// <param name="rate">Slot type encounter rate.</param>
/// <returns>Array of encounter slots.</returns>
public static EncounterSlot1[] ReadSlots(byte[] data, ref int ofs, int count, SlotType type, int rate)
{
var bump = type == SlotType.Surf ? 4 : 0;
var slots = new EncounterSlot1[count];
for (int slot = 0; slot < count; slot++)
{
int min = data[ofs++];
int species = data[ofs++];
int max = min + bump;
slots[slot] = new EncounterSlot1(species, min, max, rate, type, slot);
}
return slots;
Version = game;
}
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)

View file

@ -11,40 +11,13 @@
public override int Generation => 2;
public int SlotNumber { get; set; }
public int Rate;
internal EncounterTime Time;
public EncounterSlot2(int species, int min, int max, int rate, SlotType type, int slot)
public EncounterSlot2(EncounterArea2 area, int species, int min, int max, int slot, GameVersion game) : base(area)
{
Species = species;
LevelMin = min;
LevelMax = max;
Rate = rate;
Type = type;
SlotNumber = slot;
}
/// <summary>
/// Deserializes Gen2 Encounter Slots from data.
/// </summary>
/// <param name="data">Byte array containing complete slot data table.</param>
/// <param name="ofs">Offset to start reading from.</param>
/// <param name="count">Amount of slots to read.</param>
/// <param name="type">Type of encounter slot table.</param>
/// <param name="rate">Slot type encounter rate.</param>
/// <returns>Array of encounter slots.</returns>
public static EncounterSlot2[] ReadSlots(byte[] data, ref int ofs, int count, SlotType type, int rate)
{
var bump = type == SlotType.Surf ? 4 : 0;
var slots = new EncounterSlot2[count];
for (int slot = 0; slot < count; slot++)
{
int min = data[ofs++];
int species = data[ofs++];
int max = min + bump;
slots[slot] = new EncounterSlot2(species, min, max, rate, type, slot);
}
return slots;
Version = game;
}
protected override void ApplyDetails(ITrainerInfo sav, EncounterCriteria criteria, PKM pk)
@ -53,7 +26,7 @@
var pk2 = (PK2)pk;
if (Version == GameVersion.C)
pk2.Met_TimeOfDay = Time.RandomValidTime();
pk2.Met_TimeOfDay = ((EncounterArea2)Area!).Time.RandomValidTime();
}
}
}

View file

@ -4,11 +4,27 @@ namespace PKHeX.Core
{
public override int Generation => 3;
public int StaticIndex { get; set; } = -1;
public int MagnetPullIndex { get; set; } = -1;
public int StaticIndex { get; set; }
public int MagnetPullIndex { get; set; }
public int StaticCount { get; set; }
public int MagnetPullCount { get; set; }
public int SlotNumber { get; set; }
public EncounterSlot3(EncounterArea3 area, int species, int form, int min, int max, int slot, int mpi, int mpc, int sti, int stc, GameVersion game) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
SlotNumber = slot;
Version = game;
MagnetPullIndex = mpi;
MagnetPullCount = mpc;
StaticIndex = sti;
StaticCount = stc;
}
}
}

View file

@ -6,12 +6,13 @@ namespace PKHeX.Core
public int SlotNumber { get; set; }
public EncounterSlot3PokeSpot(int species, int min, int max, int slot)
public EncounterSlot3PokeSpot(EncounterArea3XD area, int species, int min, int max, int slot) : base(area)
{
Species = species;
LevelMin = min;
LevelMax = max;
SlotNumber = slot;
Version = GameVersion.XD;
}
// PokeSpot encounters always have Fateful Encounter set.

View file

@ -7,7 +7,8 @@ namespace PKHeX.Core
public override int Generation => 3;
public IReadOnlyList<int> Moves { get; }
public EncounterSlot3Swarm(IReadOnlyList<int> moves) => Moves = moves;
public EncounterSlot3Swarm(EncounterArea3 area, int species, int min, int max, int slot, GameVersion game,
IReadOnlyList<int> moves) : base(area, species, 0, min, max, slot, 0, 0, 0, 0, game) => Moves = moves;
protected override void SetEncounterMoves(PKM pk, GameVersion version, int level)
{

View file

@ -3,15 +3,31 @@ namespace PKHeX.Core
public sealed class EncounterSlot4 : EncounterSlot, IMagnetStatic, INumberedSlot
{
public override int Generation => 4;
public EncounterType TypeEncounter { get; set; } = EncounterType.None;
public EncounterType TypeEncounter => ((EncounterArea4)Area).TypeEncounter;
public int StaticIndex { get; set; } = -1;
public int MagnetPullIndex { get; set; } = -1;
public int StaticIndex { get; set; }
public int MagnetPullIndex { get; set; }
public int StaticCount { get; set; }
public int MagnetPullCount { get; set; }
public int SlotNumber { get; set; }
public EncounterSlot4(EncounterArea4 area, int species, int form, int min, int max, int slot, int mpi, int mpc, int sti, int stc, GameVersion game) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
SlotNumber = slot;
Version = game;
MagnetPullIndex = mpi;
MagnetPullCount = mpc;
StaticIndex = sti;
StaticCount = stc;
}
protected override void SetFormatSpecificData(PKM pk) => ((PK4)pk).EncounterType = TypeEncounter.GetIndex();
}
}

View file

@ -3,5 +3,14 @@ namespace PKHeX.Core
public sealed class EncounterSlot5 : EncounterSlot
{
public override int Generation => 5;
public EncounterSlot5(EncounterArea5 area, int species, int form, int min, int max, GameVersion game) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
Version = game;
}
}
}

View file

@ -4,18 +4,26 @@ namespace PKHeX.Core
{
public override int Generation => 6;
public EncounterSlot6AO(EncounterArea6AO area, int species, int form, int min, int max, GameVersion game) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
Version = game;
}
public bool Pressure { get; set; }
public bool AllowDexNav { get; set; }
public bool DexNav { get; set; }
public bool WhiteFlute { get; set; }
public bool BlackFlute { get; set; }
private bool IsDexNav => AllowDexNav && DexNav;
public bool CanDexNav => Area.Type != SlotType.Rock_Smash;
protected override void SetFormatSpecificData(PKM pk)
{
var pk6 = (PK6)pk;
if (IsDexNav)
if (CanDexNav)
{
var eggMoves = MoveEgg.GetEggMoves(pk, Species, Form, Version);
if (eggMoves.Length > 0)

View file

@ -5,6 +5,15 @@ namespace PKHeX.Core
public override int Generation => 6;
public bool Pressure { get; set; }
public EncounterSlot6XY(EncounterArea6XY area, int species, int form, int min, int max, GameVersion game) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
Version = game;
}
protected override void SetFormatSpecificData(PKM pk)
{
var pk6 = (PK6)pk;

View file

@ -2,6 +2,15 @@ namespace PKHeX.Core
{
public sealed class EncounterSlot7 : EncounterSlot
{
public EncounterSlot7(EncounterArea7 area, int species, int form, int min, int max, GameVersion game) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
Version = game;
}
public override int Generation => 7;
public bool Pressure { get; set; }
}

View file

@ -3,5 +3,14 @@ namespace PKHeX.Core
public sealed class EncounterSlot7GO : EncounterSlot
{
public override int Generation => 7;
public EncounterSlot7GO(EncounterArea7g area, int species, int form, int min, int max, GameVersion game) : base(area)
{
Species = species;
Form = form;
LevelMin = min;
LevelMax = max;
Version = game;
}
}
}

View file

@ -3,5 +3,13 @@ namespace PKHeX.Core
public sealed class EncounterSlot7b : EncounterSlot
{
public override int Generation => 7;
public EncounterSlot7b(EncounterArea7b area, int species, int min, int max, GameVersion game) : base(area)
{
Species = species;
LevelMin = min;
LevelMax = max;
Version = game;
}
}
}

View file

@ -1,4 +1,7 @@
namespace PKHeX.Core
using System;
using System.Linq;
namespace PKHeX.Core
{
public class EncounterStatic3 : EncounterStatic
{
@ -26,11 +29,39 @@
{
if (EggEncounter)
return true;
if (Location == 0)
return true;
if (pkm.Format == 3)
return Location == pkm.Met_Location;
return true; // transfer location verified later
if (pkm.Format != 3)
return true; // transfer location verified later
var met = pkm.Met_Location;
if (!Roaming)
return Location == met;
var table = Version <= GameVersion.E ? Roaming_MetLocation_RSE : Roaming_MetLocation_FRLG;
return table.Contains(met);
}
protected override void SetMetData(PKM pk, int level, DateTime today)
{
pk.Met_Level = level;
pk.Met_Location = !Roaming ? Location : (Version <= GameVersion.E ? Roaming_MetLocation_RSE : Roaming_MetLocation_FRLG)[0];
}
private static readonly int[] Roaming_MetLocation_FRLG =
{
// Route 1-25 encounter is possible either in grass or on water
101,102,103,104,105,106,107,108,109,110,
111,112,113,114,115,116,117,118,119,120,
121,122,123,124,125
};
private static readonly int[] Roaming_MetLocation_RSE =
{
// Roaming encounter is possible in tall grass and on water
// Route 101-138
16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
46, 47, 48, 49,
};
}
}

View file

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace PKHeX.Core
{
@ -24,5 +25,30 @@ namespace PKHeX.Core
public EncounterStaticShadow(TeamLock[] locks) => Locks = locks;
public EncounterStaticShadow() => Locks = Array.Empty<TeamLock>();
private static readonly int[] MirorBXDLocations =
{
090, // Rock
091, // Oasis
092, // Cave
113, // Pyrite Town
059, // Realgam Tower
};
protected override bool IsMatchLocation(PKM pkm)
{
if (pkm.Format != 3)
return true; // transfer location verified later
var met = pkm.Met_Location;
if (Version == GameVersion.XD)
{
if (met == Location)
return true;
return MirorBXDLocations.Contains(met);
}
return met == Location;
}
}
}
}

View file

@ -306,16 +306,10 @@ namespace PKHeX.Core
{ yield return z; ++ctr; }
if (ctr != 0) yield break;
if (EncounterArea6XYFriendSafari.WasFriendSafari(pkm))
{
foreach (var z in EncounterArea6XYFriendSafari.GetValidSafariEncounters(pkm))
{ yield return z; ++ctr; }
if (ctr != 0) yield break;
}
foreach (var z in GetValidWildEncounters(pkm, chain))
{ yield return z; ++ctr; }
if (ctr != 0) yield break;
foreach (var z in GetValidEncounterTrades(pkm, chain))
{ yield return z; ++ctr; }
}

View file

@ -310,14 +310,11 @@ namespace PKHeX.Core
switch (slot.Generation)
{
case 2:
if ((slot.Type & SlotType.Safari) != 0) // Safari Zone is unavailable in Gen 2.
return true;
if ((slot.Type & SlotType.Headbutt) != 0) // Unreachable Headbutt Trees.
if (slot.Area.Type == SlotType.Headbutt) // Unreachable Headbutt Trees.
return Encounters2.GetGSCHeadbuttAvailability(slot, pk.TID) != TreeEncounterAvailable.ValidTree;
break;
case 4:
if (slot.Location == 193 && slot.Type == SlotType.Surf) // Johto Route 45 surfing encounter. Unreachable Water tiles.
if (slot.Location == 193 && slot.Area.Type == SlotType.Surf) // Johto Route 45 surfing encounter. Unreachable Water tiles.
return true;
break;
}

View file

@ -24,7 +24,7 @@ namespace PKHeX.Core
private static IEnumerable<EncounterSlot> GetRawEncounterSlots(PKM pkm, IReadOnlyList<EvoCriteria> chain, GameVersion gameSource)
{
if (pkm.Egg_Location != 0)
if (pkm.Egg_Location != 0 || pkm.IsEgg)
yield break;
var possibleAreas = GetEncounterAreas(pkm, gameSource);
@ -61,39 +61,36 @@ namespace PKHeX.Core
var s = GetRawEncounterSlots(pkm, chain, gameSource);
bool IsSafariBall = pkm.Ball == (int)Ball.Safari;
bool IsSportBall = pkm.Ball == (int)Ball.Sport;
bool IsHidden = pkm.AbilityNumber == 4; // hidden Ability
int species = pkm.Species;
return s.DeferByBoolean(slot => slot.IsDeferred(species, pkm, IsSafariBall, IsSportBall, IsHidden)); // non-deferred first
return s.DeferByBoolean(slot => slot.IsDeferred(species, pkm, IsHidden)); // non-deferred first
}
public static bool IsDeferred3(this EncounterSlot slot, int currentSpecies, PKM pkm, bool IsSafariBall)
{
return slot.IsDeferredWurmple(currentSpecies, pkm)
|| slot.IsDeferredSafari(IsSafariBall);
|| slot.IsDeferredSafari3(IsSafariBall);
}
public static bool IsDeferred4(this EncounterSlot slot, int currentSpecies, PKM pkm, bool IsSafariBall, bool IsSportBall)
{
return slot.IsDeferredWurmple(currentSpecies, pkm)
|| slot.IsDeferredSafari(IsSafariBall)
|| slot.IsDeferredSafari4(IsSafariBall)
|| slot.IsDeferredSport(IsSportBall);
}
private static bool IsDeferred(this EncounterSlot slot, int currentSpecies, PKM pkm, bool IsSafariBall, bool IsSportBall, bool IsHidden)
private static bool IsDeferred(this EncounterSlot slot, int currentSpecies, PKM pkm, bool IsHidden)
{
return slot.IsDeferredWurmple(currentSpecies, pkm)
|| slot.IsDeferredHiddenAbility(IsHidden)
|| slot.IsDeferredSafari(IsSafariBall)
|| slot.IsDeferredSport(IsSportBall);
|| slot.IsDeferredHiddenAbility(IsHidden);
}
private static bool IsDeferredWurmple(this IEncounterable slot, int currentSpecies, PKM pkm) => slot.Species == (int)Species.Wurmple && currentSpecies != (int)Species.Wurmple && !WurmpleUtil.IsWurmpleEvoValid(pkm);
private static bool IsDeferredSafari(this EncounterSlot slot, bool IsSafariBall) => IsSafariBall != ((slot.Type & SlotType.Safari) != 0);
private static bool IsDeferredSport(this EncounterSlot slot, bool IsSportBall) => IsSportBall != ((slot.Type & SlotType.BugContest) != 0);
private static bool IsDeferredHiddenAbility(this EncounterSlot slot, bool IsHidden) => IsHidden != slot.IsHiddenAbilitySlot();
private static bool IsDeferredSafari3(this ILocation slot, bool IsSafariBall) => IsSafariBall != Locations.IsSafariZoneLocation3(slot.Location);
private static bool IsDeferredSafari4(this ILocation slot, bool IsSafariBall) => IsSafariBall != Locations.IsSafariZoneLocation4(slot.Location);
private static bool IsDeferredSport(this ILocation slot, bool IsSportBall) => IsSportBall != (slot.Location == Locations.BugCatchingContest4);
private static bool IsDeferredHiddenAbility(this EncounterSlot slot, bool IsHidden) => IsHidden && !slot.IsHiddenAbilitySlot();
private static IEnumerable<EncounterSlot> GetValidEncounterSlots(PKM pkm, EncounterArea loc, IReadOnlyList<EvoCriteria> chain)
{
@ -125,20 +122,13 @@ namespace PKHeX.Core
private static bool IsHiddenAbilitySlot(this EncounterSlot slot)
{
return (slot is EncounterSlot6AO ao && ao.DexNav) || slot.Type == SlotType.FriendSafari || slot.Type == SlotType.Horde || slot.Type == SlotType.SOS;
return (slot is EncounterSlot6AO ao && ao.CanDexNav) || slot.Area.Type == SlotType.FriendSafari || slot.Area.Type == SlotType.Horde || slot.Area.Type == SlotType.SOS;
}
internal static EncounterArea? GetCaptureLocation(PKM pkm)
internal static EncounterSlot? GetCaptureLocation(PKM pkm)
{
var chain = EvolutionChain.GetValidPreEvolutions(pkm, maxLevel: 100, skipChecks: true);
return (from area in GetEncounterSlots(pkm)
let slots = GetValidEncounterSlots(pkm, area, chain).ToArray()
where slots.Length != 0
select new EncounterAreaFake
{
Location = area.Location,
Slots = slots,
}).OrderBy(area => area.Slots.Min(x => x.LevelMin)).FirstOrDefault();
return GetPossible(pkm, chain).OrderBy(z => z.LevelMin).FirstOrDefault();
}
private static IEnumerable<EncounterArea> GetEncounterTable(PKM pkm, GameVersion gameSource = GameVersion.Any)

View file

@ -138,21 +138,11 @@ namespace PKHeX.Core
{
switch (item)
{
case EncounterSlot s:
var type = s.Type;
if (type == 0)
{
yield return new EncounterSummary(item);
break;
}
for (int i = 0; i < sizeof(SlotType) * 8; i++)
{
var flag = (SlotType)(1 << i);
if ((type & flag) != 0)
yield return new EncounterSummary(item, flag.ToString());
}
case EncounterSlot s when s.Area.Type != 0:
{
yield return new EncounterSummary(item, s.Area.Type.ToString());
break;
}
default:
yield return new EncounterSummary(item);

View file

@ -63,11 +63,9 @@ namespace PKHeX.Core
}
}
private static EncounterSuggestionData GetSuggestedEncounterWild(PKM pkm, EncounterArea area, int loc = -1)
private static EncounterSuggestionData GetSuggestedEncounterWild(PKM pkm, EncounterSlot first, int loc = -1)
{
var slots = area.Slots.OrderBy(s => s.LevelMin);
var first = slots.First();
var met = loc != -1 ? loc : area.Location;
var met = loc != -1 ? loc : first.Location;
return new EncounterSuggestionData(pkm, first, met);
}

View file

@ -56,15 +56,6 @@ namespace PKHeX.Core
// Gen2 Wild Encounters
private static CheckResult VerifyWildEncounterGen2(PKM pkm, EncounterSlot2 encounter)
{
switch (encounter.Type)
{
// Fishing in the beta gen 2 Safari Zone
case SlotType.Old_Rod_Safari:
case SlotType.Good_Rod_Safari:
case SlotType.Super_Rod_Safari:
return new CheckResult(Severity.Invalid, LG2InvalidTileSafari, CheckIdentifier.Encounter);
}
if (encounter.Version == GameVersion.C)
return VerifyWildEncounterCrystal(pkm, encounter);
@ -73,10 +64,9 @@ namespace PKHeX.Core
private static CheckResult VerifyWildEncounterCrystal(PKM pkm, EncounterSlot encounter)
{
switch (encounter.Type)
switch (encounter.Area.Type)
{
case SlotType.Headbutt:
case SlotType.Headbutt_Special:
return VerifyWildEncounterCrystalHeadbutt(pkm, encounter);
case SlotType.Old_Rod:
@ -261,7 +251,7 @@ namespace PKHeX.Core
switch (slot.Generation)
{
case 4:
if (slot.Location == 193 && slot.Type == SlotType.Surf) // surfing in Johto Route 45
if (slot.Location == 193 && slot.Area.Type == SlotType.Surf) // surfing in Johto Route 45
return new CheckResult(Severity.Invalid, LG4InvalidTileR45Surf, CheckIdentifier.Encounter);
break;
}

View file

@ -22,7 +22,7 @@ namespace PKHeX.Core
{
IRelearn s when s.Relearn.Count > 0 => VerifyRelearnSpecifiedMoveset(pkm, info, s.Relearn),
EncounterEgg e => VerifyRelearnEggBase(pkm, info, e),
EncounterSlot6AO z when pkm.RelearnMove1 != 0 && z.DexNav => VerifyRelearnDexNav(pkm, info),
EncounterSlot6AO z when pkm.RelearnMove1 != 0 && z.CanDexNav => VerifyRelearnDexNav(pkm, info),
_ => VerifyRelearnNone(pkm, info)
};
}

View file

@ -8,7 +8,7 @@ namespace PKHeX.Core
/// <remarks>
/// Different from <see cref="EncounterType"/>, this corresponds to the method that the <see cref="IEncounterable"/> may be encountered.</remarks>
[Flags]
public enum SlotType
public enum SlotType : byte
{
/// <summary>
/// Default (un-assigned) encounter slot type.
@ -18,125 +18,77 @@ namespace PKHeX.Core
/// <summary>
/// Slot is encountered via Grass.
/// </summary>
Grass = 1 << 00,
Grass = 1,
/// <summary>
/// Slot is encountered via Surfing.
/// </summary>
Surf = 1 << 01,
Surf = 2,
/// <summary>
/// Slot is encountered via Old Rod (Fishing).
/// </summary>
Old_Rod = 1 << 02,
Old_Rod = 3,
/// <summary>
/// Slot is encountered via Good Rod (Fishing).
/// </summary>
Good_Rod = 1 << 03,
Good_Rod = 4,
/// <summary>
/// Slot is encountered via Super Rod (Fishing).
/// </summary>
Super_Rod = 1 << 04,
Super_Rod = 5,
/// <summary>
/// Slot is encountered via Rock Smash.
/// </summary>
Rock_Smash = 1 << 05,
/// <summary>
/// Slot is encountered via a Horde.
/// </summary>
Horde = 1 << 06,
/// <summary>
/// Slot is encountered via the Friend Safari.
/// </summary>
FriendSafari = 1 << 07,
/// <summary>
/// Slot is encountered through special means. Used to signify special slots for Gen2, sometimes for later follow-up).
/// </summary>
Special = 1 << 08,
/// <summary>
/// Slot is encountered via SOS signal.
/// </summary>
SOS = 1 << 09,
/// <summary>
/// Slot is encountered in a Swarm.
/// </summary>
Swarm = 1 << 10,
Rock_Smash = 6,
/// <summary>
/// Slot is encountered via Headbutt.
/// </summary>
Headbutt = 1 << 11,
Headbutt_Special = Headbutt | Special,
/// <summary>
/// Slot is encountered via the Poké Radar.
/// </summary>
Pokeradar = 1 << 12,
Headbutt = 7,
/// <summary>
/// Slot is encountered via a Honey Tree.
/// </summary>
HoneyTree = 1 << 13,
/// <summary>
/// Slot is encountered via a Hidden Grotto.
/// </summary>
HiddenGrotto = 1 << 14,
HoneyTree = 8,
/// <summary>
/// Slot is encountered via the Bug Catching Contest.
/// </summary>
BugContest = 1 << 15,
BugContest = 9,
/// <summary>
/// Slot is encountered via the Go Park.
/// </summary>
GoPark = 1 << 16,
HiddenGrotto = 10,
GoPark = 11,
FriendSafari = 12,
Horde = 13,
// Pokeradar = 14,
SOS = 15,
// always used as a modifier to another slot type
/// <summary>
/// Slot is encountered in the Safari Zone.
/// </summary>
Safari = 1 << 30, // always used as a modifier to another slot type
Grass_Safari = Grass | Safari,
Surf_Safari = Surf | Safari,
Old_Rod_Safari = Old_Rod | Safari,
Good_Rod_Safari = Good_Rod | Safari,
Super_Rod_Safari = Super_Rod | Safari,
Rock_Smash_Safari = Rock_Smash | Safari,
Pokeradar_Safari = Pokeradar | Safari,
Swarm = 1 << 7,
}
public static partial class Extensions
{
internal static bool IsSafariType(this SlotType t) => (t & SlotType.Safari) != 0;
internal static bool IsFishingRodType(this SlotType t)
{
return (t & SlotType.Old_Rod) != 0 || (t & SlotType.Good_Rod) != 0 || (t & SlotType.Super_Rod) != 0;
t &= (SlotType)0xF;
return t == SlotType.Old_Rod || t == SlotType.Good_Rod || t == SlotType.Super_Rod;
}
internal static bool IsSweetScentType(this SlotType t)
{
return !(t.IsFishingRodType() || (t & SlotType.Rock_Smash) != 0);
}
return t switch
{
SlotType.Grass => true,
SlotType.Surf => true,
SlotType.BugContest => true,
public static Ball GetBall(this SlotType t)
{
if (t == SlotType.BugContest)
return Ball.Sport;
if (t.IsSafariType())
return Ball.Safari;
return Ball.Poke;
_ => false,
};
}
}
}

View file

@ -78,7 +78,7 @@
{
// Static and Magnet Pull do a slot search rather than slot mapping 0-99.
return Lead != LeadRequired.StaticMagnet
? SlotRange.GetSlot(slot.Type, RandESV, FrameType)
? SlotRange.GetSlot(slot.Area.Type, RandESV, FrameType)
: SlotRange.GetSlotStaticMagnet(slot, RandESV);
}

View file

@ -32,15 +32,10 @@ namespace PKHeX.Core
return type switch
{
SlotType.Old_Rod => CalcSlot(ESV, H_OldRod),
SlotType.Old_Rod_Safari => CalcSlot(ESV, H_OldRod),
SlotType.Good_Rod => CalcSlot(ESV, H_GoodRod),
SlotType.Good_Rod_Safari => CalcSlot(ESV, H_GoodRod),
SlotType.Super_Rod => CalcSlot(ESV, H_SuperRod),
SlotType.Super_Rod_Safari => CalcSlot(ESV, H_SuperRod),
SlotType.Rock_Smash => CalcSlot(ESV, H_Surf),
SlotType.Rock_Smash_Safari => CalcSlot(ESV, H_Surf),
SlotType.Surf => CalcSlot(ESV, H_Surf),
SlotType.Surf_Safari => CalcSlot(ESV, H_Surf),
SlotType.Swarm => (ESV < 50 ? 0 : -1),
_ => CalcSlot(ESV, H_Regular)
};
@ -60,15 +55,7 @@ namespace PKHeX.Core
return CalcSlot(ESV, K_SuperRod);
case SlotType.BugContest:
return CalcSlot(ESV, K_BCC);
case SlotType.Grass_Safari:
case SlotType.Surf_Safari:
case SlotType.Old_Rod_Safari:
case SlotType.Good_Rod_Safari:
case SlotType.Super_Rod_Safari:
case SlotType.Rock_Smash_Safari:
return 0; // (int)(rand % 10); /* Block Slot Priority not implemented */
case SlotType.Headbutt:
case SlotType.Headbutt_Special:
return CalcSlot(ESV, K_Headbutt);
default:
return CalcSlot(ESV, H_Regular);
@ -143,7 +130,7 @@ namespace PKHeX.Core
public static bool GetIsEncounterable(EncounterSlot slot, FrameType frameType, int rand, LeadRequired lead)
#pragma warning restore IDE0060, RCS1163 // Unused parameter.
{
if (slot.Type.IsSweetScentType())
if (slot.Area.Type.IsSweetScentType())
return true;
return true; // todo
//return GetCanEncounter(slot, frameType, rand, lead);
@ -153,27 +140,28 @@ namespace PKHeX.Core
public static bool GetCanEncounter(EncounterSlot slot, FrameType frameType, int rand, LeadRequired lead)
{
int proc = frameType == FrameType.MethodJ ? rand / 656 : rand % 100;
if ((slot.Type & SlotType.Rock_Smash) != 0)
var stype = slot.Area.Type;
if (stype == SlotType.Rock_Smash)
return proc < 60;
if (frameType == FrameType.MethodH)
return true; // fishing encounters are disjointed by the hooked message.
// fishing
if ((slot.Type & SlotType.Old_Rod) != 0)
if (stype == SlotType.Old_Rod)
{
if (proc < 25)
return true;
if (proc < 50)
return lead == LeadRequired.None;
}
else if ((slot.Type & SlotType.Good_Rod) != 0)
else if (stype == SlotType.Good_Rod)
{
if (proc < 50)
return true;
if (proc < 75 && lead == LeadRequired.None)
return lead == LeadRequired.None;
}
else if ((slot.Type & SlotType.Super_Rod) != 0)
else if (stype == SlotType.Super_Rod)
{
if (proc < 75)
return true;

View file

@ -401,16 +401,7 @@ namespace PKHeX.Core
private static void SetRandomIVs(PKM pk)
{
var rng = Util.Rand;
pk.IVs = new[]
{
rng.Next(32),
rng.Next(32),
rng.Next(32),
rng.Next(32),
rng.Next(32),
rng.Next(32),
};
pk.IVs = pk.SetRandomIVs();
}
}
}

View file

@ -71,6 +71,8 @@
/// <summary> Generation 4 -> Generation 5 Transfer Location (Crown Beast - Event activated in Gen 5) </summary>
public const int Transfer4_CrownUsed = 30013;
public const int BugCatchingContest4 = 207;
public static int TradedEggLocationNPC(int gen)
{
return gen switch
@ -98,6 +100,11 @@
public static bool IsPtHGSSLocationEgg(int location) => 2010 < location && location < 3000;
public static bool IsEventLocation5(int location) => 40000 < location && location < 50000;
public static bool IsSafariZoneLocation4(int loc) => loc == 52 || loc == 202;
private const int SafariLocation_RSE = 57;
private const int SafariLocation_FRLG = 136;
private const int SafariLocation_HGSS = 202;
private const int MarshLocation_DPPt = 52;
public static bool IsSafariZoneLocation3(int loc) => loc == SafariLocation_RSE || loc == SafariLocation_FRLG;
public static bool IsSafariZoneLocation4(int loc) => loc == MarshLocation_DPPt || loc == SafariLocation_HGSS;
}
}

View file

@ -37,11 +37,12 @@ namespace PKHeX.Core
if (abilval < 0)
return GetInvalid(LAbilityUnexpected);
if (data.EncounterMatch is MysteryGift g && g.Format >= 4)
return VerifyAbilityMG(data, abilities, g.AbilityType);
var enc = data.EncounterMatch;
if (enc is MysteryGift g && g.Format >= 4)
return VerifyAbilityMG(data, g, abilities);
if (pkm.Format < 6)
return VerifyAbility345(data, abilities, abilval);
return VerifyAbility345(data, enc, abilities, abilval);
// Check AbilityNumber is a single set bit
var num = pkm.AbilityNumber;
@ -58,8 +59,8 @@ namespace PKHeX.Core
private CheckResult VerifyAbility(LegalityAnalysis data, IReadOnlyList<int> abilities, int abilnum)
{
var EncounterMatch = data.EncounterMatch;
var eabil = GetEncounterFixedAbilityNumber(EncounterMatch);
var enc = data.EncounterMatch;
var eabil = GetEncounterFixedAbilityNumber(enc);
if (eabil >= 0)
{
if ((data.pkm.AbilityNumber == 4) != (eabil == 4))
@ -71,15 +72,15 @@ namespace PKHeX.Core
var gen = data.Info.Generation;
return gen switch
{
5 => VerifyAbility5(data, abilities),
6 => VerifyAbility6(data),
7 => VerifyAbility7(data),
8 => VerifyAbility8(data),
5 => VerifyAbility5(data, enc, abilities),
6 => VerifyAbility6(data, enc),
7 => VerifyAbility7(data, enc),
8 => VerifyAbility8(data, enc),
_ => CheckMatch(data.pkm, abilities, gen, AbilityState.CanMismatch)
};
}
private CheckResult VerifyAbility345(LegalityAnalysis data, IReadOnlyList<int> abilities, int abilnum)
private CheckResult VerifyAbility345(LegalityAnalysis data, IEncounterable enc, IReadOnlyList<int> abilities, int abilnum)
{
var pkm = data.pkm;
var state = AbilityState.MustMatch;
@ -98,7 +99,7 @@ namespace PKHeX.Core
int gen = data.Info.Generation;
if (gen == 5)
return VerifyAbility5(data, abilities);
return VerifyAbility5(data, enc, abilities);
return CheckMatch(pkm, abilities, gen, state);
}
@ -188,29 +189,30 @@ namespace PKHeX.Core
return AbilityState.CanMismatch;
}
private CheckResult VerifyAbilityMG(LegalityAnalysis data, IReadOnlyList<int> abilities, int cardtype)
private CheckResult VerifyAbilityMG(LegalityAnalysis data, MysteryGift g, IReadOnlyList<int> abilities)
{
if (data.EncounterMatch is PCD d)
if (g is PCD d)
return VerifyAbilityPCD(data, abilities, d);
var pkm = data.pkm;
if (data.EncounterMatch is PGT) // Ranger Manaphy
if (g is PGT) // Ranger Manaphy
return (pkm.Format >= 6 ? (pkm.AbilityNumber == 1) : (pkm.AbilityNumber < 4)) ? VALID : GetInvalid(LAbilityMismatchGift);
int abilNumber = pkm.AbilityNumber;
if (cardtype == 4) // 1/2/H
var cardType = g.AbilityType;
if (cardType == 4) // 1/2/H
return VALID;
if (cardtype == 3) // 1/2
int abilNumber = pkm.AbilityNumber;
if (cardType == 3) // 1/2
return abilNumber == 4 ? GetInvalid(LAbilityMismatchGift) : VALID;
// Only remaining matches are fixed index abilities
int cardAbilIndex = 1 << cardtype;
int cardAbilIndex = 1 << cardType;
if (abilNumber == cardAbilIndex)
return VALID;
// Can still match if the ability was changed via ability capsule...
// However, it can't change to/from Hidden Abilities.
if (abilNumber == 4 || cardtype == 2)
if (abilNumber == 4 || cardType == 2)
return GetInvalid(LAbilityHiddenFail);
// Ability can be flipped 0/1 if Ability Capsule is available, is not Hidden Ability, and Abilities are different.
@ -243,14 +245,14 @@ namespace PKHeX.Core
return pkm.Ability == pcd.Gift.PK.Ability ? VALID : INVALID;
}
private CheckResult VerifyAbility5(LegalityAnalysis data, IReadOnlyList<int> abilities)
private CheckResult VerifyAbility5(LegalityAnalysis data, IEncounterable enc, IReadOnlyList<int> abilities)
{
var pkm = data.pkm;
switch (data.EncounterMatch)
switch (enc)
{
case EncounterSlot w:
// Hidden Abilities for Wild Encounters are only available at a Hidden Grotto
bool grotto = w.Type == SlotType.HiddenGrotto;
bool grotto = w.Area.Type == SlotType.HiddenGrotto;
if (pkm.AbilityNumber == 4 ^ grotto)
return GetInvalid(grotto ? LAbilityMismatchGrotto : LAbilityHiddenFail);
break;
@ -265,17 +267,16 @@ namespace PKHeX.Core
return CheckMatch(data.pkm, abilities, 5, state);
}
private CheckResult VerifyAbility6(LegalityAnalysis data)
private CheckResult VerifyAbility6(LegalityAnalysis data, IEncounterable enc)
{
var pkm = data.pkm;
if (pkm.AbilityNumber != 4)
return VALID;
// hidden abilities
var EncounterMatch = data.EncounterMatch;
if (EncounterMatch is EncounterSlot slot)
if (enc is EncounterSlot slot)
{
bool valid = (slot is EncounterSlot6AO ao && ao.DexNav) || slot.Type == SlotType.FriendSafari || slot.Type == SlotType.Horde;
bool valid = (slot is EncounterSlot6AO ao && ao.CanDexNav) || slot.Area.Type == SlotType.FriendSafari || slot.Area.Type == SlotType.Horde;
if (!valid)
return GetInvalid(LAbilityMismatchHordeSafari);
}
@ -285,13 +286,12 @@ namespace PKHeX.Core
return VALID;
}
private CheckResult VerifyAbility7(LegalityAnalysis data)
private CheckResult VerifyAbility7(LegalityAnalysis data, IEncounterable enc)
{
var pkm = data.pkm;
var EncounterMatch = data.EncounterMatch;
if (EncounterMatch is EncounterSlot slot && pkm.AbilityNumber == 4)
if (enc is EncounterSlot slot && pkm.AbilityNumber == 4)
{
bool valid = slot.Type == SlotType.SOS;
bool valid = slot.Area.Type == SlotType.SOS;
if (!valid)
return GetInvalid(LAbilityMismatchSOS);
}
@ -301,11 +301,10 @@ namespace PKHeX.Core
return VALID;
}
private CheckResult VerifyAbility8(LegalityAnalysis data)
private CheckResult VerifyAbility8(LegalityAnalysis data, IEncounterable enc)
{
var pkm = data.pkm;
var EncounterMatch = data.EncounterMatch;
if (EncounterMatch is EncounterSlot && pkm.AbilityNumber == 4)
if (enc is EncounterSlot && pkm.AbilityNumber == 4)
return GetInvalid(LAbilityHiddenUnavailable);
if (Legal.Ban_NoHidden8.Contains(pkm.SpecForm) && pkm.AbilityNumber == 4)
return GetInvalid(LAbilityHiddenUnavailable);

View file

@ -74,20 +74,30 @@ namespace PKHeX.Core
return VerifyBallEquals(data, Legal.GetWildBalls(data.Info.Generation, data.Info.Game));
}
private static Ball GetRequiredBallValue(int gen, int loc)
{
return gen switch
{
// For Gen3 Safari Zones, we've already deferred partial match encounters.
3 when Locations.IsSafariZoneLocation3(loc) => Safari,
// For Gen4 Safari Zones and BCC, we've already deferred partial match encounters.
4 when Locations.IsSafariZoneLocation4(loc) => Safari,
4 when Locations.BugCatchingContest4 == loc => Sport,
// Poké Pelago
7 when loc == 30016 => Poke,
_ => None,
};
}
private CheckResult VerifyBallWild(LegalityAnalysis data, EncounterSlot w)
{
if (w.Location == 30016 && w.Generation == 7) // Poké Pelago
return VerifyBallEquals(data, (int)Poke); // Pokeball
var req = BallExtensions.GetRequiredBallValueWild(w.Generation, w.Location);
if (req != None)
return VerifyBallEquals(data, (int) req);
var Info = data.Info;
// For gen3/4 Safari Zones and BCC getValidWildEncounters already filter to not return
// mixed possible encounters between safari, BCC and other encounters
// That means is the first encounter is not safari then there is no safari encounter in the array
if (3 <= Info.Generation && Info.Generation <= 4 && w.Type.IsSafariType())
return VerifyBallEquals(data, (int)Safari); // Safari Ball
if (Info.Generation == 4 && w.Type == SlotType.BugContest)
return VerifyBallEquals(data, (int)Sport); // Sport Ball
return VerifyBallEquals(data, Legal.GetWildBalls(data.Info.Generation, data.Info.Game));
}

View file

@ -71,7 +71,7 @@ namespace PKHeX.Core
var EncounterMatch = data.EncounterMatch;
if (!awakened.AwakeningAllValid())
data.AddLine(GetInvalid(LAwakenedCap));
if (EncounterMatch is EncounterSlot s && s.Type == SlotType.GoPark && Enumerable.Range(0, 6).Select(awakened.GetAV).Any(z => z < 2))
if (EncounterMatch is EncounterSlot s && s.Area.Type == SlotType.GoPark && Enumerable.Range(0, 6).Select(awakened.GetAV).Any(z => z < 2))
data.AddLine(GetInvalid(string.Format(LAwakenedShouldBeValue, 2))); // go park transfers have 2 AVs for all stats.
else if (awakened.AwakeningSum() == 0 && !EncounterMatch.IsWithinRange(pkm))
data.AddLine(Get(LAwakenedEXPIncreased, Severity.Fishy));

View file

@ -42,7 +42,7 @@ namespace PKHeX.Core
if (!PersonalInfo.IsFormeWithinRange(form) && !FormConverter.IsValidOutOfBoundsForme(species, form, Info.Generation))
return GetInvalid(string.Format(LFormInvalidRange, count - 1, form));
if (EncounterMatch is EncounterSlot w && w.Type == SlotType.FriendSafari)
if (EncounterMatch is EncounterSlot w && w.Area.Type == SlotType.FriendSafari)
{
VerifyFormFriendSafari(data);
}

View file

@ -94,7 +94,7 @@ namespace PKHeX.Core
var pkm = data.pkm;
if (pkm.XY && PersonalTable.XY[data.EncounterMatch.Species].IsEggGroup(15)) // Undiscovered
VerifyIVsFlawless(data, 3);
else if (w.Type == SlotType.FriendSafari)
else if (w.Area.Type == SlotType.FriendSafari)
VerifyIVsFlawless(data, 2);
}

View file

@ -57,9 +57,9 @@ namespace PKHeX.Core
break;
case EncounterSlot w:
if (pkm.IsShiny && w.Type == SlotType.HiddenGrotto)
if (pkm.IsShiny && w.Area.Type == SlotType.HiddenGrotto)
data.AddLine(GetInvalid(LG5PIDShinyGrotto, CheckIdentifier.Shiny));
if (Info.Generation == 5 && w.Type != SlotType.HiddenGrotto)
if (Info.Generation == 5 && w.Area.Type != SlotType.HiddenGrotto)
VerifyG5PID_IDCorrelation(data);
break;

View file

@ -31,47 +31,36 @@
<None Remove="Resources\byte\encounter_b2.pkl" />
<None Remove="Resources\byte\encounter_blue.pkl" />
<None Remove="Resources\byte\encounter_crystal.pkl" />
<None Remove="Resources\byte\encounter_crystal_h.pkl" />
<None Remove="Resources\byte\encounter_d.pkl" />
<None Remove="Resources\byte\encounter_e.pkl" />
<None Remove="Resources\byte\encounter_fr.pkl" />
<None Remove="Resources\byte\encounter_ge.pkl" />
<None Remove="Resources\byte\encounter_gold.pkl" />
<None Remove="Resources\byte\encounter_gold_h.pkl" />
<None Remove="Resources\byte\encounter_gp.pkl" />
<None Remove="Resources\byte\encounter_gsc_f.pkl" />
<None Remove="Resources\byte\encounter_hg.pkl" />
<None Remove="Resources\byte\encounter_lg.pkl" />
<None Remove="Resources\byte\encounter_mn.pkl" />
<None Remove="Resources\byte\encounter_mn_sos.pkl" />
<None Remove="Resources\byte\encounter_o.pkl" />
<None Remove="Resources\byte\encounter_p.pkl" />
<None Remove="Resources\byte\encounter_pt.pkl" />
<None Remove="Resources\byte\encounter_r.pkl" />
<None Remove="Resources\byte\encounter_rb_f.pkl" />
<None Remove="Resources\byte\encounter_red.pkl" />
<None Remove="Resources\byte\encounter_rse_swarm.pkl" />
<None Remove="Resources\byte\encounter_s.pkl" />
<None Remove="Resources\byte\encounter_sh_hidden.pkl" />
<None Remove="Resources\byte\encounter_sh_symbol.pkl" />
<None Remove="Resources\byte\encounter_silver.pkl" />
<None Remove="Resources\byte\encounter_silver_h.pkl" />
<None Remove="Resources\byte\encounter_sn.pkl" />
<None Remove="Resources\byte\encounter_sn_sos.pkl" />
<None Remove="Resources\byte\encounter_ss.pkl" />
<None Remove="Resources\byte\encounter_sw_hidden.pkl" />
<None Remove="Resources\byte\encounter_sw_symbol.pkl" />
<None Remove="Resources\byte\encounter_us.pkl" />
<None Remove="Resources\byte\encounter_us_sos.pkl" />
<None Remove="Resources\byte\encounter_um.pkl" />
<None Remove="Resources\byte\encounter_um_sos.pkl" />
<None Remove="Resources\byte\encounter_w.pkl" />
<None Remove="Resources\byte\encounter_w2.pkl" />
<None Remove="Resources\byte\encounter_x.pkl" />
<None Remove="Resources\byte\encounter_y.pkl" />
<None Remove="Resources\byte\encounter_yellow.pkl" />
<None Remove="Resources\byte\encounter_yellow_f.pkl" />
<None Remove="Resources\byte\encounter_hb_hg.pkl" />
<None Remove="Resources\byte\encounter_hb_ss.pkl" />
<None Remove="Resources\byte\evos_ao.pkl" />
<None Remove="Resources\byte\evos_g3.pkl" />
<None Remove="Resources\byte\evos_g4.pkl" />
@ -856,47 +845,36 @@
<EmbeddedResource Include="Resources\byte\encounter_b2.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_blue.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_crystal.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_crystal_h.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_d.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_e.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_fr.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_ge.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_gold.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_gold_h.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_gsc_f.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_gp.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_hg.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_lg.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_mn.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_mn_sos.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_o.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_p.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_pt.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_r.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_rb_f.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_red.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_rse_swarm.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_s.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_sh_hidden.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_sh_symbol.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_silver.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_silver_h.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_sn.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_sn_sos.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_ss.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_sw_hidden.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_sw_symbol.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_us.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_us_sos.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_um.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_um_sos.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_w.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_w2.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_x.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_y.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_yellow.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_yellow_f.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_hb_hg.pkl" />
<EmbeddedResource Include="Resources\byte\encounter_hb_ss.pkl" />
<EmbeddedResource Include="Resources\byte\evos_ao.pkl" />
<EmbeddedResource Include="Resources\byte\evos_g3.pkl" />
<EmbeddedResource Include="Resources\byte\evos_g4.pkl" />

Binary file not shown.

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