PKHeX/PKHeX.Core/Legality/Structures/TreesArea.cs

147 lines
6.2 KiB
C#
Raw Normal View History

using System.Linq;
namespace PKHeX.Core
{
// Pokemon Crystal Headbutt tree encounters by trainer id, base on mechanics described in
// https://bulbapedia.bulbagarden.net/wiki/Headbutt_tree#Mechanics
public enum TreeEncounterAvailable
{
ValidTree, // Encounter is possible a reacheable tree
InvalidTree, // Encounter is only possible a tree reacheable only with walk-trought walls cheats
Impossible // Encounter is not possible in any tree
}
public class TreeCoordinates
{
internal int X { get; set; }
internal int Y { get; set; }
internal int Index => ((X * Y + X + Y) / 5) % 10;
}
public class TreesArea
{
public int Location { get; private set; }
public TreeEncounterAvailable[] TrainerModerateEncounterTree { get; private set; }
public TreeEncounterAvailable[] TrainerLowEncounterTree { get; private set; }
private int[] ValidTreeIndex { get; set; }
private int[] InvalidTreeIndex { get; set; }
private TreeCoordinates[] ValidTrees { get; set; }
private TreeCoordinates[] InvalidTrees { get; set; }
private static int[][] TrainerModerateTreeIndex { get; set; }
internal static TreesArea[] GetArray(byte[][] entries)
{
if (entries == null)
return null;
TrainerModerateTreeIndex = GenerateTrainersTreeIndex();
var Areas = new TreesArea[entries.Length];
for(int i = 0; i < entries.Length; i++)
{
Areas[i] = GetArea(entries[i]);
}
return Areas;
}
private static int[][] GenerateTrainersTreeIndex()
{
// A tree have a low encounter or moderate encounter base on the TID Pivot Index ( TID % 10)
// Calculate for every Trainer Pivot Index the 5 tree index for low encounters
int[][] TrainersIndex = new int[10][];
for (int pivotindex = 0; pivotindex < 10; pivotindex++)
{
int[] ModerateEncounterTreeIndex = new int[5];
for(int index = 0; index <= 4; index++)
ModerateEncounterTreeIndex[index] = (pivotindex + index) % 10;
TrainersIndex[pivotindex] = ModerateEncounterTreeIndex.OrderBy(x => x).ToArray();
}
return TrainersIndex;
}
private static TreesArea GetArea(byte[] entrie)
{
var Area = new TreesArea();
Area.ReadAreaRawData(entrie);
Area.GenerateAreaTreeIndex();
Area.GenerateAreaTrainerEncounters();
return Area;
}
private void ReadAreaRawData(byte[] entrie)
{
// Coordinates of trees for every are obtained with programa G2Map
// ValidTrees are those accesible from the player
// Invalid tress are trees that the player can not reach without cheating devices, like a tree beyond other trees
Location = entrie[0];
ValidTrees = new TreeCoordinates[entrie[1]];
var ofs = 2;
for (int i = 0; i < ValidTrees.Length; i++)
{
ValidTrees[i] = new TreeCoordinates()
{
X = entrie[ofs],
Y = entrie[ofs + 1]
};
ofs += 2;
}
InvalidTrees = new TreeCoordinates[entrie[ofs]];
ofs += 1;
for (int i = 0; i < InvalidTrees.Length; i++)
{
InvalidTrees[i] = new TreeCoordinates()
{
X = entrie[ofs],
Y = entrie[ofs + 1]
};
ofs += 2;
}
}
private void GenerateAreaTreeIndex()
{
// For legallity purpose only the tree index is needed, group the trees data by their index, trees with the same index are indistinguible
ValidTreeIndex = ValidTrees.Select(t => t.Index).Distinct().OrderBy(i => i).ToArray();
InvalidTreeIndex = InvalidTrees.Select(t => t.Index).Distinct().OrderBy(i => i).Except(ValidTreeIndex).ToArray();
}
private void GenerateAreaTrainerEncounters()
{
// Check for every trainer pivot index if there is trees with low encounter and moderate encounter available in the area
TrainerModerateEncounterTree = new TreeEncounterAvailable[10];
TrainerLowEncounterTree = new TreeEncounterAvailable[10];
for (int pivotindex = 0; pivotindex < 10; pivotindex++)
{
var TrainerModerateTrees = TrainerModerateTreeIndex[pivotindex];
var ModerateValid = ValidTreeIndex.Any(t => TrainerModerateTrees.Contains(t));
var ModerateInvalid = InvalidTreeIndex.Any(t => TrainerModerateTrees.Contains(t));
if (ModerateValid)
// There is a valid tree with an index for moderate encounters
TrainerModerateEncounterTree[pivotindex] = TreeEncounterAvailable.ValidTree;
else if (ModerateInvalid)
// There is a tree with an index for moderate encounters but is invalid
TrainerModerateEncounterTree[pivotindex] = TreeEncounterAvailable.InvalidTree;
else
// No trees for moderate encounters
TrainerModerateEncounterTree[pivotindex] = TreeEncounterAvailable.Impossible;
var LowValid = ValidTreeIndex.Except(TrainerModerateTrees).Any();
var LowInvalid = InvalidTreeIndex.Except(TrainerModerateTrees).Any();
if (LowValid)
// There is a valid tree with an index for low encounters
TrainerLowEncounterTree[pivotindex] = TreeEncounterAvailable.ValidTree;
else if (LowInvalid)
// There is a tree with an index for low encounters but is invalid
TrainerLowEncounterTree[pivotindex] = TreeEncounterAvailable.InvalidTree;
else
// No trees for low encounters
TrainerLowEncounterTree[pivotindex] = TreeEncounterAvailable.Impossible;
}
}
}
}