2017-07-06 23:03:41 +00:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
|
|
namespace PKHeX.Core
|
|
|
|
|
{
|
2017-07-07 11:53:21 +00:00
|
|
|
|
// Pokemon Crystal Headbutt tree encounters by trainer id, based on mechanics described in
|
2017-07-06 23:03:41 +00:00
|
|
|
|
// https://bulbapedia.bulbagarden.net/wiki/Headbutt_tree#Mechanics
|
|
|
|
|
|
2017-08-01 00:09:16 +00:00
|
|
|
|
/// <summary> Indicates the Availability of the Headbutt Tree </summary>
|
2017-07-06 23:03:41 +00:00
|
|
|
|
public enum TreeEncounterAvailable
|
|
|
|
|
{
|
2017-08-01 00:09:16 +00:00
|
|
|
|
/// <summary> Encounter is possible a reachable tree </summary>
|
|
|
|
|
ValidTree,
|
|
|
|
|
/// <summary> Encounter is only possible a tree reachable only with walk-through walls cheats </summary>
|
|
|
|
|
InvalidTree,
|
|
|
|
|
/// <summary> Encounter is not possible in any tree </summary>
|
|
|
|
|
Impossible
|
2017-07-06 23:03:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-01 00:09:16 +00:00
|
|
|
|
/// <summary> Coordinate / Index Relationship for a Headbutt Tree </summary>
|
2017-07-07 11:53:21 +00:00
|
|
|
|
internal class TreeCoordinates
|
2017-07-06 23:03:41 +00:00
|
|
|
|
{
|
2018-05-19 04:35:57 +00:00
|
|
|
|
internal int X { get; }
|
|
|
|
|
internal int Y { get; }
|
2017-08-01 00:09:16 +00:00
|
|
|
|
internal int Index => (X*Y + X+Y) / 5 % 10;
|
2017-07-06 23:03:41 +00:00
|
|
|
|
|
2017-08-01 00:09:16 +00:00
|
|
|
|
public TreeCoordinates(int x, int y)
|
2017-07-06 23:03:41 +00:00
|
|
|
|
{
|
2017-08-01 00:09:16 +00:00
|
|
|
|
X = x;
|
|
|
|
|
Y = y;
|
2017-07-06 23:03:41 +00:00
|
|
|
|
}
|
2017-08-01 00:09:16 +00:00
|
|
|
|
}
|
2017-07-06 23:03:41 +00:00
|
|
|
|
|
2017-08-01 00:09:16 +00:00
|
|
|
|
/// <summary> Trees on a given map </summary>
|
|
|
|
|
public class TreesArea
|
|
|
|
|
{
|
|
|
|
|
private const int PivotCount = 10;
|
|
|
|
|
private static int[][] TrainerModerateTreeIndex { get; } = GenerateTrainersTreeIndex();
|
2017-07-06 23:03:41 +00:00
|
|
|
|
private static int[][] GenerateTrainersTreeIndex()
|
|
|
|
|
{
|
2017-08-01 00:09:16 +00:00
|
|
|
|
// A tree have a low encounter or moderate encounter base on the TID Pivot Index (TID % 10)
|
2017-07-06 23:03:41 +00:00
|
|
|
|
// Calculate for every Trainer Pivot Index the 5 tree index for low encounters
|
2017-08-01 00:09:16 +00:00
|
|
|
|
int[][] TrainersIndex = new int[PivotCount][];
|
|
|
|
|
for (int i = 0; i < PivotCount; i++)
|
2017-07-06 23:03:41 +00:00
|
|
|
|
{
|
|
|
|
|
int[] ModerateEncounterTreeIndex = new int[5];
|
2017-08-01 00:09:16 +00:00
|
|
|
|
for (int j = 0; j <= 4; j++)
|
|
|
|
|
ModerateEncounterTreeIndex[j] = (i + j) % PivotCount;
|
|
|
|
|
TrainersIndex[i] = ModerateEncounterTreeIndex.OrderBy(x => x).ToArray();
|
2017-07-06 23:03:41 +00:00
|
|
|
|
}
|
|
|
|
|
return TrainersIndex;
|
|
|
|
|
}
|
2017-08-01 00:09:16 +00:00
|
|
|
|
internal static TreesArea[] GetArray(byte[][] entries) => entries.Select(z => new TreesArea(z)).ToArray();
|
2017-07-06 23:03:41 +00:00
|
|
|
|
|
2017-08-01 00:09:16 +00:00
|
|
|
|
public int Location { get; private set; }
|
|
|
|
|
public TreeEncounterAvailable[] GetTrees(SlotType t) => t == SlotType.Headbutt
|
|
|
|
|
? TrainerModerateEncounterTree
|
|
|
|
|
: TrainerLowEncounterTree;
|
2017-07-06 23:03:41 +00:00
|
|
|
|
|
2017-08-01 00:09:16 +00:00
|
|
|
|
private TreeEncounterAvailable[] TrainerModerateEncounterTree { get; set; }
|
|
|
|
|
private TreeEncounterAvailable[] TrainerLowEncounterTree { get; set; }
|
|
|
|
|
private int[] ValidTreeIndex { get; set; }
|
|
|
|
|
private int[] InvalidTreeIndex { get; set; }
|
|
|
|
|
private TreeCoordinates[] ValidTrees { get; set; }
|
|
|
|
|
private TreeCoordinates[] InvalidTrees { get; set; }
|
|
|
|
|
|
|
|
|
|
private TreesArea(byte[] entry)
|
|
|
|
|
{
|
|
|
|
|
ReadAreaRawData(entry);
|
|
|
|
|
GenerateAreaTreeIndex();
|
|
|
|
|
GenerateAreaTrainerEncounters();
|
2017-07-06 23:03:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-08-01 00:09:16 +00:00
|
|
|
|
private void ReadAreaRawData(byte[] entry)
|
2017-07-06 23:03:41 +00:00
|
|
|
|
{
|
2017-07-07 11:53:21 +00:00
|
|
|
|
// Coordinates of trees for every are obtained with the program G2Map
|
|
|
|
|
// ValidTrees are those accessible by the player
|
2017-08-01 00:09:16 +00:00
|
|
|
|
Location = entry[0];
|
|
|
|
|
ValidTrees = new TreeCoordinates[entry[1]];
|
2017-07-06 23:03:41 +00:00
|
|
|
|
var ofs = 2;
|
2017-08-01 00:09:16 +00:00
|
|
|
|
for (int i = 0; i < ValidTrees.Length; i++, ofs += 2)
|
|
|
|
|
ValidTrees[i] = new TreeCoordinates(entry[ofs], entry[ofs + 1]);
|
|
|
|
|
|
|
|
|
|
// Invalid tress are trees that the player can not reach without cheating devices, like a tree beyond other trees
|
|
|
|
|
InvalidTrees = new TreeCoordinates[entry[ofs]];
|
2018-05-12 19:28:48 +00:00
|
|
|
|
ofs++;
|
2017-08-01 00:09:16 +00:00
|
|
|
|
for (int i = 0; i < InvalidTrees.Length; i++, ofs += 2)
|
|
|
|
|
InvalidTrees[i] = new TreeCoordinates(entry[ofs], entry[ofs + 1]);
|
2017-07-06 23:03:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void GenerateAreaTreeIndex()
|
|
|
|
|
{
|
2017-08-01 00:09:16 +00:00
|
|
|
|
// For legality purposes, only the tree index is needed.
|
|
|
|
|
// Group the trees data by their index; trees that share indexes are indistinguishable from one another
|
2017-07-06 23:03:41 +00:00
|
|
|
|
ValidTreeIndex = ValidTrees.Select(t => t.Index).Distinct().OrderBy(i => i).ToArray();
|
|
|
|
|
InvalidTreeIndex = InvalidTrees.Select(t => t.Index).Distinct().OrderBy(i => i).Except(ValidTreeIndex).ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void GenerateAreaTrainerEncounters()
|
|
|
|
|
{
|
2017-08-01 00:09:16 +00:00
|
|
|
|
// Check for every trainer pivot index if there are trees with moderate encounter and low encounter available in the area
|
|
|
|
|
TrainerModerateEncounterTree = new TreeEncounterAvailable[PivotCount];
|
|
|
|
|
TrainerLowEncounterTree = new TreeEncounterAvailable[PivotCount];
|
|
|
|
|
for (int i = 0; i < PivotCount; i++)
|
2017-07-06 23:03:41 +00:00
|
|
|
|
{
|
2017-08-01 00:09:16 +00:00
|
|
|
|
var TrainerModerateTrees = TrainerModerateTreeIndex[i];
|
|
|
|
|
TrainerModerateEncounterTree[i] = GetAvailableModerate(TrainerModerateTrees);
|
|
|
|
|
TrainerLowEncounterTree[i] = GetAvailableLow(TrainerModerateTrees);
|
2017-07-06 23:03:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-01 00:09:16 +00:00
|
|
|
|
|
|
|
|
|
private TreeEncounterAvailable GetAvailableModerate(int[] moderate)
|
|
|
|
|
{
|
|
|
|
|
if (ValidTreeIndex.Any(moderate.Contains))
|
|
|
|
|
return TreeEncounterAvailable.ValidTree;
|
|
|
|
|
if (InvalidTreeIndex.Any(moderate.Contains))
|
|
|
|
|
return TreeEncounterAvailable.InvalidTree;
|
|
|
|
|
return TreeEncounterAvailable.Impossible;
|
|
|
|
|
}
|
|
|
|
|
private TreeEncounterAvailable GetAvailableLow(int[] moderate)
|
|
|
|
|
{
|
|
|
|
|
if (ValidTreeIndex.Except(moderate).Any())
|
|
|
|
|
return TreeEncounterAvailable.ValidTree;
|
|
|
|
|
if (InvalidTreeIndex.Except(moderate).Any())
|
|
|
|
|
return TreeEncounterAvailable.InvalidTree;
|
|
|
|
|
return TreeEncounterAvailable.Impossible;
|
|
|
|
|
}
|
2018-05-19 04:35:57 +00:00
|
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
private void DumpLocation()
|
|
|
|
|
{
|
|
|
|
|
string loc = GameInfo.GetStrings("en").metGSC_00000[Location];
|
|
|
|
|
System.Console.WriteLine($"Location: {loc}");
|
|
|
|
|
System.Console.WriteLine("Valid:");
|
|
|
|
|
foreach (var tree in ValidTrees)
|
|
|
|
|
System.Console.WriteLine($"{tree.Index} @ ({tree.X:D2},{tree.Y:D2})");
|
|
|
|
|
System.Console.WriteLine("Invalid:");
|
|
|
|
|
foreach (var tree in InvalidTrees)
|
|
|
|
|
System.Console.WriteLine($"{tree.Index} @ ({tree.X:D2},{tree.Y:D2})");
|
|
|
|
|
System.Console.WriteLine("===");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2017-07-06 23:03:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|