Support for saving node data to json files.

支持将节点数据转存为json文件了。
This commit is contained in:
Cold-Mint 2024-05-15 21:52:07 +08:00
parent 2fded22910
commit 2e64f57749
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
15 changed files with 420 additions and 71 deletions

View File

@ -7,7 +7,4 @@
<RootNamespace>ColdMint</RootNamespace> <RootNamespace>ColdMint</RootNamespace>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Folder Include="scripts\map\branch\" />
</ItemGroup>
</Project> </Project>

View File

@ -22,4 +22,7 @@ default_room_name,房间{0},Room{0},部屋{0}です
room_template_collection_prompt,房间模板集(每一行被认为是一条房间模板地址。),Room template set (Each row is considered to be one room template address.),お部屋テンプレートセット(各行がお部屋テンプレートアドレスとされています。) room_template_collection_prompt,房间模板集(每一行被认为是一条房间模板地址。),Room template set (Each row is considered to be one room template address.),お部屋テンプレートセット(各行がお部屋テンプレートアドレスとされています。)
error_specifying_room_template_line,位于{0}错误,文件不存在。,"Located at {0} error, file does not exist.",{0}エラーに位置し、ファイルは存在しません。 error_specifying_room_template_line,位于{0}错误,文件不存在。,"Located at {0} error, file does not exist.",{0}エラーに位置し、ファイルは存在しません。
line_errors_must_start_with_res,位于{0}错误必须以res://开头。,"Located at {0} error, must start with res://.",{0}エラーに位置し、res://で始めなければなりません。 line_errors_must_start_with_res,位于{0}错误必须以res://开头。,"Located at {0} error, must start with res://.",{0}エラーに位置し、res://で始めなければなりません。
display_current_page_data,显示当前页面数据,Displays the current page data,現在のページデータを表示します open_the_export_directory,打开导出目录,Open the export directory,エクスポートディレクトリを開きます
save,保存,Save,保留
filename,文件名,File name,ファイル名
cancel,取消,Cancel,キャンセル
1 id zh en jp
22 room_template_collection_prompt 房间模板集(每一行被认为是一条房间模板地址。) Room template set (Each row is considered to be one room template address.) お部屋テンプレートセット(各行がお部屋テンプレートアドレスとされています。)
23 error_specifying_room_template_line 位于{0}错误,文件不存在。 Located at {0} error, file does not exist. {0}エラーに位置し、ファイルは存在しません。
24 line_errors_must_start_with_res 位于{0}错误,必须以res://开头。 Located at {0} error, must start with res://. {0}エラーに位置し、res://で始めなければなりません。
25 display_current_page_data open_the_export_directory 显示当前页面数据 打开导出目录 Displays the current page data Open the export directory 現在のページデータを表示します エクスポートディレクトリを開きます
26 save 保存 Save 保留
27 filename 文件名 File name ファイル名
28 cancel 取消 Cancel キャンセル

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -152,9 +152,13 @@ offset_right = -15.0
offset_bottom = 47.0 offset_bottom = 47.0
grow_horizontal = 0 grow_horizontal = 0
[node name="DisplayCurrentPageData" type="Button" parent="HBoxContainer"] [node name="OpenExportFolderButton" type="Button" parent="HBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "display_current_page_data" text = "open_the_export_directory"
[node name="SaveButton" type="Button" parent="HBoxContainer"]
layout_mode = 2
text = "save"
[node name="ShowCreateRoomPanelButton" type="Button" parent="HBoxContainer"] [node name="ShowCreateRoomPanelButton" type="Button" parent="HBoxContainer"]
layout_mode = 2 layout_mode = 2
@ -163,3 +167,75 @@ text = "create_room"
[node name="ReturnButton" type="Button" parent="HBoxContainer"] [node name="ReturnButton" type="Button" parent="HBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "close" text = "close"
[node name="SaveOrloadPanel" type="Panel" parent="."]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 232.0
offset_top = 202.0
offset_right = -121.0
offset_bottom = -155.0
grow_horizontal = 2
grow_vertical = 2
[node name="Label" type="Label" parent="SaveOrloadPanel"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -18.5
offset_top = 15.0
offset_right = 21.5
offset_bottom = 40.0
grow_horizontal = 2
text = "save"
[node name="LineEdit" type="LineEdit" parent="SaveOrloadPanel"]
layout_mode = 1
anchors_preset = 14
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_left = 18.0
offset_top = -5.0
offset_right = -18.0
offset_bottom = 28.0
grow_horizontal = 2
grow_vertical = 2
[node name="Label2" type="Label" parent="SaveOrloadPanel"]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = 20.0
offset_top = -38.0
offset_right = 80.0
offset_bottom = -13.0
grow_vertical = 2
text = "filename"
[node name="HBoxContainer" type="HBoxContainer" parent="SaveOrloadPanel"]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -117.0
offset_top = -50.0
offset_right = -19.0
offset_bottom = -10.0
grow_horizontal = 0
grow_vertical = 0
[node name="CancelButton" type="Button" parent="SaveOrloadPanel/HBoxContainer"]
layout_mode = 2
text = "cancel"
[node name="ActionButton" type="Button" parent="SaveOrloadPanel/HBoxContainer"]
layout_mode = 2
text = "save"

View File

@ -133,6 +133,85 @@ public static class Config
return OS.HasFeature("debug"); return OS.HasFeature("debug");
} }
public enum OsEnum
{
//unknown
//未知
Unknown,
//Runs on Android (non-web browser)
//在 Android 上运行(非 Web 浏览器)
Android,
//Runs on Linux (non-web browser)
//在 Linux 上运行(非 Web 浏览器)
Linux,
//Runs on macOS (non-Web browser)
//在 macOS 上运行(非 Web 浏览器)
Macos,
//Runs on iOS (non-Web browser)
//在 iOS 上运行(非 Web 浏览器)
Ios,
//Runs on Windows
//在 Windows 上运行
Windows,
//The host operating system is a web browser
//宿主操作系统是网页浏览器
Web,
//Editor
//编辑器
// Editor
}
/// <summary>
/// <para>Get what platform is currently running on</para>
/// <para>获取当前在什么平台上运行</para>
/// </summary>
/// <returns></returns>
public static OsEnum GetOs()
{
// if (OS.HasFeature("editor"))
// {
// return OsEnum.Editor;
// }
if (OS.HasFeature("windows"))
{
return OsEnum.Windows;
}
if (OS.HasFeature("android"))
{
return OsEnum.Android;
}
if (OS.HasFeature("linux"))
{
return OsEnum.Linux;
}
if (OS.HasFeature("web"))
{
return OsEnum.Web;
}
if (OS.HasFeature("macos"))
{
return OsEnum.Macos;
}
if (OS.HasFeature("ios"))
{
return OsEnum.Ios;
}
return OsEnum.Unknown;
}
public static string GetVersion() public static string GetVersion()
{ {
var stringBuilder = new StringBuilder(); var stringBuilder = new StringBuilder();
@ -162,6 +241,17 @@ public static class Config
} }
} }
/// <summary>
/// <para>Get the export directory for the level graph</para>
/// <para>获取关卡图的导出目录</para>
/// </summary>
/// <returns></returns>
public static string GetLevelGraphExportDirectory()
{
return Path.Join(GetGameDataDirectory(), "LevelGraphs");
}
/// <summary> /// <summary>
/// <para>The initial year of creating this game</para> /// <para>The initial year of creating this game</para>
/// <para>创建此游戏的初始年份</para> /// <para>创建此游戏的初始年份</para>
@ -215,11 +305,6 @@ public static class Config
/// </summary> /// </summary>
public const int HorizontalSpeedOfDamageNumbers = 3; public const int HorizontalSpeedOfDamageNumbers = 3;
/// <summary>
/// <para>The file name of the packet's manifest</para>
/// <para>数据包的清单文件名</para>
/// </summary>
public const string DataPackManifestName = "DataPackManifest.json";
/// <summary> /// <summary>
/// <para>VerticalVelocityOfDamageNumbers</para> /// <para>VerticalVelocityOfDamageNumbers</para>

View File

@ -2,8 +2,8 @@
public class ConnectionData public class ConnectionData
{ {
public IRoomNodeData? From { get; set; } public string? FromId { get; set; }
public IRoomNodeData? To { get; set; } public string? ToId { get; set; }
public int FromPort { get; set; } public int FromPort { get; set; }
public int ToPort { get; set; } public int ToPort { get; set; }
} }

View File

@ -2,6 +2,8 @@
public interface IRoomNodeData public interface IRoomNodeData
{ {
string Id { get; set; }
/// <summary> /// <summary>
/// <para>Title</para> /// <para>Title</para>
/// <para>标题</para> /// <para>标题</para>

View File

@ -8,5 +8,7 @@ namespace ColdMint.scripts.levelGraphEditor;
/// </summary> /// </summary>
public class LevelGraphEditorSaveData public class LevelGraphEditorSaveData
{ {
public List<ConnectionData>? ConnectionData { get; set; } public List<ConnectionData>? ConnectionDataList { get; set; }
public List<IRoomNodeData>? RoomNodeDataList { get; set; }
} }

View File

@ -2,6 +2,7 @@
public class RoomNodeData : IRoomNodeData public class RoomNodeData : IRoomNodeData
{ {
public string Id { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Description { get; set; } public string Description { get; set; }
} }

View File

@ -1,11 +1,12 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json; using System.Diagnostics;
using ColdMint.scripts.debug; using System.IO;
using ColdMint.scripts.levelGraphEditor; using ColdMint.scripts.levelGraphEditor;
using ColdMint.scripts.serialization; using ColdMint.scripts.serialization;
using ColdMint.scripts.utils; using ColdMint.scripts.utils;
using Godot; using Godot;
using Godot.Collections; using Godot.Collections;
using FileAccess = Godot.FileAccess;
namespace ColdMint.scripts.loader.uiLoader; namespace ColdMint.scripts.loader.uiLoader;
@ -34,13 +35,20 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
private int _roomIndex = 1; private int _roomIndex = 1;
private TextEdit? _roomTemplateCollectionTextEdit; private TextEdit? _roomTemplateCollectionTextEdit;
private Label? _roomTemplateTipsLabel; private Label? _roomTemplateTipsLabel;
private Button? _displayCurrentPageData; private Button? _saveButton;
private Button? _openExportFolderButton;
private HBoxContainer? _hBoxContainer;
public override void InitializeData() public override void InitializeData()
{ {
base.InitializeData(); base.InitializeData();
_roomNodeScene = (PackedScene)GD.Load("res://prefab/ui/RoomNode.tscn"); _roomNodeScene = (PackedScene)GD.Load("res://prefab/ui/RoomNode.tscn");
_defaultRoomName = TranslationServer.Translate("default_room_name"); _defaultRoomName = TranslationServer.Translate("default_room_name");
var folder = Config.GetLevelGraphExportDirectory();
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
} }
@ -53,7 +61,16 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
_roomTemplateTipsLabel.Text = string.Empty; _roomTemplateTipsLabel.Text = string.Empty;
} }
_displayCurrentPageData = GetNode<Button>("HBoxContainer/DisplayCurrentPageData"); _openExportFolderButton = GetNode<Button>("HBoxContainer/OpenExportFolderButton");
if (_openExportFolderButton != null)
{
//If open directories are supported, a button is displayed.
//若支持打开目录,那么显示按钮。
_openExportFolderButton.Visible = ExplorerUtils.SupportOpenDirectory();
}
_hBoxContainer = GetNode<HBoxContainer>("HBoxContainer");
_saveButton = GetNode<Button>("HBoxContainer/SaveButton");
_roomTemplateCollectionTextEdit = GetNode<TextEdit>("CreateOrEditorPanel/RoomTemplateCollectionTextEdit"); _roomTemplateCollectionTextEdit = GetNode<TextEdit>("CreateOrEditorPanel/RoomTemplateCollectionTextEdit");
_graphEdit = GetNode<GraphEdit>("GraphEdit"); _graphEdit = GetNode<GraphEdit>("GraphEdit");
_showCreateRoomPanelButton = GetNode<Button>("HBoxContainer/ShowCreateRoomPanelButton"); _showCreateRoomPanelButton = GetNode<Button>("HBoxContainer/ShowCreateRoomPanelButton");
@ -167,6 +184,14 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
}; };
} }
if (_openExportFolderButton != null)
{
_openExportFolderButton.Pressed += () =>
{
ExplorerUtils.OpenFolder(Config.GetLevelGraphExportDirectory());
};
}
if (_showCreateRoomPanelButton != null) if (_showCreateRoomPanelButton != null)
{ {
_showCreateRoomPanelButton.Pressed += () => _showCreateRoomPanelButton.Pressed += () =>
@ -186,9 +211,9 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
_roomNameLineEdit.Text = string.Format(_defaultRoomName, _roomIndex); _roomNameLineEdit.Text = string.Format(_defaultRoomName, _roomIndex);
} }
if (_returnButton != null) if (_hBoxContainer != null)
{ {
_returnButton.Visible = false; _hBoxContainer.Visible = false;
} }
_showCreateRoomPanelButton.Visible = false; _showCreateRoomPanelButton.Visible = false;
@ -219,6 +244,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
var roomNodeData = new RoomNodeData var roomNodeData = new RoomNodeData
{ {
Id = GuidUtils.GetGuid(),
Title = _roomNameLineEdit.Text, Title = _roomNameLineEdit.Text,
Description = _roomDescriptionLineEdit.Text Description = _roomDescriptionLineEdit.Text
}; };
@ -230,78 +256,131 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
}; };
} }
if (_displayCurrentPageData != null) if (_saveButton != null)
{ {
_displayCurrentPageData.Pressed += () => _saveButton.Pressed += () =>
{ {
if (_graphEdit == null) if (_graphEdit == null)
{ {
return; return;
} }
Array<Dictionary> connectionList = _graphEdit.GetConnectionList();
var levelGraphEditorSaveData = new LevelGraphEditorSaveData(); var levelGraphEditorSaveData = new LevelGraphEditorSaveData();
var connectionDataList = new List<ConnectionData>(); //Serialize room node information
levelGraphEditorSaveData.ConnectionData = connectionDataList; //序列化房间节点信息
if (connectionList.Count <= 0) return; var length = _graphEdit.GetChildCount();
foreach (var dictionary in connectionList) if (length <= 0)
{ {
if (dictionary == null) //no room
//没有房间
return;
}
var roomNodeDataList = new List<IRoomNodeData>();
levelGraphEditorSaveData.RoomNodeDataList = roomNodeDataList;
for (var i = 0; i < length; i++)
{
var node = _graphEdit.GetChild(i);
if (node is not RoomNode roomNode) continue;
var data = roomNode.RoomNodeData;
if (data == null)
{ {
continue; continue;
} }
var keys = dictionary.Keys; roomNodeDataList.Add(data);
if (keys.Count == 0) }
{
continue;
}
var connectionData = new ConnectionData(); //Serialized connection information
foreach (var variant in keys) //序列化连接信息
Array<Dictionary> connectionList = _graphEdit.GetConnectionList();
var connectionDataList = new List<ConnectionData>();
levelGraphEditorSaveData.ConnectionDataList = connectionDataList;
if (connectionList.Count > 0)
{
foreach (var dictionary in connectionList)
{ {
var typeStr = variant.ToString(); if (dictionary == null)
switch (typeStr)
{ {
case "from_node": continue;
// LogCat.Log("查找"+dictionary[variant].Obj); }
// var obj = dictionary[variant].Obj;
// if (obj == null) var keys = dictionary.Keys;
// { if (keys.Count == 0)
// LogCat.Log("obj is null"); {
// continue; continue;
// } }
//
// LogCat.Log("obj is " + obj.ToString()); var connectionData = new ConnectionData();
// var roomNode = dictionary[variant]. as RoomNode; foreach (var variant in keys)
// LogCat.Log("roomNode is" + (roomNode == null)); {
// var roomNode = dictionary[variant].As<RoomNode>(); var typeStr = variant.ToString();
// LogCat.Log("空的?" + (roomNode == null)+"类为"+dictionary[variant].ToString()+"路径为"+dictionary[variant].AsNodePath()+"对于C#"+dictionary[variant].Obj ); switch (typeStr)
// connectionData.From = dictionary[variant].Obj as RoomNode; {
// LogCat.Log("类型" + dictionary[variant]); case "from_node":
break; var fromRoomNodeData = GetRoomNodeData(dictionary[variant].AsString());
case "from_port": if (fromRoomNodeData == null)
connectionData.FromPort = dictionary[variant].AsInt32(); {
break; continue;
case "to_node": }
connectionData.To = dictionary[variant].Obj as IRoomNodeData;
break; connectionData.FromId = fromRoomNodeData.Id;
case "to_port": break;
connectionData.ToPort = dictionary[variant].AsInt32(); case "from_port":
break; connectionData.FromPort = dictionary[variant].AsInt32();
break;
case "to_node":
var toRoomNodeData = GetRoomNodeData(dictionary[variant].AsString());
if (toRoomNodeData == null)
{
continue;
}
connectionData.ToId = toRoomNodeData.Id;
break;
case "to_port":
connectionData.ToPort = dictionary[variant].AsInt32();
break;
}
} }
LogCat.Log(variant.ToString());
connectionDataList.Add(connectionData); connectionDataList.Add(connectionData);
} }
} }
LogCat.Log(JsonSerialization.Serialize(connectionDataList)); var filePath = Path.Join(Config.GetLevelGraphExportDirectory(), GuidUtils.GetGuid() + ".json");
File.WriteAllText(filePath, JsonSerialization.Serialize(levelGraphEditorSaveData));
}; };
} }
} }
/// <summary>
/// <para>Get node data by name</para>
/// <para>根据名称获取节点数据</para>
/// </summary>
/// <param name="name">
///<para>name</para>
///<para>名称</para>
/// </param>
/// <returns></returns>
private IRoomNodeData? GetRoomNodeData(string name)
{
if (_graphEdit == null)
{
return null;
}
var roomNode = _graphEdit.GetNodeOrNull<RoomNode>(name);
if (roomNode == null)
{
return null;
}
return roomNode.RoomNodeData;
}
/// <summary> /// <summary>
/// <para>Hide the Create Room panel</para> /// <para>Hide the Create Room panel</para>
/// <para>隐藏创建房间面板</para> /// <para>隐藏创建房间面板</para>
@ -318,9 +397,9 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
_createOrEditorPanel.Visible = false; _createOrEditorPanel.Visible = false;
} }
if (_returnButton != null) if (_hBoxContainer != null)
{ {
_returnButton.Visible = true; _hBoxContainer.Visible = true;
} }
if (_showCreateRoomPanelButton != null) if (_showCreateRoomPanelButton != null)

View File

@ -1,5 +1,7 @@
using System.IO; using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json; using System.Text.Json;
using System.Text.Unicode;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ColdMint.scripts.serialization; namespace ColdMint.scripts.serialization;
@ -10,7 +12,13 @@ public static class JsonSerialization
{ {
//Case-insensitive attribute matching //Case-insensitive attribute matching
//不区分大小写的属性匹配 //不区分大小写的属性匹配
PropertyNameCaseInsensitive = true PropertyNameCaseInsensitive = true,
//Try to avoid metastasis
//尽量避免转移
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
//Enable smart Print
//启用漂亮打印
WriteIndented = true
}; };
/// <summary> /// <summary>

View File

@ -0,0 +1,80 @@
using System;
using System.Diagnostics;
namespace ColdMint.scripts.utils;
/// <summary>
/// <para>Explorer Utils</para>
/// <para>资源管理器工具</para>
/// </summary>
public class ExplorerUtils
{
/// <summary>
/// <para>Call Explorer to open the directory</para>
/// <para>调用资源管理器打开目录</para>
/// </summary>
/// <param name="path">
///<para>要打开的目录路径</para>
///<para>The path of the directory to open</para>
/// </param>
public static void OpenFolder(string path)
{
var osEnum = Config.GetOs();
switch (osEnum)
{
case Config.OsEnum.Windows:
var startInfoWindows = new ProcessStartInfo
{
Arguments = path,
FileName = "explorer.exe"
};
Process.Start(startInfoWindows);
break;
case Config.OsEnum.Linux:
// Use the xdg-open command to open the directory on Linux
// 使用xdg-open命令在Linux上打开目录
var startInfoLinux = new ProcessStartInfo
{
Arguments = path,
FileName = "xdg-open"
};
Process.Start(startInfoLinux);
break;
case Config.OsEnum.Android:
// A different approach may be required on Android, as there is usually no desktop environment
// A general Intent is used here to open the file manager, but a specific implementation may be required
// The following code is only an indication, the actual implementation may need to be adjusted according to the Android API
// Android上可能需要使用不同的方法因为通常没有桌面环境
// 这里使用一个通用的Intent来打开文件管理器但可能需要具体的实现
// 以下代码只是一个示意实际的实现可能需要根据Android API进行调整
var startInfoAndroid = new ProcessStartInfo
{
Arguments = "VIEW",
FileName = "content://com.android.externalstorage.documents/tree/primary%3ADocuments"
};
Process.Start(startInfoAndroid);
break;
default:
throw new NotImplementedException($"No implementation for OS: {osEnum}");
}
}
/// <summary>
/// <para>Whether opening directories using Explorer is supported on the current system</para>
/// <para>在当前系统上是否支持使用资源管理器打开目录</para>
/// </summary>
/// <returns></returns>
public static bool SupportOpenDirectory()
{
var osEnum = Config.GetOs();
switch (osEnum)
{
case Config.OsEnum.Windows:
case Config.OsEnum.Linux:
case Config.OsEnum.Android:
return true;
default:
return false;
}
}
}

View File

@ -0,0 +1,16 @@
using System;
namespace ColdMint.scripts.utils;
public class GuidUtils
{
/// <summary>
/// <para>Get the new GUID</para>
/// <para>获取新的GUID</para>
/// </summary>
/// <returns></returns>
public static string GetGuid()
{
return Guid.NewGuid().ToString();
}
}