Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 162 additions & 18 deletions CodeWalker.Core/World/Space.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public class Space

public SpaceNodeGrid NodeGrid;
private Dictionary<uint, YndFile> AllYnds = new Dictionary<uint, YndFile>();
private Dictionary<uint, RpfFileEntry> DefaultYndEntries = new Dictionary<uint, RpfFileEntry>();
private Dictionary<int, HashSet<YndFile>> YndDependentsByArea = new Dictionary<int, HashSet<YndFile>>();
private Dictionary<YndFile, HashSet<int>> YndDependencyAreas = new Dictionary<YndFile, HashSet<int>>();

public SpaceNavGrid NavGrid;

Expand Down Expand Up @@ -348,6 +351,9 @@ private void InitNodeGrid()

NodeGrid = new SpaceNodeGrid();
AllYnds.Clear();
DefaultYndEntries.Clear();
YndDependentsByArea.Clear();
YndDependencyAreas.Clear();

var rpfman = GameFileCache.RpfMan;
Dictionary<uint, RpfFileEntry> yndentries = new Dictionary<uint, RpfFileEntry>();
Expand Down Expand Up @@ -375,6 +381,11 @@ private void InitNodeGrid()
}
}

foreach (var entry in yndentries)
{
DefaultYndEntries[entry.Key] = entry.Value;
}


Vector3 corner = new Vector3(-8192, -8192, -2048);
Vector3 cellsize = new Vector3(512, 512, 4096);
Expand All @@ -390,11 +401,7 @@ private void InitNodeGrid()
if (yndentries.TryGetValue(fnhash, out fentry))
{
cell.Ynd = rpfman.GetFile<YndFile>(fentry);
cell.Ynd.BBMin = corner + (cellsize * new Vector3(x, y, 0));
cell.Ynd.BBMax = cell.Ynd.BBMin + cellsize;
cell.Ynd.CellX = x;
cell.Ynd.CellY = y;
cell.Ynd.Loaded = true;
ConfigureYndForCell(cell.Ynd, x, y);

AllYnds[fnhash] = cell.Ynd;

Expand Down Expand Up @@ -494,11 +501,106 @@ private void InitNodeGrid()
//string str = sb.ToString();
}

private void ConfigureYndForCell(YndFile ynd, int x, int y)
{
if (ynd == null) return;

Vector3 corner = new Vector3(-8192, -8192, -2048);
Vector3 cellsize = new Vector3(512, 512, 4096);
ynd.BBMin = corner + (cellsize * new Vector3(x, y, 0));
ynd.BBMax = ynd.BBMin + cellsize;
ynd.CellX = x;
ynd.CellY = y;
ynd.Loaded = true;
}

public void PatchYndFile(YndFile ynd)
{
//ideally we should be able to revert to the vanilla ynd's after closing the project window,
//but codewalker can always just be restarted, so who cares really
NodeGrid.UpdateYnd(ynd);
UpdateAllYndReference(ynd);
}

private void UpdateAllYndReference(YndFile ynd, int? previousAreaId = null)
{
if (ynd == null) return;

if (previousAreaId.HasValue)
{
uint previousHash = JenkHash.GenHash("nodes" + previousAreaId.Value + ".ynd");
if (AllYnds.TryGetValue(previousHash, out var previousYnd) && (previousYnd == ynd))
{
AllYnds.Remove(previousHash);
}
}

uint currentHash = JenkHash.GenHash("nodes" + ynd.AreaID + ".ynd");
if (AllYnds.TryGetValue(currentHash, out var existingYnd) && (existingYnd != ynd))
{
UnregisterYndDependencies(existingYnd);
}
AllYnds[currentHash] = ynd;
}

private void UnregisterYndDependencies(YndFile ynd)
{
if ((ynd == null) || !YndDependencyAreas.TryGetValue(ynd, out var areas))
{
return;
}

foreach (var area in areas)
{
if (YndDependentsByArea.TryGetValue(area, out var dependents))
{
dependents.Remove(ynd);
if (dependents.Count == 0)
{
YndDependentsByArea.Remove(area);
}
}
}

YndDependencyAreas.Remove(ynd);
}

private void RegisterYndDependencies(YndFile ynd)
{
UnregisterYndDependencies(ynd);

if (ynd?.Links == null)
{
return;
}

var dependencyAreas = new HashSet<int>();
foreach (var link in ynd.Links)
{
var node2 = link?.Node2;
if (node2 == null)
{
continue;
}

dependencyAreas.Add(node2.AreaID);
}

if (dependencyAreas.Count == 0)
{
return;
}

YndDependencyAreas[ynd] = dependencyAreas;
foreach (var area in dependencyAreas)
{
if (!YndDependentsByArea.TryGetValue(area, out var dependents))
{
dependents = new HashSet<YndFile>();
YndDependentsByArea[area] = dependents;
}
dependents.Add(ynd);
}
}

private void AddRpfYnds(RpfFile rpffile, Dictionary<uint, RpfFileEntry> yndentries)
Expand All @@ -524,7 +626,11 @@ public void BuildYndLinks(YndFile ynd, List<YndLink> tlinks = null, List<YndLink
var ynodes = ynd.Nodes;
var nodes = ynd.NodeDictionary?.Nodes;
var links = ynd.NodeDictionary?.Links;
if ((ynodes == null) || (nodes == null) || (links == null)) return;
if ((ynodes == null) || (nodes == null) || (links == null))
{
UnregisterYndDependencies(ynd);
return;
}

int nodecount = ynodes.Length;

Expand Down Expand Up @@ -568,6 +674,7 @@ public void BuildYndLinks(YndFile ynd, List<YndLink> tlinks = null, List<YndLink
node.Links = nlinks.ToArray();
}
ynd.Links = tlinks.ToArray();
RegisterYndDependencies(ynd);

}
public void BuildYndVerts(YndFile ynd, YndNode[] selectedNodes, List<EditorVertex> tverts = null)
Expand Down Expand Up @@ -697,24 +804,60 @@ public void BuildYndData(YndFile ynd, List<EditorVertex> tverts = null, List<Ynd

}

public HashSet<YndFile> GetYndFilesThatDependOnArea(int areaId)
{
if (YndDependentsByArea.TryGetValue(areaId, out var dependents))
{
return new HashSet<YndFile>(dependents);
}

return new HashSet<YndFile>();
}

public HashSet<YndFile> GetYndFilesThatDependOnYndFile(YndFile file)
{
HashSet<YndFile> result = new HashSet<YndFile>();
int targetAreaID = file.AreaID; // Cache to avoid repeated property access
if (file == null)
{
return new HashSet<YndFile>();
}

foreach (var ynd in AllYnds.Values)
return GetYndFilesThatDependOnArea(file.AreaID);
}

public YndFile RestoreYndArea(int areaId)
{
var cell = NodeGrid?.GetCell(areaId);
if (cell == null)
{
foreach (var link in ynd.Links)
{
if (link.Node2.AreaID == targetAreaID)
{
result.Add(ynd);
break; // No need to check more links for this YndFile
}
}
return null;
}

uint areaHash = JenkHash.GenHash("nodes" + areaId + ".ynd");
if (AllYnds.TryGetValue(areaHash, out var existingYnd))
{
UnregisterYndDependencies(existingYnd);
}

if (!DefaultYndEntries.TryGetValue(areaHash, out var defaultEntry))
{
cell.Ynd = null;
AllYnds.Remove(areaHash);
return null;
}

var defaultYnd = GameFileCache?.RpfMan?.GetFile<YndFile>(defaultEntry);
if (defaultYnd == null)
{
cell.Ynd = null;
AllYnds.Remove(areaHash);
return null;
}

return result;
ConfigureYndForCell(defaultYnd, cell.X, cell.Y);
NodeGrid.UpdateYnd(defaultYnd);
AllYnds[areaHash] = defaultYnd;
RegisterYndDependencies(defaultYnd);
return defaultYnd;
}

public void MoveYndArea(YndFile ynd, int desiredX, int desiredY)
Expand Down Expand Up @@ -799,6 +942,7 @@ public void MoveYndArea(YndFile ynd, int desiredX, int desiredY)
ynd.BuildStructs();
}
NodeGrid.UpdateYnd(ynd);
UpdateAllYndReference(ynd, areaIdorig);
}

public void RecalculateAllYndIndices()
Expand Down
50 changes: 50 additions & 0 deletions CodeWalker/Project/Panels/ProjectExplorerPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2216,6 +2216,31 @@ public void UpdateYbnTreeNode(YbnFile ybn)
tn.Text = ybn.RpfFileEntry?.Name ?? ybn.Name;
}
}
public void AddYndTreeNode(YndFile ynd)
{
if (ynd == null) return;
if (FindYndTreeNode(ynd) != null) return;
if (ProjectTreeView.Nodes.Count <= 0)
{
LoadProjectTree(CurrentProjectFile);
return;
}

var projnode = ProjectTreeView.Nodes[0];
var yndsnode = GetChildTreeNode(projnode, "Ynd");
if (yndsnode == null)
{
yndsnode = projnode.Nodes.Add("Ynd Files");
yndsnode.Name = "Ynd";
}

var changestr = ynd.HasChanged ? "*" : "";
var name = ynd.RpfFileEntry?.Name ?? ynd.Name;
var yndnode = yndsnode.Nodes.Add(changestr + name);
yndnode.Tag = ynd;
LoadYndTreeNodes(ynd, yndnode);
yndsnode.Expand();
}
public void UpdateYndTreeNode(YndFile ynd)
{
var tn = FindYndTreeNode(ynd);
Expand All @@ -2224,6 +2249,31 @@ public void UpdateYndTreeNode(YndFile ynd)
tn.Text = ynd.RpfFileEntry?.Name ?? ynd.Name;
}
}
public void RefreshYndTreeNode(YndFile ynd)
{
var tn = FindYndTreeNode(ynd);
if (tn == null)
{
return;
}

var wasExpanded = tn.IsExpanded;
var nodesExpanded = GetChildTreeNode(tn, "Nodes")?.IsExpanded ?? false;
var changestr = ynd.HasChanged ? "*" : "";
var name = ynd.RpfFileEntry?.Name ?? ynd.Name;

tn.Text = changestr + name;
LoadYndTreeNodes(ynd, tn);

if (wasExpanded)
{
tn.Expand();
}
if (nodesExpanded)
{
GetChildTreeNode(tn, "Nodes")?.Expand();
}
}
public void UpdateYnvTreeNode(YnvFile ynv)
{
var tn = FindYnvTreeNode(ynv);
Expand Down
47 changes: 45 additions & 2 deletions CodeWalker/Project/ProjectForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,12 @@ public void CloseProject()
{
if (CurrentProjectFile == null) return;

var yndAreasToRestore = CurrentProjectFile.YndFiles
.Where(ynd => ynd != null)
.Select(ynd => ynd.AreaID)
.Distinct()
.ToArray();

foreach (var ymap in CurrentProjectFile.YmapFiles)
{
if ((ymap != null) && (ymap.HasChanged))
Expand Down Expand Up @@ -1522,6 +1528,32 @@ public void CloseProject()

if (WorldForm != null)
{
if (yndAreasToRestore.Length > 0)
{
lock (WorldForm.RenderSyncRoot)
{
var yndsToRefresh = new HashSet<YndFile>();
foreach (var areaId in yndAreasToRestore)
{
foreach (var dependent in WorldForm.Space.GetYndFilesThatDependOnArea(areaId))
{
yndsToRefresh.Add(dependent);
}

var restoredYnd = WorldForm.Space.RestoreYndArea(areaId);
if (restoredYnd != null)
{
yndsToRefresh.Add(restoredYnd);
}
}

foreach (var ynd in yndsToRefresh)
{
WorldForm.UpdatePathYndGraphics(ynd, true);
}
}
}

WorldForm.SelectItem(null);//make sure current selected item isn't still selected...
}

Expand Down Expand Up @@ -4718,7 +4750,14 @@ public void AddYndToProject(YndFile ynd)
{
ynd.HasChanged = true;
CurrentProjectFile.HasChanged = true;
LoadProjectTree();
if ((ProjectExplorer != null) && (ProjectExplorer.CurrentProjectFile == CurrentProjectFile))
{
ProjectExplorer.AddYndTreeNode(ynd);
}
else
{
LoadProjectTree();
}
}
CurrentYndFile = ynd;
RefreshUI();
Expand Down Expand Up @@ -4807,7 +4846,7 @@ public YndNode NewPathNode(YndNode copy = null, bool copyPosition = false, bool

if (selectNew)
{
LoadProjectTree();
ProjectExplorer?.RefreshYndTreeNode(CurrentYndFile);
ProjectExplorer?.TrySelectPathNodeTreeNode(n);
CurrentPathNode = n;
//ShowEditYndPanel(false);;
Expand Down Expand Up @@ -8782,6 +8821,10 @@ private void LoadYndFromFile(YndFile ynd, string filename)
ynd.Load(data);
WorldForm.Space.PatchYndFile(ynd);

// Rebuild the imported file immediately so live dependency scans and link rendering
// operate on resolved Node2 references instead of the raw node dictionary only.
WorldForm?.UpdatePathYndGraphics(ynd, true);

if (WorldForm != null)
{
HashSet<YndFile> updatedFiles = new HashSet<YndFile>();
Expand Down