Skip to content

Commit 3428817

Browse files
committed
feat: Pfb game object support copy/paste and fix some bugs
Fix pfb reopen bug Fix recent files menu display Check resource path when rebulid info
1 parent 5f5483f commit 3428817

File tree

10 files changed

+222
-12
lines changed

10 files changed

+222
-12
lines changed

RszTool.App/MainWindow.xaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@
8585
<MenuItem Header="{res:Text OpenRecentFiles}">
8686
<MenuItem.Resources>
8787
<CollectionViewSource x:Key="RecentFiles" Source="{Binding SaveData.RecentFiles}"/>
88+
<DataTemplate x:Key="StringOnlyTemplate">
89+
<TextBlock Text="{Binding}" />
90+
</DataTemplate>
8891
</MenuItem.Resources>
8992
<MenuItem.ItemsSource>
9093
<CompositeCollection>
@@ -97,6 +100,7 @@
97100
<Style TargetType="{x:Type MenuItem}">
98101
<Setter Property="Command" Value="{Binding DataContext.OpenRecentFile, RelativeSource={RelativeSource AncestorType=Window}}" />
99102
<Setter Property="CommandParameter" Value="{Binding}" />
103+
<Setter Property="HeaderTemplate" Value="{StaticResource StringOnlyTemplate}" />
100104
</Style>
101105
</MenuItem.ItemContainerStyle>
102106
</MenuItem>

RszTool.App/RszTool.App.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
<UseWPF>true</UseWPF>
1010
<NoWarn>$(NoWarn);CS0067</NoWarn>
1111
<ApplicationManifest>app.manifest</ApplicationManifest>
12-
<AssemblyVersion>0.2.0.0</AssemblyVersion>
13-
<FileVersion>0.2.0.0</FileVersion>
12+
<AssemblyVersion>0.2.1.0</AssemblyVersion>
13+
<FileVersion>0.2.1.0</FileVersion>
1414
</PropertyGroup>
1515

1616
<ItemGroup>

RszTool.App/ViewModels/BaseRszFileViewModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public bool SaveAs(string path)
9595
{
9696
Changed = false;
9797
HeaderChanged?.Invoke();
98+
OnPropertyChanged(nameof(FilePath));
9899
}
99100
return result;
100101
}

RszTool.App/ViewModels/PfbFileViewModel.cs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ public class PfbFileViewModel(PfbFile file) : BaseRszFileViewModel
1515
public GameObjectSearchViewModel GameObjectSearchViewModel { get; } = new() { IncludeChildren = true };
1616
public ObservableCollection<PfbFile.GameObjectData>? SearchGameObjectList { get; set; }
1717

18+
19+
public static PfbFile.GameObjectData? CopiedGameObject { get; private set; }
20+
21+
public RelayCommand CopyGameObject => new(OnCopyGameObject);
22+
public RelayCommand RemoveGameObject => new(OnRemoveGameObject);
23+
public RelayCommand DuplicateGameObject => new(OnDuplicateGameObject);
24+
public RelayCommand PasteGameObject => new(OnPasteGameObject);
25+
public RelayCommand PasteGameobjectAsChild => new(OnPasteGameobjectAsChild);
1826
public RelayCommand SearchGameObjects => new(OnSearchGameObjects);
1927
public RelayCommand AddComponent => new(OnAddComponent);
2028
public RelayCommand PasteInstanceAsComponent => new(OnPasteInstanceAsComponent);
@@ -29,7 +37,64 @@ public override IEnumerable<object> TreeViewItems
2937
{
3038
get
3139
{
32-
yield return new TreeItemViewModel("GameObjects", GameObjects);
40+
yield return new GameObjectsHeader("GameObjects", GameObjects);
41+
}
42+
}
43+
44+
/// <summary>
45+
/// 复制游戏对象
46+
/// </summary>
47+
/// <param name="arg"></param>
48+
private static void OnCopyGameObject(object arg)
49+
{
50+
CopiedGameObject = (PfbFile.GameObjectData)arg;
51+
}
52+
53+
/// <summary>
54+
/// 删除游戏对象
55+
/// </summary>
56+
/// <param name="arg"></param>
57+
private void OnRemoveGameObject(object arg)
58+
{
59+
PfbFile.RemoveGameObject((PfbFile.GameObjectData)arg);
60+
Changed = true;
61+
}
62+
63+
/// <summary>
64+
/// 重复游戏对象
65+
/// </summary>
66+
/// <param name="arg"></param>
67+
private void OnDuplicateGameObject(object arg)
68+
{
69+
PfbFile.DuplicateGameObject((PfbFile.GameObjectData)arg);
70+
Changed = true;
71+
}
72+
73+
/// <summary>
74+
/// 粘贴游戏对象
75+
/// </summary>
76+
/// <param name="arg"></param>
77+
private void OnPasteGameObject(object arg)
78+
{
79+
if (CopiedGameObject != null)
80+
{
81+
PfbFile.ImportGameObject(CopiedGameObject);
82+
OnPropertyChanged(nameof(GameObjects));
83+
Changed = true;
84+
}
85+
}
86+
87+
/// <summary>
88+
/// 粘贴游戏对象到父对象
89+
/// </summary>
90+
/// <param name="arg"></param>
91+
private void OnPasteGameobjectAsChild(object arg)
92+
{
93+
if (CopiedGameObject != null)
94+
{
95+
var parent = (PfbFile.GameObjectData)arg;
96+
PfbFile.ImportGameObject(CopiedGameObject, parent: parent);
97+
Changed = true;
3398
}
3499
}
35100

RszTool.App/Views/RszPfbFileView.xaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,26 @@
2222
<converters:PfbGameObjectDataSubItemsConverter x:Key="PfbGameObjectDataSubItemsConverter"/>
2323
<common:BindingProxy x:Key="ParentData" Data="{Binding}" />
2424

25+
<HierarchicalDataTemplate DataType="{x:Type viewmodels:GameObjectsHeader}" ItemsSource="{Binding Items}">
26+
<TextBlock Text="{Binding Name}">
27+
<TextBlock.ContextMenu>
28+
<ContextMenu>
29+
<MenuItem Header="{res:Text PasteGameobject}" Command="{common:ParentBinding PasteGameObject}" />
30+
</ContextMenu>
31+
</TextBlock.ContextMenu>
32+
</TextBlock>
33+
</HierarchicalDataTemplate>
34+
2535
<HierarchicalDataTemplate DataType="{x:Type rsztool:PfbFile+GameObjectData}"
2636
ItemsSource="{Binding ., Converter={StaticResource PfbGameObjectDataSubItemsConverter}}"
2737
ItemContainerStyle="{StaticResource TreeViewItemHideIfEmpty}">
2838
<TextBlock Text="{Binding Name}">
2939
<TextBlock.ContextMenu>
3040
<ContextMenu>
41+
<MenuItem Header="{res:Text Copy}" Command="{common:ParentBinding CopyGameObject}" CommandParameter="{Binding}" />
42+
<MenuItem Header="{res:Text Remove}" Command="{common:ParentBinding RemoveGameObject}" CommandParameter="{Binding}" />
43+
<MenuItem Header="{res:Text Duplicate}" Command="{common:ParentBinding DuplicateGameObject}" CommandParameter="{Binding}" />
44+
<MenuItem Header="{res:Text PasteGameobjectAsChild}" Command="{common:ParentBinding PasteGameobjectAsChild}" CommandParameter="{Binding}" />
3145
<MenuItem Header="{res:Text AddComponent}" Command="{common:ParentBinding AddComponent}" CommandParameter="{Binding}" />
3246
<MenuItem Header="{res:Text PasteInstanceAsComponent}" Command="{common:ParentBinding PasteInstanceAsComponent}" CommandParameter="{Binding}" />
3347
</ContextMenu>

RszTool.App/Views/RszScnFileView.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<TextBlock Text="{Binding Name}">
2828
<TextBlock.ContextMenu>
2929
<ContextMenu>
30-
<MenuItem Header="{res:Text PasteGameobject}" Command="{Binding Data.PasteGameObject, Source={StaticResource ParentData}}" />
30+
<MenuItem Header="{res:Text PasteGameobject}" Command="{common:ParentBinding PasteGameObject}" />
3131
</ContextMenu>
3232
</TextBlock.ContextMenu>
3333
</TextBlock>

RszTool/RszFile/PfbFile.cs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,23 @@ public GameObjectData? Parent
116116

117117
public int? ObjectId => Info?.Data.objectId;
118118

119+
public object Clone()
120+
{
121+
GameObjectData gameObject = new()
122+
{
123+
Info = Info != null ? new() { Data = Info.Data } : null,
124+
Components = new(Components.Select(item => item.CloneCached())),
125+
Instance = Instance?.CloneCached(),
126+
};
127+
foreach (var child in Children)
128+
{
129+
var newChild = (GameObjectData)child.Clone();
130+
newChild.Parent = gameObject;
131+
gameObject.Children.Add(newChild);
132+
}
133+
return gameObject;
134+
}
135+
119136
public override string ToString()
120137
{
121138
return Name ?? "";
@@ -171,6 +188,7 @@ protected override bool DoRead()
171188
GameObjectRefInfoList.Clear();
172189
ResourceInfoList.Clear();
173190
UserdataInfoList.Clear();
191+
GameObjectDatas?.Clear();
174192

175193
var handler = FileHandler;
176194
var header = Header;
@@ -312,6 +330,27 @@ public static void CollectGameObjectInstances(GameObjectData gameObject, List<Rs
312330
}
313331
}
314332

333+
/// <summary>
334+
/// 迭代GameObject以及子物体的实例和组件实例
335+
/// </summary>
336+
/// <param name="gameObject"></param>
337+
/// <returns></returns>
338+
public static IEnumerable<RszInstance> IterGameObjectInstances(GameObjectData gameObject)
339+
{
340+
yield return gameObject.Instance!;
341+
foreach (var item in gameObject.Components)
342+
{
343+
yield return item;
344+
}
345+
foreach (var child in gameObject.Children)
346+
{
347+
foreach (var item in IterGameObjectInstances(child))
348+
{
349+
yield return item;
350+
}
351+
}
352+
}
353+
315354
public IEnumerable<GameObjectData> IterGameObjects(GameObjectData? parent = null, bool includeChildren = false)
316355
{
317356
var items = parent?.Children ?? GameObjectDatas;
@@ -422,6 +461,89 @@ public void PfbFromScnGameObject(ScnFile.GameObjectData scnGameObject)
422461
RebuildInfoTable();
423462
}
424463

464+
public void RemoveGameObject(GameObjectData gameObject)
465+
{
466+
if (gameObject.Parent != null)
467+
{
468+
gameObject.Parent.Children.Remove(gameObject);
469+
gameObject.Parent = null;
470+
}
471+
else
472+
{
473+
GameObjectDatas?.Remove(gameObject);
474+
}
475+
StructChanged = true;
476+
}
477+
478+
/// <summary>
479+
/// 导入外部的游戏对象
480+
/// 文件夹和父对象只能指定一个
481+
/// </summary>
482+
/// <param name="gameObject"></param>
483+
/// <param name="folder">文件夹</param>
484+
/// <param name="parent">父对象</param>
485+
/// <param name="isDuplicate">在原对象的位置后面添加</param>
486+
public void ImportGameObject(GameObjectData gameObject,
487+
GameObjectData? parent = null, bool isDuplicate = false)
488+
{
489+
RszInstance.CleanCloneCache();
490+
GameObjectData newGameObject = (GameObjectData)gameObject.Clone();
491+
492+
newGameObject.Parent = null;
493+
ObservableCollection<GameObjectData> collection;
494+
if (parent != null)
495+
{
496+
newGameObject.Parent = parent;
497+
collection = parent.Children;
498+
}
499+
else
500+
{
501+
GameObjectDatas ??= [];
502+
collection = GameObjectDatas;
503+
}
504+
505+
// 为了可视化重新排序号,否则会显示序号是-1,但实际上保存的时候的序号和现在编号的可能不一致
506+
// 所以要考虑这步操作是否有必要
507+
RSZ!.FixInstanceListIndex(IterGameObjectInstances(newGameObject));
508+
509+
if (isDuplicate)
510+
{
511+
int index = collection.IndexOf(gameObject);
512+
index = index == -1 ? collection.Count : index + 1;
513+
collection.Insert(index, newGameObject);
514+
}
515+
else
516+
{
517+
collection.Add(newGameObject);
518+
}
519+
StructChanged = true;
520+
RszInstance.CleanCloneCache();
521+
}
522+
523+
/// <summary>
524+
/// 导入外部的游戏对象
525+
/// 批量添加建议直接GameObjectDatas添加,最后再RebuildInfoTable
526+
/// </summary>
527+
/// <param name="gameObject"></param>
528+
public void ImportGameObjects(
529+
IEnumerable<GameObjectData> gameObjects,
530+
GameObjectData? parent = null)
531+
{
532+
foreach (var gameObject in gameObjects)
533+
{
534+
ImportGameObject(gameObject, parent);
535+
}
536+
}
537+
538+
/// <summary>
539+
/// 复制游戏对象
540+
/// </summary>
541+
/// <param name="gameObject"></param>
542+
public void DuplicateGameObject(GameObjectData gameObject)
543+
{
544+
ImportGameObject(gameObject, gameObject.Parent, true);
545+
}
546+
425547
/// <summary>
426548
/// 添加组件
427549
/// </summary>

RszTool/RszFile/RszUtils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public static void AddResourceFromRsz(List<ResourceInfo> resourcesInfos, RSZFile
5353
}
5454
void CheckResouce(string path)
5555
{
56-
if (path.Contains('/') && !addedPath.Contains(path))
56+
if (path.Contains('/') && Path.GetExtension(path) != "" && !addedPath.Contains(path))
5757
{
5858
addedPath.Add(path);
5959
resourcesInfos.Add(new ResourceInfo { Path = path });

RszTool/RszFile/RszValueType.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ public readonly string Hex()
286286
return rgba.ToString("X8");
287287
}
288288

289+
public readonly override string ToString()
290+
{
291+
return Hex();
292+
}
293+
289294
/// <summary>
290295
/// Parse a hex string
291296
/// </summary>

RszTool/RszFile/ScnFile.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -551,19 +551,18 @@ public void RebuildInfoTable()
551551
GameObjectInfoList.Clear();
552552
FolderInfoList.Clear();
553553
PrefabInfoList.Clear();
554-
if (GameObjectDatas != null)
554+
if (FolderDatas != null)
555555
{
556-
foreach (var gameObjectData in GameObjectDatas)
556+
foreach (var folder in FolderDatas)
557557
{
558-
AddGameObjectInfoRecursion(gameObjectData);
558+
AddFolderInfoRecursion(folder);
559559
}
560560
}
561-
562-
if (FolderDatas != null)
561+
if (GameObjectDatas != null)
563562
{
564-
foreach (var folder in FolderDatas)
563+
foreach (var gameObjectData in GameObjectDatas)
565564
{
566-
AddFolderInfoRecursion(folder);
565+
AddGameObjectInfoRecursion(gameObjectData);
567566
}
568567
}
569568
RszUtils.SyncUserDataFromRsz(UserdataInfoList, RSZ);

0 commit comments

Comments
 (0)