mirror of
https://github.com/kwsch/pkNX.git
synced 2026-08-28 21:34:02 -05:00
Add wander tolerance to encounter slot pkl creation (#301)
* Rough draft: 50f * Don't skip spawn-less locations, cross pollinate * Dump points and biomes * Lower wander tolerance to 30f, reorder time flags * Reorder crabrawler evo order
This commit is contained in:
@@ -35,4 +35,7 @@ public class AreaInfo
|
||||
[FlatBufferItem(22)] public EncBiome BaseBiome { get; set; }
|
||||
[FlatBufferItem(23)] public AreaTag Tag { get; set; }
|
||||
[FlatBufferItem(24)] public OverrideBiome OverrideBiome { get; set; }
|
||||
|
||||
public int ActualMinLevel => MinEncLv != 0 ? MinEncLv : 1;
|
||||
public int ActualMaxLevel => MaxEncLv != 0 ? MaxEncLv : 100;
|
||||
}
|
||||
|
||||
@@ -425,7 +425,7 @@ private static (int, uint) ReadVar32(ReadOnlySpan<byte> buffer)
|
||||
};
|
||||
}
|
||||
|
||||
public class AABBTree
|
||||
public class AABBTree : IContainsV3f
|
||||
{
|
||||
private readonly List<hkcdSimdTreeNode> _nodes;
|
||||
private int _numLeafRects;
|
||||
@@ -471,8 +471,10 @@ public AABBTree(List<hkcdSimdTreeNode> nodes)
|
||||
|
||||
// Official logic for area-containment checks for y intersection between y+1 and y-10000.0
|
||||
public bool ContainsPoint(float x, float y, float z) => ContainsPointInNode(1, x, y - 10000, y + 1, z);
|
||||
public bool ContainsPoint(float x, float y, float z, float toleranceX, float toleranceY, float toleranceZ)
|
||||
=> ContainsPointInNode(1, x, y - 10000, y + 1, z, toleranceX, toleranceY, toleranceZ);
|
||||
|
||||
private bool ContainsPointInNode(int nodeIndex, float x, float ly, float hy, float z)
|
||||
private bool ContainsPointInNode(int nodeIndex, float x, float ly, float hy, float z, float tx = 0f, float ty = 0f, float tz = 0f)
|
||||
{
|
||||
if (nodeIndex == 0)
|
||||
return false;
|
||||
@@ -480,16 +482,17 @@ private bool ContainsPointInNode(int nodeIndex, float x, float ly, float hy, flo
|
||||
var node = _nodes[nodeIndex];
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
if (node.lx[i] > node.hx[i] || !(node.lx[i] <= x && x <= node.hx[i]))
|
||||
if (node.lx[i] > node.hx[i] || !(node.lx[i] - tx <= x) || !(node.hx[i] + tx >= x))
|
||||
continue;
|
||||
if (node.lz[i] > node.hz[i] || !(node.lz[i] <= z && z <= node.hz[i]))
|
||||
if (node.lz[i] > node.hz[i] || !(node.lz[i] - tz <= z) || !(node.hz[i] + tz >= z))
|
||||
continue;
|
||||
if (node.ly[i] > node.hy[i] || !(node.ly[i] <= hy && ly <= node.hy[i]))
|
||||
if (node.ly[i] > node.hy[i] || !(node.ly[i] - ty <= hy) || !(node.hy[i] + ty >= ly))
|
||||
continue;
|
||||
if (node.IsLeaf || ContainsPointInNode((int)node.data[i], x, ly, hy, z))
|
||||
if (node.IsLeaf)
|
||||
return true;
|
||||
if (ContainsPointInNode((int)node.data[i], x, ly, hy, z, tx, ty, tz))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -585,3 +588,9 @@ private struct HavokItem
|
||||
public uint Count;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IContainsV3f
|
||||
{
|
||||
bool ContainsPoint(float x, float y, float z);
|
||||
bool ContainsPoint(float x, float y, float z, float toleranceX, float toleranceY, float toleranceZ);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -353,14 +353,19 @@ static byte[] GetPickle(PersonalInfo9SV e)
|
||||
{
|
||||
if (!e.IsPresentInGame)
|
||||
return Array.Empty<byte>();
|
||||
return Write(e.FB.Evolutions);
|
||||
return Write(e.FB.Info.SpeciesCopy, e.FB.Evolutions);
|
||||
}
|
||||
|
||||
static byte[] Write(PersonalInfo9SVEvolutions[] evos)
|
||||
static byte[] Write(int species, PersonalInfo9SVEvolutions[] evos)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
using var bw = new BinaryWriter(ms);
|
||||
foreach (var m in evos) // just in case
|
||||
|
||||
var list = evos.ToArray();
|
||||
if (species == (int)Species.Crabrawler)
|
||||
Array.Reverse(list); // put the levelup evo last.
|
||||
|
||||
foreach (var m in list) // just in case
|
||||
{
|
||||
var method = ((EvolutionType)m.Method);
|
||||
if (method == EvolutionType.Hisui)
|
||||
@@ -533,6 +538,7 @@ private void DumpAjito()
|
||||
|
||||
private void DumpEncount()
|
||||
{
|
||||
Dump<TreeShakePokemonArray, TreeShakePokemon>("world/data/event/treeshake/treeshake_pokemon/treeshake_pokemon_array.bfbs");
|
||||
Dump<PointDataArray, PointData>("world/data/encount/point_data/point_data/encount_data_atlantis.bfbs");
|
||||
Dump<PointDataArray, PointData>("world/data/encount/point_data/point_data/encount_data_100000.bfbs");
|
||||
Dump<EncountPokeDataArray, EncountPokeData>("world/data/encount/pokedata/pokedata/pokedata_array.bfbs");
|
||||
@@ -907,6 +913,7 @@ public void DumpSpecific()
|
||||
"world/data/encount/setting/setting/data.bfbs",
|
||||
"world/data/encount/setting/raid_difficulty_lottery/raid_difficulty_lottery_array.bfbs",
|
||||
"world/data/encount/setting/raid_gem_setting/raid_gem_setting.bfbs",
|
||||
"world/data/event/treeshake/treeshake_pokemon/treeshake_pokemon_array.bfbs",
|
||||
// Field
|
||||
"world/data/field/area/field_dungeon_area/field_dungeon_area_array.bfbs",
|
||||
"world/data/field/area/field_inside_area/field_inside_area_array.bfbs",
|
||||
|
||||
31
pkNX.WinForms/Dumping/SV/BoxCollision9.cs
Normal file
31
pkNX.WinForms/Dumping/SV/BoxCollision9.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace pkNX.Structures.FlatBuffers;
|
||||
|
||||
public class BoxCollision9 : IContainsV3f
|
||||
{
|
||||
public PackedVec3f Position { get; init; }
|
||||
public PackedVec3f Size { get; init; }
|
||||
|
||||
public bool ContainsPoint(float x, float y, float z) => ContainsPoint(x, y, z, 0, 0, 0);
|
||||
|
||||
public bool ContainsPoint(float x, float y, float z, float toleranceX, float toleranceY, float toleranceZ)
|
||||
{
|
||||
var box_lx = Position.X - (Size.X / 2.0f) - toleranceX;
|
||||
var box_hx = Position.X + (Size.X / 2.0f) + toleranceX;
|
||||
if (box_lx > box_hx || !(box_lx <= x && x <= box_hx))
|
||||
return false;
|
||||
|
||||
var box_lz = Position.Z - (Size.Z / 2.0f) - toleranceZ;
|
||||
var box_hz = Position.Z + (Size.Z / 2.0f) + toleranceZ;
|
||||
if (box_lz > box_hz || !(box_lz <= z && z <= box_hz))
|
||||
return false;
|
||||
|
||||
var ly = y - 10000;
|
||||
var hy = y + 1;
|
||||
var box_ly = Position.Y - (Size.Y / 2.0f) - toleranceY;
|
||||
var box_hy = Position.Y + (Size.Y / 2.0f) + toleranceY;
|
||||
if (box_ly > box_hy || !(box_ly <= hy && ly <= box_hy))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
520
pkNX.WinForms/Dumping/SV/EncounterDumperSV.cs
Normal file
520
pkNX.WinForms/Dumping/SV/EncounterDumperSV.cs
Normal file
@@ -0,0 +1,520 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using pkNX.Containers;
|
||||
using pkNX.Structures.FlatBuffers;
|
||||
|
||||
namespace pkNX.WinForms;
|
||||
|
||||
public class EncounterDumperSV
|
||||
{
|
||||
private readonly IFileInternal ROM;
|
||||
private const float tolX = 30f;
|
||||
private const float tolY = 30f;
|
||||
private const float tolZ = 30f;
|
||||
|
||||
public EncounterDumperSV(IFileInternal rom) => ROM = rom;
|
||||
|
||||
public void DumpTo(string path, IReadOnlyList<string> specNames, IReadOnlyList<string> moveNames,
|
||||
Dictionary<string, (string Name, int Index)> placeNameMap,
|
||||
bool writeText = true, bool writePickle = true)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
var field = new PaldeaFieldModel(ROM);
|
||||
var scene = new PaldeaSceneModel(ROM, field);
|
||||
var fsym = new PaldeaFixedSymbolModel(ROM);
|
||||
var csym = new PaldeaCoinSymbolModel(ROM);
|
||||
var mlEncPoints = FlatBufferConverter.DeserializeFrom<PointDataArray>(ROM.GetPackedFile("world/data/encount/point_data/point_data/encount_data_100000.bin"));
|
||||
var alEncPoints = FlatBufferConverter.DeserializeFrom<PointDataArray>(ROM.GetPackedFile("world/data/encount/point_data/point_data/encount_data_atlantis.bin"));
|
||||
var pokeData = FlatBufferConverter.DeserializeFrom<EncountPokeDataArray>(ROM.GetPackedFile("world/data/encount/pokedata/pokedata/pokedata_array.bin"));
|
||||
|
||||
var db = new LocationDatabase();
|
||||
|
||||
// Overall Logic Flow:
|
||||
// 1 - At every point, spawn everything that can exist at that point.
|
||||
// 2 - At each area, absorb native points into local points.
|
||||
// 3 - At each area, absorb crossover points into crossover points.
|
||||
// 4 - Consolidate the encounters from both point lists.
|
||||
// Just compute everything (big memory!) then crunch it all down.
|
||||
|
||||
// Points can be used by multiple areas as crossover sources. Need to be able to "belong" to multiple areas, and indicate their parent area.
|
||||
var pointMain = ReformatPoints(mlEncPoints);
|
||||
var pointAtlantis = ReformatPoints(alEncPoints);
|
||||
|
||||
// Fill the point lists for each area, then spawn everything into those points.
|
||||
foreach (var areaName in scene.areaNames)
|
||||
{
|
||||
var areaInfo = scene.AreaInfos[areaName];
|
||||
|
||||
// Determine potential spawners
|
||||
if (!scene.TryGetContainsCheck(areaName, out var collider))
|
||||
{
|
||||
Console.WriteLine($"No collider for {areaName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Locations that do not spawn encounters can still have crossovers bleed into them.
|
||||
// We'll have empty local encounter lists for them.
|
||||
var name = areaInfo.LocationNameMain;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
continue;
|
||||
var storage = db.Get(placeNameMap[name].Index, areaName, areaInfo);
|
||||
if (areaInfo.Tag is AreaTag.NG_Encount or AreaTag.NG_All)
|
||||
continue;
|
||||
|
||||
var points = scene.isAtlantis[areaName] ? pointAtlantis : pointMain;
|
||||
storage.LoadPoints(points, collider, areaInfo.ActualMinLevel, areaInfo.ActualMaxLevel);
|
||||
storage.GetEncounters(pokeData, scene);
|
||||
}
|
||||
|
||||
// For each area, we need to peek at the other areas to see if they have any crossover points.
|
||||
// For each of those crossover points, we need to see if they are in the current area's collider.
|
||||
// If they are, we need to add them to the current area's list of crossover points.
|
||||
foreach (var areaName in scene.areaNames)
|
||||
{
|
||||
// Same sanity checking as above iteration.
|
||||
var areaInfo = scene.AreaInfos[areaName];
|
||||
|
||||
// Determine potential spawners
|
||||
if (!scene.TryGetContainsCheck(areaName, out var collider))
|
||||
{
|
||||
Console.WriteLine($"No collider for {areaName}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = areaInfo.LocationNameMain;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
continue;
|
||||
//if (areaInfo.Tag is AreaTag.NG_Encount or AreaTag.NG_All)
|
||||
// continue;
|
||||
|
||||
var storage = db.Get(placeNameMap[name].Index, areaName, areaInfo);
|
||||
// Here's where the fun begins. Iterate over areas inside this loop so we can look for all possible adjacent areas.
|
||||
foreach (var otherName in scene.areaNames)
|
||||
{
|
||||
// Skip self
|
||||
if (otherName == areaName)
|
||||
continue;
|
||||
// Skip areas that don't have a location name
|
||||
var otherAreaInfo = scene.AreaInfos[otherName];
|
||||
var otherNameMain = otherAreaInfo.LocationNameMain;
|
||||
if (string.IsNullOrEmpty(otherNameMain))
|
||||
continue;
|
||||
|
||||
// Skip areas that don't have a collider
|
||||
if (!scene.TryGetContainsCheck(otherName, out _))
|
||||
continue;
|
||||
|
||||
// Iterate over all crossover points in the other area.
|
||||
var cross = db.Get(placeNameMap[otherNameMain].Index, otherName, otherAreaInfo);
|
||||
foreach (var point in cross.Local)
|
||||
{
|
||||
// If the crossover point is close enough to the current area's collider, add it to the current area's list of crossover points.
|
||||
if (collider.ContainsPoint(point.X, point.Y, point.Z, tolX, tolY, tolZ))
|
||||
storage.Nearby.Add(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Each area and their local / crossover points have been aggregated.
|
||||
// Integrate the points' slots into a single list, and consolidate entries with same level ranges to as few objects as possible.
|
||||
foreach (var storage in db.Locations.Values)
|
||||
{
|
||||
// Consolidate encounters
|
||||
storage.Integrate();
|
||||
storage.Consolidate();
|
||||
}
|
||||
|
||||
// Output to stream if available.
|
||||
if (writeText)
|
||||
{
|
||||
using var sw = File.CreateText(Path.Combine(path, "titan_enc.txt"));
|
||||
foreach (var s in db.Locations.Values)
|
||||
WriteLocation(sw, specNames, placeNameMap, s.AreaName, s.AreaInfo, s.Slots);
|
||||
using var tw = File.CreateText(Path.Combine(path, "titan_loc_enc.txt"));
|
||||
WriteLocEncList(tw, specNames, placeNameMap, db);
|
||||
|
||||
using var pw = File.CreateText(Path.Combine(path, "titan_loc_point.txt"));
|
||||
WriteLocPointList(pw, placeNameMap, db);
|
||||
}
|
||||
if (writePickle)
|
||||
{
|
||||
string binPath = Path.Combine(path, "encounter_wild_paldea.pkl");
|
||||
SerializeEncounters(binPath, db);
|
||||
}
|
||||
|
||||
// Fixed symbols
|
||||
List<byte[]> serialized = new();
|
||||
int[] bannedIndexes =
|
||||
{
|
||||
31, // Lighthouse Wingull
|
||||
};
|
||||
var fsymData = FlatBufferConverter.DeserializeFrom<FixedSymbolTableArray>(ROM.GetPackedFile("world/data/field/fixed_symbol/fixed_symbol_table/fixed_symbol_table_array.bin"));
|
||||
var eventBattle = FlatBufferConverter.DeserializeFrom<EventBattlePokemonArray>(ROM.GetPackedFile("world/data/battle/eventBattlePokemon/eventBattlePokemon_array.bin"));
|
||||
foreach (var (game, gamePoints) in new[] { ("sl", fsym.scarletPoints), ("vl", fsym.violetPoints)})
|
||||
{
|
||||
using var gw = File.CreateText(Path.Combine(path, $"titan_fixed_{game}.txt"));
|
||||
for (var i = 0; i < fsymData.Table.Length; i++)
|
||||
{
|
||||
FixedSymbolTable? entry = fsymData.Table[i];
|
||||
var tableKey = entry.TableKey;
|
||||
|
||||
var points = gamePoints.Where(p => p.TableKey == tableKey).ToList();
|
||||
if (points.Count == 0)
|
||||
continue;
|
||||
|
||||
var areas = new List<string>();
|
||||
foreach (var areaName in scene.areaNames)
|
||||
{
|
||||
if (scene.isAtlantis[areaName])
|
||||
continue;
|
||||
|
||||
var areaInfo = scene.AreaInfos[areaName];
|
||||
var name = areaInfo.LocationNameMain;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
continue;
|
||||
if (areaInfo.Tag is AreaTag.NG_Encount or AreaTag.NG_All)
|
||||
continue;
|
||||
|
||||
if (points.Any(p => scene.IsPointContained(areaName, p.Position.X, p.Position.Y, p.Position.Z)))
|
||||
areas.Add(areaName);
|
||||
}
|
||||
|
||||
var locs = areas.Select(a => placeNameMap[scene.AreaInfos[a].LocationNameMain].Index).Distinct().ToList();
|
||||
|
||||
gw.WriteLine("===");
|
||||
gw.WriteLine(entry.TableKey);
|
||||
gw.WriteLine("===");
|
||||
gw.WriteLine(" PokeData:");
|
||||
var pd = entry.PokeDataSymbol;
|
||||
gw.WriteLine($" Species: {specNames[(int)pd.DevId]}");
|
||||
gw.WriteLine($" Form: {pd.FormId}");
|
||||
gw.WriteLine($" Level: {pd.Level}");
|
||||
gw.WriteLine($" Sex: {new[] { "Random", "Male", "Female" }[(int)pd.Sex]}");
|
||||
gw.WriteLine($" Shiny: {new[] { "Random", "Never", "Always" }[(int)pd.RareType]}");
|
||||
|
||||
var talentStr = pd.TalentType switch
|
||||
{
|
||||
TalentType.RANDOM => "Random",
|
||||
TalentType.V_NUM => $"{pd.TalentVNum} Perfect",
|
||||
TalentType.VALUE => $"{pd.TalentValue.HP}/{pd.TalentValue.ATK}/{pd.TalentValue.DEF}/{pd.TalentValue.SPA}/{pd.TalentValue.SPD}/{pd.TalentValue.SPE}",
|
||||
_ => "Invalid",
|
||||
};
|
||||
gw.WriteLine($" IVs: {talentStr}");
|
||||
gw.WriteLine($" Ability: {new[] { "1/2", "1/2/3", "1", "2", "3" }[(int)pd.TokuseiIndex]}");
|
||||
switch (pd.WazaType)
|
||||
{
|
||||
case WazaType.DEFAULT:
|
||||
gw.WriteLine(" Moves: Random");
|
||||
break;
|
||||
case WazaType.MANUAL:
|
||||
gw.WriteLine($" Moves: {moveNames[(int)pd.Waza1.WazaId]}/{moveNames[(int)pd.Waza2.WazaId]}/{moveNames[(int)pd.Waza3.WazaId]}/{moveNames[(int)pd.Waza4.WazaId]}");
|
||||
break;
|
||||
}
|
||||
|
||||
gw.WriteLine($" Scale: {new[] { "Random", "XS", "S", "M", "L", "XL", $"{pd.ScaleValue}" }[(int)pd.ScaleType]}");
|
||||
gw.WriteLine($" GemType: {(int)pd.GemType}");
|
||||
|
||||
gw.WriteLine(" Points:");
|
||||
foreach (var point in points)
|
||||
{
|
||||
gw.WriteLine($" - ({point.Position.X}, {point.Position.Y}, {point.Position.Z})");
|
||||
}
|
||||
gw.WriteLine(" Areas:");
|
||||
foreach (var areaName in areas)
|
||||
{
|
||||
var areaInfo = scene.AreaInfos[areaName];
|
||||
var loc = areaInfo.LocationNameMain;
|
||||
(string name, int index) = placeNameMap[loc];
|
||||
gw.WriteLine($" - {areaName} - {loc} - {name} ({index})");
|
||||
}
|
||||
|
||||
// Serialize
|
||||
if (locs.Count == 0)
|
||||
continue;
|
||||
if (bannedIndexes.Contains(i))
|
||||
continue;
|
||||
|
||||
areas.Clear();
|
||||
foreach (var areaName in scene.areaNames)
|
||||
{
|
||||
if (scene.isAtlantis[areaName])
|
||||
continue;
|
||||
|
||||
var areaInfo = scene.AreaInfos[areaName];
|
||||
var name = areaInfo.LocationNameMain;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
continue;
|
||||
if (areaInfo.Tag is AreaTag.NG_Encount or AreaTag.NG_All)
|
||||
continue;
|
||||
|
||||
if (!scene.TryGetContainsCheck(areaName, out var collider))
|
||||
continue;
|
||||
|
||||
if (points.Any(p => collider.ContainsPoint(p.Position.X, p.Position.Y, p.Position.Z, tolX, tolY, tolZ)))
|
||||
areas.Add(areaName);
|
||||
}
|
||||
|
||||
locs = areas.Select(a => placeNameMap[scene.AreaInfos[a].LocationNameMain].Index).Distinct().ToList();
|
||||
WriteFixedSymbol(serialized, entry, locs);
|
||||
}
|
||||
}
|
||||
|
||||
using var cw = File.CreateText(Path.Combine(path, "titan_coin_symbol.txt"));
|
||||
foreach (var entry in csym.Points)
|
||||
{
|
||||
var areas = new List<string>();
|
||||
foreach (var areaName in scene.areaNames)
|
||||
{
|
||||
if (scene.isAtlantis[areaName])
|
||||
continue;
|
||||
|
||||
var areaInfo = scene.AreaInfos[areaName];
|
||||
var name = areaInfo.LocationNameMain;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
continue;
|
||||
if (areaInfo.Tag is AreaTag.NG_Encount or AreaTag.NG_All)
|
||||
continue;
|
||||
|
||||
if (scene.IsPointContained(areaName, entry.Position.X, entry.Position.Y, entry.Position.Z))
|
||||
areas.Add(areaName);
|
||||
}
|
||||
|
||||
var locs = areas.Select(a => placeNameMap[scene.AreaInfos[a].LocationNameMain].Index).Distinct().ToList();
|
||||
|
||||
cw.WriteLine("===");
|
||||
cw.WriteLine(entry.Name);
|
||||
cw.WriteLine("===");
|
||||
cw.WriteLine($" First Num: {entry.FirstNum}");
|
||||
cw.WriteLine($" Coordinates: ({entry.Position.X}, {entry.Position.Y}, {entry.Position.Z})");
|
||||
|
||||
if (entry.IsBox)
|
||||
{
|
||||
cw.WriteLine($" Box Label: {entry.BoxLabel}");
|
||||
cw.WriteLine(" PokeData:");
|
||||
var pd = Array.Find(eventBattle.Table, e => e.Label == entry.BoxLabel)!.PokeData;
|
||||
|
||||
cw.WriteLine($" Species: {specNames[(int)pd.DevId]}");
|
||||
cw.WriteLine($" Form: {pd.FormId}");
|
||||
cw.WriteLine($" Level: {pd.Level}");
|
||||
cw.WriteLine($" Sex: {new[] { "Random", "Male", "Female" }[(int)pd.Sex]}");
|
||||
cw.WriteLine($" Shiny: {new[] { "Random", "Never", "Always" }[(int)pd.RareType]}");
|
||||
|
||||
var talentStr = pd.TalentType switch
|
||||
{
|
||||
TalentType.RANDOM => "Random",
|
||||
TalentType.V_NUM => $"{pd.TalentVnum} Perfect",
|
||||
TalentType.VALUE => $"{pd.TalentValue.HP}/{pd.TalentValue.ATK}/{pd.TalentValue.DEF}/{pd.TalentValue.SPA}/{pd.TalentValue.SPD}/{pd.TalentValue.SPE}",
|
||||
_ => "Invalid",
|
||||
};
|
||||
cw.WriteLine($" IVs: {talentStr}");
|
||||
cw.WriteLine($" Ability: {new[] { "1/2", "1/2/3", "1", "2", "3" }[(int)pd.Tokusei]}");
|
||||
switch (pd.WazaType)
|
||||
{
|
||||
case WazaType.DEFAULT:
|
||||
cw.WriteLine($" Moves: Random");
|
||||
break;
|
||||
case WazaType.MANUAL:
|
||||
cw.WriteLine($" Moves: {moveNames[(int)pd.Waza1.WazaId]}/{moveNames[(int)pd.Waza2.WazaId]}/{moveNames[(int)pd.Waza3.WazaId]}/{moveNames[(int)pd.Waza4.WazaId]}");
|
||||
break;
|
||||
}
|
||||
|
||||
cw.WriteLine($" Scale: {new[] { "Random", "XS", "S", "M", "L", "XL", $"{pd.ScaleValue}" }[(int)pd.ScaleType]}");
|
||||
cw.WriteLine($" GemType: {(int)pd.GemType}");
|
||||
}
|
||||
|
||||
cw.WriteLine(" Areas:");
|
||||
foreach (var areaName in areas)
|
||||
{
|
||||
var areaInfo = scene.AreaInfos[areaName];
|
||||
var loc = areaInfo.LocationNameMain;
|
||||
(string name, int index) = placeNameMap[loc];
|
||||
cw.WriteLine($" - {areaName} - {loc} - {name} ({index})");
|
||||
}
|
||||
}
|
||||
var pathPickle = Path.Combine(path, "encounter_fixed_paldea.pkl");
|
||||
var ordered = serialized
|
||||
.OrderBy(z => BinaryPrimitives.ReadUInt16LittleEndian(z)) // Species
|
||||
.ThenBy(z => z[2]) // Form
|
||||
.ThenBy(z => z[3]) // Level
|
||||
;
|
||||
File.WriteAllBytes(pathPickle, ordered.SelectMany(z => z).ToArray());
|
||||
}
|
||||
|
||||
private static LocationPointDetail[] ReformatPoints(PointDataArray all)
|
||||
{
|
||||
var arr = all.Table;
|
||||
var result = new LocationPointDetail[arr.Length];
|
||||
for (int i = 0; i < arr.Length; i++)
|
||||
result[i] = new LocationPointDetail(arr[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void WriteFixedSymbol(ICollection<byte[]> exist, FixedSymbolTable entry, IReadOnlyList<int> locs)
|
||||
{
|
||||
var enc = entry.PokeDataSymbol;
|
||||
using var ms = new MemoryStream();
|
||||
using var bw = new BinaryWriter(ms);
|
||||
|
||||
bw.Write((ushort)enc.DevId);
|
||||
bw.Write((byte)enc.FormId);
|
||||
bw.Write((byte)enc.Level);
|
||||
|
||||
bw.Write((byte)enc.TalentVNum);
|
||||
bw.Write((byte)enc.GemType);
|
||||
bw.Write((byte)(enc.Sex - 1));
|
||||
bw.Write((byte)0); // reserved
|
||||
|
||||
bw.Write((ushort)enc.Waza1.WazaId);
|
||||
bw.Write((ushort)enc.Waza2.WazaId);
|
||||
|
||||
bw.Write((ushort)enc.Waza3.WazaId);
|
||||
bw.Write((ushort)enc.Waza4.WazaId);
|
||||
|
||||
// At most 3 locations, but just use 4 bytes.
|
||||
Span<byte> temp = stackalloc byte[4];
|
||||
if (locs.Count > temp.Length)
|
||||
throw new ArgumentException("Too many locations??", nameof(locs));
|
||||
for (int i = 0; i < locs.Count; i++)
|
||||
temp[i] = (byte)locs[i];
|
||||
bw.Write(temp);
|
||||
|
||||
var result = ms.ToArray();
|
||||
if (!exist.Any(x => x.SequenceEqual(result)))
|
||||
exist.Add(result);
|
||||
}
|
||||
|
||||
private static void SerializeEncounters(string binPath, LocationDatabase db)
|
||||
{
|
||||
var tables = db.Locations.Values;
|
||||
int ctr = 0;
|
||||
var result = new byte[tables.Count + tables.Sum(z => z.SlotsCrossover.Count)][];
|
||||
foreach (var x in tables)
|
||||
{
|
||||
result[ctr++] = SerializeLocationSet((ushort)x.Location, 0, x.Slots);
|
||||
foreach (var sub in x.SlotsCrossover)
|
||||
result[ctr++] = SerializeLocationSet((ushort)x.Location, (ushort)sub.Key, sub.Value.Slots);
|
||||
}
|
||||
var mini = MiniUtil.PackMini(result, "sv");
|
||||
File.WriteAllBytes(binPath, mini);
|
||||
}
|
||||
|
||||
private static byte[] SerializeLocationSet(ushort loc, ushort crossover, IReadOnlyList<PaldeaEncounter> slots)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
using var bw = new BinaryWriter(ms);
|
||||
bw.Write(loc);
|
||||
bw.Write(crossover);
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
bw.Write((ushort)slot.Species);
|
||||
bw.Write((byte)slot.Form);
|
||||
bw.Write((byte)slot.Gender);
|
||||
|
||||
bw.Write((byte)slot.MinLevel);
|
||||
bw.Write((byte)slot.MaxLevel);
|
||||
bw.Write((byte)slot.Time);
|
||||
bw.Write((byte)0);
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
private static PokeDataSymbol GetPokeDataSymbol(FixedSymbolTableArray fsymTable, string tableKey)
|
||||
{
|
||||
foreach (var entry in fsymTable.Table)
|
||||
{
|
||||
if (entry.TableKey == tableKey)
|
||||
return entry.PokeDataSymbol;
|
||||
}
|
||||
throw new ArgumentException($"TableKey not found ({tableKey})");
|
||||
}
|
||||
|
||||
private static void WriteLocation(TextWriter tw, IReadOnlyList<string> specNames,
|
||||
IReadOnlyDictionary<string, (string Name, int Index)> placeNameMap,
|
||||
string areaName, AreaInfo areaInfo, List<PaldeaEncounter> encounts)
|
||||
{
|
||||
var loc = areaInfo.LocationNameMain;
|
||||
(string name, int index) = placeNameMap[loc];
|
||||
var heading = $"{areaName} - {loc} - {name} ({index})";
|
||||
WriteEncounts(tw, specNames, heading, encounts);
|
||||
}
|
||||
|
||||
private static void WriteLocEncList(TextWriter tw, IReadOnlyList<string> specNames,
|
||||
IReadOnlyDictionary<string, (string Name, int Index)> placeNameMap,
|
||||
LocationDatabase db)
|
||||
{
|
||||
foreach (var place in placeNameMap.Keys.OrderBy(p => placeNameMap[p].Index))
|
||||
{
|
||||
(string name, int index) = placeNameMap[place];
|
||||
if (!db.Locations.TryGetValue(index, out var encounts))
|
||||
continue;
|
||||
|
||||
var heading = $"{name} ({index})";
|
||||
WriteEncounts(tw, specNames, heading, encounts.Slots);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteLocPointList(TextWriter tw,
|
||||
IReadOnlyDictionary<string, (string Name, int Index)> placeNameMap,
|
||||
LocationDatabase db)
|
||||
{
|
||||
foreach (var place in placeNameMap.Keys.OrderBy(p => placeNameMap[p].Index))
|
||||
{
|
||||
(string name, int index) = placeNameMap[place];
|
||||
if (!db.Locations.TryGetValue(index, out var loc))
|
||||
continue;
|
||||
|
||||
var heading = $"{name} ({index})";
|
||||
WriteBiomes(tw, heading, loc.Local);
|
||||
}
|
||||
|
||||
foreach (var place in placeNameMap.Keys.OrderBy(p => placeNameMap[p].Index))
|
||||
{
|
||||
(string name, int index) = placeNameMap[place];
|
||||
if (!db.Locations.TryGetValue(index, out var loc))
|
||||
continue;
|
||||
|
||||
var heading = $"{name} ({index})";
|
||||
WritePoints(tw, heading, loc.Local);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteBiomes(TextWriter tw, string heading, List<LocationPointDetail> points)
|
||||
{
|
||||
var biomes = points.Select(z => z.Point.Biome).Distinct().Select(z => z.ToString()).OrderBy(z => z);
|
||||
var btext = string.Join(',', biomes);
|
||||
tw.WriteLine($"{heading}\t{btext}");
|
||||
}
|
||||
|
||||
private static void WritePoints(TextWriter tw, string heading, List<LocationPointDetail> points)
|
||||
{
|
||||
tw.WriteLine("===");
|
||||
tw.WriteLine(heading);
|
||||
tw.WriteLine("===");
|
||||
foreach (var e in points)
|
||||
tw.WriteLine($" - {e.GetString()}");
|
||||
tw.WriteLine();
|
||||
}
|
||||
|
||||
private static void WriteEncounts(TextWriter tw, IReadOnlyList<string> specNames, string heading, IEnumerable<PaldeaEncounter> encounts)
|
||||
{
|
||||
tw.WriteLine("===");
|
||||
tw.WriteLine(heading);
|
||||
tw.WriteLine("===");
|
||||
foreach (var e in encounts)
|
||||
tw.WriteLine($" - {e.GetEncountString(specNames)}");
|
||||
tw.WriteLine();
|
||||
}
|
||||
|
||||
public static Dictionary<string, (string Name, int Index)> GetPlaceNameMap(string[] text, AHTB ahtb)
|
||||
{
|
||||
var result = new Dictionary<string, (string Name, int Index)>();
|
||||
for (var i = 0; i < text.Length; i++)
|
||||
result[ahtb.Entries[i].Name] = (text[i], i);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
15
pkNX.WinForms/Dumping/SV/LocationDatabase.cs
Normal file
15
pkNX.WinForms/Dumping/SV/LocationDatabase.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace pkNX.Structures.FlatBuffers;
|
||||
|
||||
public class LocationDatabase
|
||||
{
|
||||
public readonly Dictionary<int, LocationStorage> Locations = new();
|
||||
|
||||
public LocationStorage Get(int location, string areaName, AreaInfo areaInfo)
|
||||
{
|
||||
if (!Locations.TryGetValue(location, out var loc))
|
||||
Locations[location] = loc = new LocationStorage(location, areaName, areaInfo);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
28
pkNX.WinForms/Dumping/SV/LocationPointDetail.cs
Normal file
28
pkNX.WinForms/Dumping/SV/LocationPointDetail.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace pkNX.Structures.FlatBuffers;
|
||||
|
||||
public class LocationPointDetail
|
||||
{
|
||||
public readonly PointData Point;
|
||||
|
||||
public LocationPointDetail(PointData point) => Point = point;
|
||||
public float X => Point.Position.X;
|
||||
public float Y => Point.Position.Y;
|
||||
public float Z => Point.Position.Z;
|
||||
public int Location { get; init; }
|
||||
|
||||
public bool IsWithinLevelRange(int areaMin, int areaMax)
|
||||
{
|
||||
return Point.LevelRange.X <= areaMax && areaMin <= Point.LevelRange.Y;
|
||||
}
|
||||
|
||||
public readonly List<PaldeaEncounter> Slots = new();
|
||||
|
||||
public void Add(PaldeaEncounter slot) => Slots.Add(slot);
|
||||
|
||||
public string GetString()
|
||||
{
|
||||
return $"{Location:0000} ({X:0.0000},{Y:0.0000},{Z:0.0000}) {Point.Biome} ({Point.LevelRange.X}-{Point.LevelRange.Y}) {Slots.Count:00}";
|
||||
}
|
||||
}
|
||||
228
pkNX.WinForms/Dumping/SV/LocationStorage.cs
Normal file
228
pkNX.WinForms/Dumping/SV/LocationStorage.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace pkNX.Structures.FlatBuffers;
|
||||
|
||||
public class LocationStorage
|
||||
{
|
||||
public readonly int Location;
|
||||
public readonly AreaInfo AreaInfo;
|
||||
public readonly string AreaName;
|
||||
|
||||
public readonly HashSet<ulong> Added = new();
|
||||
public readonly List<PaldeaEncounter> Slots = new();
|
||||
public readonly Dictionary<int, LocationStorage> SlotsCrossover = new();
|
||||
public readonly List<LocationPointDetail> Local = new();
|
||||
public readonly List<LocationPointDetail> Nearby = new();
|
||||
|
||||
public LocationStorage(int loc, string areaName, AreaInfo info)
|
||||
{
|
||||
Location = loc;
|
||||
AreaName = areaName;
|
||||
AreaInfo = info;
|
||||
}
|
||||
|
||||
private void Add(PaldeaEncounter slot)
|
||||
{
|
||||
var key = slot.GetHash();
|
||||
if (Added.Add(key))
|
||||
Slots.Add(slot);
|
||||
}
|
||||
|
||||
private void AddCrossover(PaldeaEncounter slot, int loc)
|
||||
{
|
||||
if (!SlotsCrossover.TryGetValue(loc, out var s))
|
||||
SlotsCrossover[loc] = s = new LocationStorage(loc, AreaName, AreaInfo);
|
||||
s.Add(slot with { CrossFromLocation = (ushort)Location });
|
||||
}
|
||||
|
||||
public void Integrate()
|
||||
{
|
||||
foreach (var point in Local)
|
||||
{
|
||||
foreach (var slot in point.Slots)
|
||||
Add(slot);
|
||||
}
|
||||
foreach (var point in Nearby)
|
||||
{
|
||||
foreach (var slot in point.Slots)
|
||||
AddCrossover(slot, point.Location);
|
||||
}
|
||||
}
|
||||
|
||||
public void Consolidate()
|
||||
{
|
||||
ConsolidateEncounters(Slots);
|
||||
foreach (var loc in SlotsCrossover.Values)
|
||||
loc.Consolidate();
|
||||
}
|
||||
|
||||
private static void ConsolidateEncounters(List<PaldeaEncounter> encounters)
|
||||
{
|
||||
// Merge and remove future indexes if they can be combined with current
|
||||
// Pre-sort so we can just iterate.
|
||||
encounters.Sort();
|
||||
|
||||
for (int i = 0; i < encounters.Count;)
|
||||
{
|
||||
// For i to i+count, merge within
|
||||
var enc = encounters[i];
|
||||
int count = GetPotentialConsolidationCount(encounters, i, enc);
|
||||
|
||||
// Remove all indexes that have Absorb return true with a later element
|
||||
while (TryAbsorbFromRange(encounters, i, count))
|
||||
count--;
|
||||
i += count;
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetPotentialConsolidationCount(List<PaldeaEncounter> encounters, int start, PaldeaEncounter enc)
|
||||
{
|
||||
int count = 1;
|
||||
for (int end = start + 1; end < encounters.Count; end++)
|
||||
{
|
||||
if (!encounters[end].IsSameSpecFormGender(enc))
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static bool TryAbsorbFromRange(List<PaldeaEncounter> encounters, int start, int count)
|
||||
{
|
||||
// Need to compare all elements with each-other; return true if any absorb.
|
||||
var end = start + count - 1;
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
for (int j = i + 1; j <= end; j++)
|
||||
{
|
||||
if (!encounters[i].Absorb(encounters[j]))
|
||||
continue;
|
||||
encounters.RemoveAt(j);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void LoadPoints(IEnumerable<LocationPointDetail> points, IContainsV3f collider, int areaMin, int areaMax)
|
||||
{
|
||||
foreach (var w in points)
|
||||
{
|
||||
if (!w.IsWithinLevelRange(areaMin, areaMax))
|
||||
continue;
|
||||
if (collider.ContainsPoint(w.X, w.Y, w.Z))
|
||||
{
|
||||
var composite = GetCompositePoint(w.Point, areaMin, areaMax, Location);
|
||||
Local.Add(composite); // native
|
||||
}
|
||||
// Don't check here for cross-area, as our areaMin/areaMax is not valid for that crossover case.
|
||||
}
|
||||
}
|
||||
|
||||
private static LocationPointDetail GetCompositePoint(PointData ep, int areaMin, int areaMax, int location)
|
||||
{
|
||||
var newX = Math.Max(ep.LevelRange.X, areaMin);
|
||||
var newY = Math.Min(ep.LevelRange.Y, areaMax);
|
||||
var newPoint = new PointData
|
||||
{
|
||||
Position = ep.Position,
|
||||
LevelRange = new PackedVec2f { X = newX, Y = newY },
|
||||
Biome = ep.Biome,
|
||||
Substance = ep.Substance,
|
||||
AreaNo = ep.AreaNo,
|
||||
};
|
||||
return new LocationPointDetail(newPoint) { Location = location };
|
||||
}
|
||||
|
||||
public void GetEncounters(EncountPokeDataArray pokeData, PaldeaSceneModel scene)
|
||||
{
|
||||
foreach (var spawner in Local)
|
||||
{
|
||||
foreach (var pd in pokeData.Table)
|
||||
{
|
||||
if (!IsAbleToSpawnAt(pd, spawner, AreaName, scene))
|
||||
continue;
|
||||
|
||||
// Add encount
|
||||
spawner.Add(PaldeaEncounter.GetNew(pd, spawner.Point));
|
||||
if (pd.BandPoke != 0) // Add band encount
|
||||
spawner.Add(PaldeaEncounter.GetBand(pd, spawner.Point));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAbleToSpawnAt(EncountPokeData pd, LocationPointDetail ep, string areaName, PaldeaSceneModel scene)
|
||||
{
|
||||
// Check area
|
||||
if (!string.IsNullOrEmpty(pd.Area) && !IsInArea(pd.Area, ep.Point.AreaNo))
|
||||
return false;
|
||||
|
||||
// Check loc
|
||||
if (!string.IsNullOrEmpty(pd.LocationName) && !IsInArea(pd.LocationName, areaName, scene, ep.Point))
|
||||
return false;
|
||||
|
||||
// Check biome
|
||||
if (!HasBiome(pd, (Biome)(int)ep.Point.Biome))
|
||||
return false;
|
||||
|
||||
// check level range overlap
|
||||
// check area level range overlap -- already done via point
|
||||
if (!LevelWithinRange(pd, ep.Point))
|
||||
return false;
|
||||
|
||||
// Assume flag, enable table, timetable are fine
|
||||
// Assume version is fine -- union wireless sessions can share encounters.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool LevelWithinRange(EncountPokeData pd, PointData clamp)
|
||||
{
|
||||
return clamp.LevelRange.X <= pd.MaxLevel && pd.MinLevel <= clamp.LevelRange.Y;
|
||||
}
|
||||
|
||||
private static bool HasBiome(EncountPokeData pd, Biome biome)
|
||||
{
|
||||
if (biome == pd.Biome1)
|
||||
return true;
|
||||
if (biome == pd.Biome2)
|
||||
return true;
|
||||
if (biome == pd.Biome3)
|
||||
return true;
|
||||
if (biome == pd.Biome4)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsInArea(ReadOnlySpan<char> areaName, int areaNo)
|
||||
{
|
||||
int start = 0;
|
||||
for (int i = 0; i < areaName.Length; i++)
|
||||
{
|
||||
if (areaName[i] != ',')
|
||||
continue;
|
||||
var name = areaName[start..i];
|
||||
if (int.TryParse(name, out var tmp) && areaNo == tmp)
|
||||
return true;
|
||||
start = i + 1;
|
||||
}
|
||||
return int.TryParse(areaName[start..], out var x) && areaNo == x;
|
||||
}
|
||||
|
||||
private static bool IsInArea(string locName, string areaName, PaldeaSceneModel scene, PointData ep)
|
||||
{
|
||||
var split = locName.Split(",");
|
||||
foreach (string a in split)
|
||||
{
|
||||
if (a == areaName)
|
||||
return true;
|
||||
|
||||
if (!scene.IsPointContained(a, ep.Position.X, ep.Position.Y, ep.Position.Z))
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
85
pkNX.WinForms/Dumping/SV/PaldeaCoinSymbolModel.cs
Normal file
85
pkNX.WinForms/Dumping/SV/PaldeaCoinSymbolModel.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using pkNX.Containers;
|
||||
using pkNX.Structures.FlatBuffers;
|
||||
|
||||
namespace pkNX.WinForms;
|
||||
|
||||
public class PaldeaCoinSymbolModel
|
||||
{
|
||||
public readonly List<PaldeaCoinSymbolPoint> Points;
|
||||
|
||||
public PaldeaCoinSymbolModel(IFileInternal ROM)
|
||||
{
|
||||
Points = new List<PaldeaCoinSymbolPoint>();
|
||||
|
||||
var cData = ROM.GetPackedFile("world/scene/parts/field/streaming_event/world_coin_placement_symbol_/world_coin_placement_symbol_0.trscn");
|
||||
// NOTE: Fine to only use Scarlet, Violet data is identical.
|
||||
|
||||
var c = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplateSV>(cData);
|
||||
|
||||
Points.AddRange(GetObjectTemplateSymbolPoints(c));
|
||||
}
|
||||
|
||||
private IEnumerable<PaldeaCoinSymbolPoint> GetObjectTemplateSymbolPoints(TrinitySceneObjectTemplateSV template)
|
||||
{
|
||||
foreach (var obj in template.Objects)
|
||||
{
|
||||
switch (obj.Type)
|
||||
{
|
||||
case "trinity_ScenePoint":
|
||||
{
|
||||
var scenePoint = FlatBufferConverter.DeserializeFrom<TrinityScenePointSV>(obj.Data);
|
||||
|
||||
if (obj.SubObjects.Length != 1)
|
||||
throw new ArgumentException($"Unexpected CoinSymbolPoint SubObject Count {obj.SubObjects.Length}");
|
||||
|
||||
if (obj.SubObjects[0].Type != "trinity_PropertySheet")
|
||||
throw new ArgumentException($"Unexpected CoinSymbolPoint SubObject ({obj.SubObjects[0].Type})");
|
||||
|
||||
var propSheet = FlatBufferConverter.DeserializeFrom<TrinityPropertySheetSV>(obj.SubObjects[0].Data);
|
||||
|
||||
yield return ParseCoinSymbolPoint(scenePoint, propSheet);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported CoinPlacement Object Type {obj.Type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PaldeaCoinSymbolPoint ParseCoinSymbolPoint(TrinityScenePointSV sp, TrinityPropertySheetSV ps)
|
||||
{
|
||||
switch (ps.Name)
|
||||
{
|
||||
case "coin_walk_symbol_point":
|
||||
return new PaldeaCoinSymbolPoint(sp.Name, GetFirstNum(ps), string.Empty, sp.Position);
|
||||
case "coin_box_symbol_point":
|
||||
return new PaldeaCoinSymbolPoint(sp.Name, GetFirstNum(ps), GetBoxLabel(ps), sp.Position);
|
||||
default:
|
||||
throw new ArgumentException($"Unknown CoinSymbol PropertySheet {ps.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
private ulong GetFirstNum(TrinityPropertySheetSV ps)
|
||||
{
|
||||
if (ps.Properties[0].Fields[0].Name != "firstNum")
|
||||
throw new ArgumentException("Invalid PropertySheet field layout");
|
||||
|
||||
if (!ps.Properties[0].Fields[0].Data.TryGet(out TrinityPropertySheetField1SV? sv))
|
||||
throw new ArgumentException("Could not get PropertySheet Table Key");
|
||||
|
||||
return sv.Value;
|
||||
}
|
||||
|
||||
private string GetBoxLabel(TrinityPropertySheetSV ps)
|
||||
{
|
||||
if (ps.Properties[0].Fields[1].Name != "label")
|
||||
throw new ArgumentException("Invalid PropertySheet field layout");
|
||||
|
||||
if (!ps.Properties[0].Fields[1].Data.TryGet(out TrinityPropertySheetFieldStringValueSV? sv))
|
||||
throw new ArgumentException("Could not get PropertySheet Table Key");
|
||||
|
||||
return sv.Value;
|
||||
}
|
||||
}
|
||||
26
pkNX.WinForms/Dumping/SV/PaldeaCoinSymbolPoint.cs
Normal file
26
pkNX.WinForms/Dumping/SV/PaldeaCoinSymbolPoint.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using pkNX.Structures.FlatBuffers;
|
||||
|
||||
namespace pkNX.WinForms;
|
||||
|
||||
public class PaldeaCoinSymbolPoint
|
||||
{
|
||||
public string Name;
|
||||
public ulong FirstNum;
|
||||
public string BoxLabel;
|
||||
public PackedVec3f Position;
|
||||
|
||||
public PaldeaCoinSymbolPoint(string name, ulong num, string boxLabel, PackedVec3f pos)
|
||||
{
|
||||
Name = name;
|
||||
FirstNum = num;
|
||||
BoxLabel = boxLabel;
|
||||
Position = new PackedVec3f
|
||||
{
|
||||
X = pos.X,
|
||||
Y = pos.Y,
|
||||
Z = pos.Z,
|
||||
};
|
||||
}
|
||||
|
||||
public bool IsBox => !string.IsNullOrEmpty(BoxLabel);
|
||||
}
|
||||
119
pkNX.WinForms/Dumping/SV/PaldeaEncounter.cs
Normal file
119
pkNX.WinForms/Dumping/SV/PaldeaEncounter.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace pkNX.Structures.FlatBuffers;
|
||||
|
||||
public record PaldeaEncounter(ushort Species, byte Form, byte Sex, byte MinLevel, byte MaxLevel, byte Time, ushort CrossFromLocation = 0) : IComparable<PaldeaEncounter>
|
||||
{
|
||||
public byte MinLevel { get; private set; } = MinLevel;
|
||||
public byte MaxLevel { get; private set; } = MaxLevel;
|
||||
|
||||
public int Gender => Sex switch
|
||||
{
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
// Match the Mark time of day order: Noon, Night, Evening, Morning
|
||||
private static byte GetTimeBits(TimeTable t) => (byte)((t.Noon ? 0 : 1) | (t.Night ? 0 : 2) | (t.Evening ? 0 : 4) | (t.Morning ? 0 : 8));
|
||||
|
||||
public static PaldeaEncounter GetNew(EncountPokeData pd, PointData ep)
|
||||
{
|
||||
// Combine the 4 bools into a single byte
|
||||
var time = GetTimeBits(pd.TimeTable);
|
||||
var min = (byte)Math.Max(ep.LevelRange.X, pd.MinLevel);
|
||||
var max = (byte)Math.Min(ep.LevelRange.Y, pd.MaxLevel);
|
||||
return new((ushort)pd.DevId, (byte)pd.Form, (byte)pd.Sex, min, max, time);
|
||||
}
|
||||
|
||||
public static PaldeaEncounter GetBand(EncountPokeData pd, PointData ep)
|
||||
{
|
||||
// Combine the 4 bools into a single byte
|
||||
var time = GetTimeBits(pd.TimeTable);
|
||||
var min = (byte)Math.Max(ep.LevelRange.X, pd.MinLevel);
|
||||
var max = (byte)Math.Min(ep.LevelRange.Y, pd.MaxLevel);
|
||||
return new((ushort)pd.BandPoke, (byte)pd.BandForm, (byte)pd.BandSex, min, max, time);
|
||||
}
|
||||
|
||||
public string GetEncountString(IReadOnlyList<string> specNames)
|
||||
{
|
||||
var species = specNames[Species];
|
||||
return GetString(species);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return GetString(((PKHeX.Core.Species)Species).ToString());
|
||||
}
|
||||
|
||||
private string GetString(string species)
|
||||
{
|
||||
var form = Form == 0 ? "" : $"-{Form}";
|
||||
var sex = Sex == 0 ? "" : $" (sex={Sex})";
|
||||
return $"{species}{form}{sex} Lv. {MinLevel}-{MaxLevel}";
|
||||
}
|
||||
|
||||
public bool Absorb(PaldeaEncounter other)
|
||||
{
|
||||
if (Time != other.Time)
|
||||
return false;
|
||||
if (CrossFromLocation != other.CrossFromLocation)
|
||||
return false;
|
||||
if (other.MinLevel == MinLevel && other.MaxLevel == MaxLevel)
|
||||
return true;
|
||||
|
||||
if (!IsLevelRangeOverlap(other) && !other.IsLevelRangeOverlap(this))
|
||||
return false;
|
||||
|
||||
MinLevel = Math.Min(MinLevel, other.MinLevel);
|
||||
MaxLevel = Math.Max(MaxLevel, other.MaxLevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsSameSpecFormGender(PaldeaEncounter other)
|
||||
{
|
||||
if (Species != other.Species || Form != other.Form)
|
||||
return false;
|
||||
if (Sex != other.Sex)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsLevelRangeOverlap(PaldeaEncounter other)
|
||||
{
|
||||
// If our level range overlaps with the other (with +/- 1 tolerance), return true.
|
||||
return MaxLevel + 1 >= other.MinLevel && MinLevel + 1 <= other.MaxLevel;
|
||||
}
|
||||
|
||||
public int CompareTo(PaldeaEncounter? other)
|
||||
{
|
||||
if (ReferenceEquals(this, other)) return 0;
|
||||
if (other is null) return 1;
|
||||
int speciesComparison = Species.CompareTo(other.Species);
|
||||
if (speciesComparison != 0) return speciesComparison;
|
||||
int formComparison = Form.CompareTo(other.Form);
|
||||
if (formComparison != 0) return formComparison;
|
||||
int sexComparison = Sex.CompareTo(other.Sex);
|
||||
if (sexComparison != 0) return sexComparison;
|
||||
int minLevelComparison = MinLevel.CompareTo(other.MinLevel);
|
||||
if (minLevelComparison != 0) return minLevelComparison;
|
||||
int maxLevelComparison = MaxLevel.CompareTo(other.MaxLevel);
|
||||
if (maxLevelComparison != 0) return maxLevelComparison;
|
||||
int timeComparison = Time.CompareTo(other.Time);
|
||||
if (timeComparison != 0) return timeComparison;
|
||||
return CrossFromLocation.CompareTo(other.CrossFromLocation);
|
||||
}
|
||||
|
||||
public ulong GetHash()
|
||||
{
|
||||
ulong result = Species;
|
||||
result = (result << 16) | CrossFromLocation;
|
||||
|
||||
result = (result << 8) | Form;
|
||||
result = (result << 8) | (byte)((byte)(Time << 4) | Sex);
|
||||
result = (result << 8) | MinLevel;
|
||||
result = (result << 8) | MaxLevel;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
52
pkNX.WinForms/Dumping/SV/PaldeaFieldModel.cs
Normal file
52
pkNX.WinForms/Dumping/SV/PaldeaFieldModel.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using pkNX.Containers;
|
||||
|
||||
namespace pkNX.Structures.FlatBuffers;
|
||||
|
||||
public class PaldeaFieldModel
|
||||
{
|
||||
private readonly FieldMainArea[] mainAreas;
|
||||
private readonly FieldSubArea[] subAreas;
|
||||
private readonly FieldInsideArea[] insideAreas;
|
||||
private readonly FieldDungeonArea[] dungeonAreas;
|
||||
private readonly FieldLocation[] fieldLocations;
|
||||
|
||||
public PaldeaFieldModel(IFileInternal ROM)
|
||||
{
|
||||
mainAreas = FlatBufferConverter.DeserializeFrom<FieldMainAreaArray>(ROM.GetPackedFile("world/data/field/area/field_main_area/field_main_area_array.bin")).Table;
|
||||
subAreas = FlatBufferConverter.DeserializeFrom<FieldSubAreaArray>(ROM.GetPackedFile("world/data/field/area/field_sub_area/field_sub_area_array.bin")).Table;
|
||||
insideAreas = FlatBufferConverter.DeserializeFrom<FieldInsideAreaArray>(ROM.GetPackedFile("world/data/field/area/field_inside_area/field_inside_area_array.bin")).Table;
|
||||
dungeonAreas = FlatBufferConverter.DeserializeFrom<FieldDungeonAreaArray>(ROM.GetPackedFile("world/data/field/area/field_dungeon_area/field_dungeon_area_array.bin")).Table;
|
||||
fieldLocations = FlatBufferConverter.DeserializeFrom<FieldLocationArray>(ROM.GetPackedFile("world/data/field/area/field_location/field_location_array.bin")).Table;
|
||||
}
|
||||
|
||||
public AreaInfo FindAreaInfo(string name)
|
||||
{
|
||||
foreach (var area in mainAreas)
|
||||
{
|
||||
if (area.Name == name)
|
||||
return area.AreaInfo;
|
||||
}
|
||||
foreach (var area in subAreas)
|
||||
{
|
||||
if (area.Name == name)
|
||||
return area.AreaInfo;
|
||||
}
|
||||
foreach (var area in insideAreas)
|
||||
{
|
||||
if (area.Name == name)
|
||||
return area.AreaInfo;
|
||||
}
|
||||
foreach (var area in dungeonAreas)
|
||||
{
|
||||
if (area.Name == name)
|
||||
return area.AreaInfo;
|
||||
}
|
||||
foreach (var area in fieldLocations)
|
||||
{
|
||||
if (area.Name == name)
|
||||
return area.AreaInfo;
|
||||
}
|
||||
throw new ArgumentException($"Unknown area {name}");
|
||||
}
|
||||
}
|
||||
104
pkNX.WinForms/Dumping/SV/PaldeaFixedSymbolModel.cs
Normal file
104
pkNX.WinForms/Dumping/SV/PaldeaFixedSymbolModel.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using pkNX.Containers;
|
||||
using pkNX.Structures.FlatBuffers;
|
||||
|
||||
namespace pkNX.WinForms;
|
||||
|
||||
public class PaldeaFixedSymbolModel
|
||||
{
|
||||
public readonly List<PaldeaFixedSymbolPoint> scarletPoints;
|
||||
public readonly List<PaldeaFixedSymbolPoint> violetPoints;
|
||||
|
||||
public PaldeaFixedSymbolModel(IFileInternal ROM)
|
||||
{
|
||||
scarletPoints = new List<PaldeaFixedSymbolPoint>();
|
||||
violetPoints = new List<PaldeaFixedSymbolPoint>();
|
||||
|
||||
var p0Data = ROM.GetPackedFile("world/scene/parts/field/streaming_event/world_fixed_placement_symbol_/world_fixed_placement_symbol_0.trscn");
|
||||
var p1Data = ROM.GetPackedFile("world/scene/parts/field/streaming_event/world_fixed_placement_symbol_/world_fixed_placement_symbol_1.trscn");
|
||||
|
||||
var p0 = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplateSV>(p0Data);
|
||||
var p1 = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplateSV>(p1Data);
|
||||
|
||||
scarletPoints.AddRange(GetObjectTemplateSymbolPoints(p0));
|
||||
violetPoints.AddRange(GetObjectTemplateSymbolPoints(p1));
|
||||
}
|
||||
|
||||
private IEnumerable<PaldeaFixedSymbolPoint> GetObjectTemplateSymbolPoints(TrinitySceneObjectTemplateSV template)
|
||||
{
|
||||
foreach (var obj in template.Objects)
|
||||
{
|
||||
switch (obj.Type)
|
||||
{
|
||||
case "trinity_ObjectTemplate":
|
||||
{
|
||||
var sObj = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplateDataSV>(obj.Data);
|
||||
if (sObj.Type != "trinity_ScenePoint")
|
||||
continue;
|
||||
var scenePoint = FlatBufferConverter.DeserializeFrom<TrinityScenePointSV>(sObj.Data);
|
||||
|
||||
foreach (var f in GetScenePointSymbolPoints(scenePoint, obj.SubObjects))
|
||||
yield return f;
|
||||
break;
|
||||
}
|
||||
case "trinity_ScenePoint":
|
||||
{
|
||||
var scenePoint = FlatBufferConverter.DeserializeFrom<TrinityScenePointSV>(obj.Data);
|
||||
|
||||
foreach (var f in GetScenePointSymbolPoints(scenePoint, obj.SubObjects))
|
||||
yield return f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<PaldeaFixedSymbolPoint> GetScenePointSymbolPoints(TrinityScenePointSV scenePoint, TrinitySceneObjectTemplateEntrySV[] subObjects)
|
||||
{
|
||||
// Handle SubObjects
|
||||
for (var i = 0; i < subObjects.Length; i++)
|
||||
{
|
||||
var sobj = subObjects[i];
|
||||
switch (sobj.Type)
|
||||
{
|
||||
case "trinity_PropertySheet":
|
||||
{
|
||||
var propSheet = FlatBufferConverter.DeserializeFrom<TrinityPropertySheetSV>(sobj.Data);
|
||||
if (propSheet.Name == "fixed_symbol_point")
|
||||
{
|
||||
var tableKey = GetTableKey(propSheet);
|
||||
if (!string.IsNullOrEmpty(tableKey))
|
||||
{
|
||||
yield return new PaldeaFixedSymbolPoint(tableKey, scenePoint.Position);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "trinity_ScenePoint":
|
||||
{
|
||||
var subScenePoint = FlatBufferConverter.DeserializeFrom<TrinityScenePointSV>(sobj.Data);
|
||||
foreach (var f in GetScenePointSymbolPoints(subScenePoint, sobj.SubObjects))
|
||||
yield return f;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException($"Unknown SubObject {sobj.Type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetTableKey(TrinityPropertySheetSV propSheet)
|
||||
{
|
||||
if (propSheet.Name != "fixed_symbol_point")
|
||||
throw new ArgumentException($"Invalid PropertySheet {propSheet.Name}");
|
||||
|
||||
if (propSheet.Properties[0].Fields[1].Name != "tableKey")
|
||||
throw new ArgumentException("Invalid PropertySheet field layout");
|
||||
|
||||
if (!propSheet.Properties[0].Fields[1].Data.TryGet(out TrinityPropertySheetFieldStringValueSV? sv))
|
||||
throw new ArgumentException("Could not get PropertySheet Table Key");
|
||||
|
||||
return sv.Value;
|
||||
}
|
||||
}
|
||||
20
pkNX.WinForms/Dumping/SV/PaldeaFixedSymbolPoint.cs
Normal file
20
pkNX.WinForms/Dumping/SV/PaldeaFixedSymbolPoint.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using pkNX.Structures.FlatBuffers;
|
||||
|
||||
namespace pkNX.WinForms;
|
||||
|
||||
public class PaldeaFixedSymbolPoint
|
||||
{
|
||||
public string TableKey;
|
||||
public PackedVec3f Position;
|
||||
|
||||
public PaldeaFixedSymbolPoint(string key, PackedVec3f pos)
|
||||
{
|
||||
TableKey = key;
|
||||
Position = new PackedVec3f
|
||||
{
|
||||
X = pos.X,
|
||||
Y = pos.Y,
|
||||
Z = pos.Z,
|
||||
};
|
||||
}
|
||||
}
|
||||
114
pkNX.WinForms/Dumping/SV/PaldeaSceneModel.cs
Normal file
114
pkNX.WinForms/Dumping/SV/PaldeaSceneModel.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using pkNX.Containers;
|
||||
|
||||
namespace pkNX.Structures.FlatBuffers;
|
||||
|
||||
public class PaldeaSceneModel
|
||||
{
|
||||
public readonly List<string> areaNames;
|
||||
public readonly Dictionary<string, AreaInfo> AreaInfos;
|
||||
public readonly Dictionary<string, HavokCollision.AABBTree> areaColTrees;
|
||||
public readonly Dictionary<string, BoxCollision9> areaColBoxes;
|
||||
public readonly Dictionary<string, bool> isAtlantis;
|
||||
|
||||
public PaldeaSceneModel(IFileInternal ROM, PaldeaFieldModel field)
|
||||
{
|
||||
// NOTE: Safe to only use _0 because _1 is identical.
|
||||
var area_management = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplateSV>(ROM.GetPackedFile("world/scene/parts/field/field_system/area_management_/area_management_0.trscn"));
|
||||
Debug.Assert(area_management.ObjectTemplateName == "area_management");
|
||||
|
||||
// NOTE: Safe to only use _0 because _1 is identical.
|
||||
var a_w23_field_area_col = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplateSV>(ROM.GetPackedFile("world/scene/parts/field/room/a_w23_field/a_w23_field_area_col_/a_w23_field_area_col_0.trscn"));
|
||||
Debug.Assert(a_w23_field_area_col.ObjectTemplateName == "a_w23_field_area_col");
|
||||
|
||||
areaNames = new List<string>();
|
||||
AreaInfos = new Dictionary<string, AreaInfo>();
|
||||
areaColTrees = new Dictionary<string, HavokCollision.AABBTree>();
|
||||
areaColBoxes = new Dictionary<string, BoxCollision9>();
|
||||
isAtlantis = new Dictionary<string, bool>();
|
||||
|
||||
void AddSceneObject(string name, TrinitySceneObjectSV sceneObject, TrinityCollisionComponent1SV collisionComponent)
|
||||
{
|
||||
areaNames.Add(name);
|
||||
AreaInfos[name] = field.FindAreaInfo(name);
|
||||
|
||||
Debug.Assert(collisionComponent.CollisionShape.Discriminator is 2 or 4);
|
||||
|
||||
if (collisionComponent.CollisionShape.TryGet(out TrinityCollisionShapeBoxSV? box))
|
||||
{
|
||||
// Box collision, obj.ObjectPosition.Field_02 is pos, box.Field_01 is size of box
|
||||
areaColBoxes[name] = new BoxCollision9
|
||||
{
|
||||
Position = sceneObject.ObjectPosition.Field_02,
|
||||
Size = box.Field_01,
|
||||
};
|
||||
}
|
||||
else if (collisionComponent.CollisionShape.TryGet(out TrinityCollisionShapeHavokSV? havok))
|
||||
{
|
||||
var havokData = ROM.GetPackedFile(havok.TrcolFilePath);
|
||||
areaColTrees[name] = HavokCollision.ParseAABBTree(havokData);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var obj in area_management.Objects.Concat(a_w23_field_area_col.Objects))
|
||||
{
|
||||
var isAtlantisObj = a_w23_field_area_col.Objects.Contains(obj);
|
||||
if (!(obj.SubObjects.Length > 0 && obj.SubObjects[0].Type == "trinity_CollisionComponent"))
|
||||
continue;
|
||||
var collisionComponent = FlatBufferConverter.DeserializeFrom<TrinityCollisionComponentSV>(obj.SubObjects[0].Data).Component.Item1;
|
||||
|
||||
switch (obj.Type)
|
||||
{
|
||||
case "trinity_ObjectTemplate":
|
||||
{
|
||||
var sObj = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectTemplateDataSV>(obj.Data);
|
||||
if (sObj.Type != "trinity_SceneObject")
|
||||
continue;
|
||||
var sceneObject = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectSV>(sObj.Data);
|
||||
Debug.Assert(sceneObject.ObjectName == sObj.ObjectTemplateExtra);
|
||||
|
||||
AddSceneObject(sObj.ObjectTemplateName, sceneObject, collisionComponent);
|
||||
isAtlantis[sObj.ObjectTemplateName] = isAtlantisObj;
|
||||
break;
|
||||
}
|
||||
case "trinity_SceneObject":
|
||||
{
|
||||
var sceneObject = FlatBufferConverter.DeserializeFrom<TrinitySceneObjectSV>(obj.Data);
|
||||
AddSceneObject(sceneObject.ObjectName, sceneObject, collisionComponent);
|
||||
isAtlantis[sceneObject.ObjectName] = isAtlantisObj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPointContained(string areaName, float x, float y, float z)
|
||||
{
|
||||
if (areaColTrees.TryGetValue(areaName, out var tree))
|
||||
return tree.ContainsPoint(x, y, z);
|
||||
if (areaColBoxes.TryGetValue(areaName, out var box))
|
||||
return box.ContainsPoint(x, y, z);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetContainsCheck(string areaName, [NotNullWhen(true)] out IContainsV3f? result)
|
||||
{
|
||||
if (areaColTrees.TryGetValue(areaName, out var tree))
|
||||
{
|
||||
result = tree;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (areaColBoxes.TryGetValue(areaName, out var box))
|
||||
{
|
||||
result = box;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
2
pkNX.WinForms/Subforms/MapViewer9.Designer.cs
generated
2
pkNX.WinForms/Subforms/MapViewer9.Designer.cs
generated
@@ -28,7 +28,6 @@ protected override void Dispose(bool disposing)
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MapViewer9));
|
||||
this.pictureBox1 = new System.Windows.Forms.PictureBox();
|
||||
this.CB_Map = new System.Windows.Forms.ComboBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
|
||||
@@ -62,7 +61,6 @@ private void InitializeComponent()
|
||||
this.ClientSize = new System.Drawing.Size(827, 614);
|
||||
this.Controls.Add(this.CB_Map);
|
||||
this.Controls.Add(this.pictureBox1);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||
this.Name = "MapViewer9";
|
||||
this.Text = "MapViewer9";
|
||||
|
||||
Reference in New Issue
Block a user