Skip to content

Commit

Permalink
feat: Pfb game object support copy/paste and fix some bugs
Browse files Browse the repository at this point in the history
Fix pfb reopen bug
Fix recent files menu display
Check resource path when rebulid info
  • Loading branch information
czastack committed Dec 13, 2023
1 parent 5f5483f commit 3428817
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 12 deletions.
4 changes: 4 additions & 0 deletions RszTool.App/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@
<MenuItem Header="{res:Text OpenRecentFiles}">
<MenuItem.Resources>
<CollectionViewSource x:Key="RecentFiles" Source="{Binding SaveData.RecentFiles}"/>
<DataTemplate x:Key="StringOnlyTemplate">
<TextBlock Text="{Binding}" />
</DataTemplate>
</MenuItem.Resources>
<MenuItem.ItemsSource>
<CompositeCollection>
Expand All @@ -97,6 +100,7 @@
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding DataContext.OpenRecentFile, RelativeSource={RelativeSource AncestorType=Window}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
<Setter Property="HeaderTemplate" Value="{StaticResource StringOnlyTemplate}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
Expand Down
4 changes: 2 additions & 2 deletions RszTool.App/RszTool.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<UseWPF>true</UseWPF>
<NoWarn>$(NoWarn);CS0067</NoWarn>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AssemblyVersion>0.2.0.0</AssemblyVersion>
<FileVersion>0.2.0.0</FileVersion>
<AssemblyVersion>0.2.1.0</AssemblyVersion>
<FileVersion>0.2.1.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions RszTool.App/ViewModels/BaseRszFileViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public bool SaveAs(string path)
{
Changed = false;
HeaderChanged?.Invoke();
OnPropertyChanged(nameof(FilePath));
}
return result;
}
Expand Down
67 changes: 66 additions & 1 deletion RszTool.App/ViewModels/PfbFileViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ public class PfbFileViewModel(PfbFile file) : BaseRszFileViewModel
public GameObjectSearchViewModel GameObjectSearchViewModel { get; } = new() { IncludeChildren = true };
public ObservableCollection<PfbFile.GameObjectData>? SearchGameObjectList { get; set; }


public static PfbFile.GameObjectData? CopiedGameObject { get; private set; }

public RelayCommand CopyGameObject => new(OnCopyGameObject);
public RelayCommand RemoveGameObject => new(OnRemoveGameObject);
public RelayCommand DuplicateGameObject => new(OnDuplicateGameObject);
public RelayCommand PasteGameObject => new(OnPasteGameObject);
public RelayCommand PasteGameobjectAsChild => new(OnPasteGameobjectAsChild);
public RelayCommand SearchGameObjects => new(OnSearchGameObjects);
public RelayCommand AddComponent => new(OnAddComponent);
public RelayCommand PasteInstanceAsComponent => new(OnPasteInstanceAsComponent);
Expand All @@ -29,7 +37,64 @@ public override IEnumerable<object> TreeViewItems
{
get
{
yield return new TreeItemViewModel("GameObjects", GameObjects);
yield return new GameObjectsHeader("GameObjects", GameObjects);
}
}

/// <summary>
/// 复制游戏对象
/// </summary>
/// <param name="arg"></param>
private static void OnCopyGameObject(object arg)
{
CopiedGameObject = (PfbFile.GameObjectData)arg;
}

/// <summary>
/// 删除游戏对象
/// </summary>
/// <param name="arg"></param>
private void OnRemoveGameObject(object arg)
{
PfbFile.RemoveGameObject((PfbFile.GameObjectData)arg);
Changed = true;
}

/// <summary>
/// 重复游戏对象
/// </summary>
/// <param name="arg"></param>
private void OnDuplicateGameObject(object arg)
{
PfbFile.DuplicateGameObject((PfbFile.GameObjectData)arg);
Changed = true;
}

/// <summary>
/// 粘贴游戏对象
/// </summary>
/// <param name="arg"></param>
private void OnPasteGameObject(object arg)
{
if (CopiedGameObject != null)
{
PfbFile.ImportGameObject(CopiedGameObject);
OnPropertyChanged(nameof(GameObjects));
Changed = true;
}
}

/// <summary>
/// 粘贴游戏对象到父对象
/// </summary>
/// <param name="arg"></param>
private void OnPasteGameobjectAsChild(object arg)
{
if (CopiedGameObject != null)
{
var parent = (PfbFile.GameObjectData)arg;
PfbFile.ImportGameObject(CopiedGameObject, parent: parent);
Changed = true;
}
}

Expand Down
14 changes: 14 additions & 0 deletions RszTool.App/Views/RszPfbFileView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,26 @@
<converters:PfbGameObjectDataSubItemsConverter x:Key="PfbGameObjectDataSubItemsConverter"/>
<common:BindingProxy x:Key="ParentData" Data="{Binding}" />

<HierarchicalDataTemplate DataType="{x:Type viewmodels:GameObjectsHeader}" ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="{res:Text PasteGameobject}" Command="{common:ParentBinding PasteGameObject}" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</HierarchicalDataTemplate>

<HierarchicalDataTemplate DataType="{x:Type rsztool:PfbFile+GameObjectData}"
ItemsSource="{Binding ., Converter={StaticResource PfbGameObjectDataSubItemsConverter}}"
ItemContainerStyle="{StaticResource TreeViewItemHideIfEmpty}">
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="{res:Text Copy}" Command="{common:ParentBinding CopyGameObject}" CommandParameter="{Binding}" />
<MenuItem Header="{res:Text Remove}" Command="{common:ParentBinding RemoveGameObject}" CommandParameter="{Binding}" />
<MenuItem Header="{res:Text Duplicate}" Command="{common:ParentBinding DuplicateGameObject}" CommandParameter="{Binding}" />
<MenuItem Header="{res:Text PasteGameobjectAsChild}" Command="{common:ParentBinding PasteGameobjectAsChild}" CommandParameter="{Binding}" />
<MenuItem Header="{res:Text AddComponent}" Command="{common:ParentBinding AddComponent}" CommandParameter="{Binding}" />
<MenuItem Header="{res:Text PasteInstanceAsComponent}" Command="{common:ParentBinding PasteInstanceAsComponent}" CommandParameter="{Binding}" />
</ContextMenu>
Expand Down
2 changes: 1 addition & 1 deletion RszTool.App/Views/RszScnFileView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<TextBlock Text="{Binding Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="{res:Text PasteGameobject}" Command="{Binding Data.PasteGameObject, Source={StaticResource ParentData}}" />
<MenuItem Header="{res:Text PasteGameobject}" Command="{common:ParentBinding PasteGameObject}" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
Expand Down
122 changes: 122 additions & 0 deletions RszTool/RszFile/PfbFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,23 @@ public static GameObjectData FromScnGameObject(ScnFile.GameObjectData scnGameObj

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

public object Clone()
{
GameObjectData gameObject = new()
{
Info = Info != null ? new() { Data = Info.Data } : null,
Components = new(Components.Select(item => item.CloneCached())),
Instance = Instance?.CloneCached(),
};
foreach (var child in Children)
{
var newChild = (GameObjectData)child.Clone();
newChild.Parent = gameObject;
gameObject.Children.Add(newChild);
}
return gameObject;
}

public override string ToString()
{
return Name ?? "";
Expand Down Expand Up @@ -171,6 +188,7 @@ protected override bool DoRead()
GameObjectRefInfoList.Clear();
ResourceInfoList.Clear();
UserdataInfoList.Clear();
GameObjectDatas?.Clear();

var handler = FileHandler;
var header = Header;
Expand Down Expand Up @@ -312,6 +330,27 @@ public static void CollectGameObjectInstances(GameObjectData gameObject, List<Rs
}
}

/// <summary>
/// 迭代GameObject以及子物体的实例和组件实例
/// </summary>
/// <param name="gameObject"></param>
/// <returns></returns>
public static IEnumerable<RszInstance> IterGameObjectInstances(GameObjectData gameObject)
{
yield return gameObject.Instance!;
foreach (var item in gameObject.Components)
{
yield return item;
}
foreach (var child in gameObject.Children)
{
foreach (var item in IterGameObjectInstances(child))
{
yield return item;
}
}
}

public IEnumerable<GameObjectData> IterGameObjects(GameObjectData? parent = null, bool includeChildren = false)
{
var items = parent?.Children ?? GameObjectDatas;
Expand Down Expand Up @@ -422,6 +461,89 @@ public void PfbFromScnGameObject(ScnFile.GameObjectData scnGameObject)
RebuildInfoTable();
}

public void RemoveGameObject(GameObjectData gameObject)
{
if (gameObject.Parent != null)
{
gameObject.Parent.Children.Remove(gameObject);
gameObject.Parent = null;
}
else
{
GameObjectDatas?.Remove(gameObject);
}
StructChanged = true;
}

/// <summary>
/// 导入外部的游戏对象
/// 文件夹和父对象只能指定一个
/// </summary>
/// <param name="gameObject"></param>
/// <param name="folder">文件夹</param>
/// <param name="parent">父对象</param>
/// <param name="isDuplicate">在原对象的位置后面添加</param>
public void ImportGameObject(GameObjectData gameObject,
GameObjectData? parent = null, bool isDuplicate = false)
{
RszInstance.CleanCloneCache();
GameObjectData newGameObject = (GameObjectData)gameObject.Clone();

newGameObject.Parent = null;
ObservableCollection<GameObjectData> collection;
if (parent != null)
{
newGameObject.Parent = parent;
collection = parent.Children;
}
else
{
GameObjectDatas ??= [];
collection = GameObjectDatas;
}

// 为了可视化重新排序号,否则会显示序号是-1,但实际上保存的时候的序号和现在编号的可能不一致
// 所以要考虑这步操作是否有必要
RSZ!.FixInstanceListIndex(IterGameObjectInstances(newGameObject));

if (isDuplicate)
{
int index = collection.IndexOf(gameObject);
index = index == -1 ? collection.Count : index + 1;
collection.Insert(index, newGameObject);
}
else
{
collection.Add(newGameObject);
}
StructChanged = true;
RszInstance.CleanCloneCache();
}

/// <summary>
/// 导入外部的游戏对象
/// 批量添加建议直接GameObjectDatas添加,最后再RebuildInfoTable
/// </summary>
/// <param name="gameObject"></param>
public void ImportGameObjects(
IEnumerable<GameObjectData> gameObjects,
GameObjectData? parent = null)
{
foreach (var gameObject in gameObjects)
{
ImportGameObject(gameObject, parent);
}
}

/// <summary>
/// 复制游戏对象
/// </summary>
/// <param name="gameObject"></param>
public void DuplicateGameObject(GameObjectData gameObject)
{
ImportGameObject(gameObject, gameObject.Parent, true);
}

/// <summary>
/// 添加组件
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion RszTool/RszFile/RszUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static void AddResourceFromRsz(List<ResourceInfo> resourcesInfos, RSZFile
}
void CheckResouce(string path)
{
if (path.Contains('/') && !addedPath.Contains(path))
if (path.Contains('/') && Path.GetExtension(path) != "" && !addedPath.Contains(path))
{
addedPath.Add(path);
resourcesInfos.Add(new ResourceInfo { Path = path });
Expand Down
5 changes: 5 additions & 0 deletions RszTool/RszFile/RszValueType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,11 @@ public struct Color
return rgba.ToString("X8");
}

public readonly override string ToString()
{
return Hex();
}

/// <summary>
/// Parse a hex string
/// </summary>
Expand Down
13 changes: 6 additions & 7 deletions RszTool/RszFile/ScnFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -551,19 +551,18 @@ public void RebuildInfoTable()
GameObjectInfoList.Clear();
FolderInfoList.Clear();
PrefabInfoList.Clear();
if (GameObjectDatas != null)
if (FolderDatas != null)
{
foreach (var gameObjectData in GameObjectDatas)
foreach (var folder in FolderDatas)
{
AddGameObjectInfoRecursion(gameObjectData);
AddFolderInfoRecursion(folder);
}
}

if (FolderDatas != null)
if (GameObjectDatas != null)
{
foreach (var folder in FolderDatas)
foreach (var gameObjectData in GameObjectDatas)
{
AddFolderInfoRecursion(folder);
AddGameObjectInfoRecursion(gameObjectData);
}
}
RszUtils.SyncUserDataFromRsz(UserdataInfoList, RSZ);
Expand Down

0 comments on commit 3428817

Please sign in to comment.