Skip to content

Commit e0901bd

Browse files
authored
Merge pull request WolvenKit#2676 from WolvenKit/quest-node-auto-select-path
Quest editor: auto select path node
2 parents e700db0 + 6368fc9 commit e0901bd

File tree

4 files changed

+248
-84
lines changed

4 files changed

+248
-84
lines changed

WolvenKit.App/Services/NodePropertiesSelectionService.cs

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
using System.Collections.ObjectModel;
22
using System.ComponentModel;
3+
using System.Linq;
34
using System.Runtime.CompilerServices;
45
using WolvenKit.App.ViewModels.Shell;
6+
using WolvenKit.RED4.Types;
7+
using Splat;
8+
using WolvenKit.Core.Interfaces;
59

610
namespace WolvenKit.App.Services
711
{
@@ -48,11 +52,134 @@ public void ClearSelection()
4852
SelectedProperties?.Clear();
4953
}
5054

55+
/// <summary>
56+
/// Auto-select important properties for specific node types
57+
/// </summary>
58+
public void AutoSelectImportantProperty(ChunkViewModel rootChunk)
59+
{
60+
if (rootChunk?.ResolvedData == null)
61+
return;
62+
63+
try
64+
{
65+
if (rootChunk.ResolvedData is questJournalNodeDefinition)
66+
{
67+
var pathProperty = FindPropertyRecursive(rootChunk, "path");
68+
if (pathProperty != null)
69+
{
70+
SelectedProperty = pathProperty;
71+
return;
72+
}
73+
}
74+
75+
if (rootChunk.ResolvedData is questFactsDBManagerNodeDefinition)
76+
{
77+
var typeProperty = FindPropertyRecursive(rootChunk, "type");
78+
if (typeProperty != null)
79+
{
80+
SelectedProperty = typeProperty;
81+
return;
82+
}
83+
}
84+
85+
if (rootChunk.ResolvedData is questConditionNodeDefinition or questPauseConditionNodeDefinition)
86+
{
87+
var typeProperty = FindPropertyRecursive(rootChunk, "type");
88+
if (typeProperty != null)
89+
{
90+
SelectedProperty = typeProperty;
91+
return;
92+
}
93+
}
94+
95+
if (rootChunk.ResolvedData is questUIManagerNodeDefinition or
96+
questEnvironmentManagerNodeDefinition or
97+
questTimeManagerNodeDefinition)
98+
{
99+
var typeProperty = FindPropertyRecursive(rootChunk, "type");
100+
if (typeProperty != null)
101+
{
102+
SelectedProperty = typeProperty;
103+
return;
104+
}
105+
}
106+
107+
if (rootChunk.ResolvedData is scnQuestNode)
108+
{
109+
try
110+
{
111+
rootChunk.CalculateProperties();
112+
}
113+
catch
114+
{
115+
// Ignore calculation errors
116+
}
117+
118+
var questNodeProperty = FindPropertyRecursive(rootChunk, "questNode") ??
119+
FindPropertyRecursive(rootChunk, "QuestNode");
120+
121+
if (questNodeProperty != null)
122+
{
123+
AutoSelectImportantProperty(questNodeProperty);
124+
return;
125+
}
126+
}
127+
128+
129+
}
130+
catch (System.Exception ex)
131+
{
132+
Locator.Current.GetService<ILoggerService>()?.Error($"Failed to auto-select property: {ex.Message}");
133+
}
134+
}
135+
136+
/// <summary>
137+
/// Recursively find a property by name in the chunk hierarchy
138+
/// </summary>
139+
private ChunkViewModel? FindPropertyRecursive(ChunkViewModel chunk, string propertyName)
140+
{
141+
if (chunk == null)
142+
return null;
143+
144+
// Check direct children first
145+
var directMatch = chunk.TVProperties?.FirstOrDefault(p =>
146+
p.Name.Equals(propertyName, System.StringComparison.OrdinalIgnoreCase));
147+
148+
if (directMatch != null)
149+
return directMatch;
150+
151+
// If not found, search in children recursively
152+
if (chunk.TVProperties != null)
153+
{
154+
foreach (var child in chunk.TVProperties)
155+
{
156+
// Try to calculate properties if not already done
157+
if (child.TVProperties == null || !child.TVProperties.Any())
158+
{
159+
try
160+
{
161+
child.CalculateProperties();
162+
}
163+
catch
164+
{
165+
// Ignore errors, continue searching
166+
}
167+
}
168+
169+
var recursiveMatch = FindPropertyRecursive(child, propertyName);
170+
if (recursiveMatch != null)
171+
return recursiveMatch;
172+
}
173+
}
174+
175+
return null;
176+
}
177+
51178
public event PropertyChangedEventHandler? PropertyChanged;
52179

53180
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
54181
{
55182
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
56183
}
57184
}
58-
}
185+
}

WolvenKit.App/ViewModels/Shell/ChunkViewModel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2058,7 +2058,8 @@ private void GenerateMissingMaterialDefinitions(bool isLocal)
20582058

20592059
CalculateProperties();
20602060

2061-
if (TVProperties.FirstOrDefault(prop => prop.Name == propertyNames[0]) is not ChunkViewModel cvm)
2061+
if (TVProperties.FirstOrDefault(prop => propertyNames[0].Equals(prop.Name, StringComparison.OrdinalIgnoreCase))
2062+
is not ChunkViewModel cvm)
20622063
{
20632064
return null;
20642065
}

WolvenKit.App/ViewModels/Tools/ChunkViewModelExtended/ChunkViewModel.ExpansionStates.cs

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Threading;
45
using System.Windows;
@@ -30,9 +31,9 @@ public partial class ChunkViewModel
3031
/// Helper method to expand and select a child node.
3132
/// </summary>
3233
/// <param name="cvm">The node to expand</param>
33-
/// <param name="selectChild">If parameter is set to false or cvm has no children, will select cvm. Otherwise, will select first child.</param>
34+
/// <param name="selectFirstChild">If parameter is set to false or cvm has no children, will select cvm. Otherwise, will select first child.</param>
3435
/// <param name="expandOnlyChild">If cvm has only one child, it will be expanded as well.</param>
35-
private void ExpandAndSelect(ChunkViewModel? cvm, bool selectChild = false, bool expandOnlyChild = false)
36+
private void ExpandAndSelect(ChunkViewModel? cvm, bool selectFirstChild = false, bool expandOnlyChild = false)
3637
{
3738
if (cvm is null)
3839
{
@@ -51,16 +52,45 @@ private void ExpandAndSelect(ChunkViewModel? cvm, bool selectChild = false, bool
5152
return;
5253
}
5354

54-
if (!selectChild || cvm.TVProperties.Count == 0)
55+
if (!selectFirstChild || cvm.TVProperties.Count == 0)
5556
{
5657
Tab.SetSelection(cvm);
5758
return;
5859
}
5960

6061
Tab.SetSelection(cvm.TVProperties[0]);
6162
}
63+
64+
65+
/// <summary>
66+
/// Map of data types with selection paths
67+
/// </summary>
68+
private static readonly Dictionary<Type, List<string>> s_typesAndChildren = new()
69+
{
70+
{ typeof(CMaterialInstance), ["values"] }, // mi file
71+
{ typeof(CMaterialTemplate), ["parameters", "2"] }, // .mt / .remt
72+
{ typeof(Multilayer_Setup), ["layers"] }, // .mt / .remt
73+
{ typeof(appearanceAppearanceResource), ["appearances"] }, // .app
74+
{ typeof(C2dArray), ["compiledData"] }, // .csv
75+
{ typeof(JsonResource), ["root"] }, // .json
76+
{ typeof(gameuiSwitcherInfo), ["options"] }, // .inkcharcustomization
77+
{ typeof(gameuiOptionsGroup), ["options"] }, // .inkcharcustomization
78+
{ typeof(questQuestPhaseResource), ["graph"] }, // .questphase
79+
};
80+
81+
private void ExpandNodeAndParents()
82+
{
83+
if (Parent?.IsExpanded == false)
84+
{
85+
Parent.ExpandNodeAndParents();
86+
}
87+
88+
IsExpanded = true;
89+
}
90+
6291
/// <summary>
6392
/// Called from ChunkViewmodelFactory after initializing the chunk.
93+
/// For quest node expansion, see RedTypeToChunkViewModelCollectionConverter.AutoExpandProperties
6494
/// </summary>
6595
public ChunkViewModel SetInitialExpansionState()
6696
{
@@ -69,20 +99,19 @@ public ChunkViewModel SetInitialExpansionState()
6999
return this;
70100
}
71101

102+
if (s_typesAndChildren.TryGetValue(PropertyType, out var paths))
103+
{
104+
CalculatePropertiesRecursive();
105+
var node = GetPropertyChild([..paths]);
106+
if (node is not null)
107+
{
108+
ExpandAndSelect(node, true);
109+
return this;
110+
}
111+
}
112+
72113
switch (ResolvedData)
73114
{
74-
// .mi file
75-
case CMaterialInstance when GetPropertyChild("values") is ChunkViewModel child:
76-
ExpandAndSelect(child, true);
77-
break;
78-
// .mt file
79-
case CMaterialTemplate when GetPropertyChild("parameters", "2") is ChunkViewModel child:
80-
ExpandAndSelect(child, true);
81-
break;
82-
// .mlsetup file
83-
case Multilayer_Setup when GetPropertyChild("layers") is ChunkViewModel child:
84-
ExpandAndSelect(child, true);
85-
break;
86115
// .inkatlas
87116
case inkTextureAtlas when GetPropertyChild("slots") is ChunkViewModel child:
88117
ExpandAndSelect(child, true);
@@ -95,10 +124,6 @@ public ChunkViewModel SetInitialExpansionState()
95124
}
96125
}
97126
break;
98-
// .app file
99-
case appearanceAppearanceResource when GetPropertyChild("appearances") is ChunkViewModel child:
100-
ExpandAndSelect(child, false, true);
101-
break;
102127
// .ent file
103128
case entEntityTemplate template:
104129

@@ -107,6 +132,8 @@ public ChunkViewModel SetInitialExpansionState()
107132
var nodeToExpand = template.Appearances.Count == 0 ? components : appearances;
108133
ExpandAndSelect(nodeToExpand, true, true);
109134
break;
135+
136+
#region mesh
110137
// .mesh file
111138
case CMesh:
112139
if (GetPropertyChild("appearances") is { TVProperties.Count: > 0 } meshAppearances)
@@ -131,14 +158,9 @@ public ChunkViewModel SetInitialExpansionState()
131158
}
132159

133160
break;
134-
// .csv
135-
case C2dArray when GetPropertyChild("compiledData") is ChunkViewModel child:
136-
ExpandAndSelect(child, true);
137-
break;
138-
// .json
139-
case JsonResource when GetPropertyChild("root") is ChunkViewModel child:
140-
ExpandAndSelect(child, true);
141-
break;
161+
162+
#endregion
163+
142164
// streamingsector
143165
case worldStreamingSector:
144166
// will run into stack overflow due to race conditions if we do this straight away. Let's wait a bit!

0 commit comments

Comments
 (0)