Level graphs migrated to yaml.

关卡图迁移到yaml。
This commit is contained in:
Cold-Mint 2024-06-16 22:44:50 +08:00
parent 7f55bd7d53
commit 8391396191
Signed by: Cold-Mint
GPG Key ID: C5A9BF8A98E0CE99
26 changed files with 195 additions and 171 deletions

View File

@ -13,4 +13,4 @@
value: 1000
- name: UniqueName
type: string
value: 劣化的死灵法杖
value: item_staff_of_the_undead_desc

View File

@ -1,88 +0,0 @@
{
"ConnectionDataList": [
{
"FromId": "c0255eb6-2c75-44f7-9058-0921fe8fb0d8",
"ToId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03",
"FromPort": 0,
"ToPort": 0
},
{
"FromId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03",
"ToId": "ba32e05c-0c80-4a79-b5ce-5b8150400e05",
"FromPort": 0,
"ToPort": 0
},
{
"FromId": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03",
"ToId": "8b3d645a-96c0-407e-871d-6f4a0b69557b",
"FromPort": 0,
"ToPort": 0
},
{
"FromId": "8b3d645a-96c0-407e-871d-6f4a0b69557b",
"ToId": "7e3ae680-3d7e-4ae9-b82f-5bfbe1c2f613",
"FromPort": 0,
"ToPort": 0
},
{
"FromId": "ba32e05c-0c80-4a79-b5ce-5b8150400e05",
"ToId": "7e3ae680-3d7e-4ae9-b82f-5bfbe1c2f613",
"FromPort": 0,
"ToPort": 0
}
],
"RoomNodeDataList": [
{
"Id": "c0255eb6-2c75-44f7-9058-0921fe8fb0d8",
"Title": "起点房间",
"Description": "测试的起点房间。",
"RoomTemplateSet": [
"res://prefab/roomTemplates/dungeon/initialRoom.tscn"
],
"Tags": [
"StartingRoom"
],
"RoomInjectionProcessorData": null
},
{
"Id": "ba32e05c-0c80-4a79-b5ce-5b8150400e05",
"Title": "房间3",
"Description": "",
"RoomTemplateSet": [
"res://prefab/roomTemplates/dungeon/"
],
"Tags": null,
"RoomInjectionProcessorData": "[\n {\n \"Id\": \"TimeInterval\",\n \"Config\": \"{\\\"DateSpecifiesLevel\\\":0,\\\"StartTime\\\":\\\"2024/1/1 20:34:23\\\",\\\"EndTime\\\":\\\"2025/1/1 20:34:23\\\"}\"\n }\n]"
},
{
"Id": "4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03",
"Title": "大厅",
"Description": "比普通房间要大一些的房间,有多个门,用于连接到其他房间。",
"RoomTemplateSet": [
"res://prefab/roomTemplates/dungeon/utilityRoom.tscn"
],
"Tags": null,
"RoomInjectionProcessorData": null
},
{
"Id": "8b3d645a-96c0-407e-871d-6f4a0b69557b",
"Title": "房间4",
"Description": "",
"RoomTemplateSet": [
"res://prefab/roomTemplates/dungeon/"
],
"Tags": null,
"RoomInjectionProcessorData": ""
},
{
"Id": "7e3ae680-3d7e-4ae9-b82f-5bfbe1c2f613",
"Title": "房间5",
"Description": "",
"RoomTemplateSet": [
"res://prefab/roomTemplates/dungeon/"
],
"Tags": null,
"RoomInjectionProcessorData": ""
}
]
}

View File

@ -0,0 +1,57 @@
connection_data_list:
- from_id: c0255eb6-2c75-44f7-9058-0921fe8fb0d8
to_id: 4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03
from_port: 0
to_port: 0
- from_id: 4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03
to_id: ba32e05c-0c80-4a79-b5ce-5b8150400e05
from_port: 0
to_port: 0
- from_id: 4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03
to_id: 8b3d645a-96c0-407e-871d-6f4a0b69557b
from_port: 0
to_port: 0
- from_id: 8b3d645a-96c0-407e-871d-6f4a0b69557b
to_id: 7e3ae680-3d7e-4ae9-b82f-5bfbe1c2f613
from_port: 0
to_port: 0
- from_id: ba32e05c-0c80-4a79-b5ce-5b8150400e05
to_id: 7e3ae680-3d7e-4ae9-b82f-5bfbe1c2f613
from_port: 0
to_port: 0
room_node_data_list:
- id: c0255eb6-2c75-44f7-9058-0921fe8fb0d8
title: 起点房间
description: 测试的起点房间。
room_template_set:
- 'res://prefab/roomTemplates/dungeon/initialRoom.tscn'
tags:
- StartingRoom
room_injection_processor_data: null
- id: ba32e05c-0c80-4a79-b5ce-5b8150400e05
title: 房间3
description: ''
room_template_set:
- 'res://prefab/roomTemplates/dungeon/'
tags: null
- id: 4ae948ea-82b7-4b2d-bec2-19ed8a9d4c03
title: 大厅
description: 比普通房间要大一些的房间,有多个门,用于连接到其他房间。
room_template_set:
- 'res://prefab/roomTemplates/dungeon/utilityRoom.tscn'
tags: null
room_injection_processor_data: null
- id: 8b3d645a-96c0-407e-871d-6f4a0b69557b
title: 房间4
description: ''
room_template_set:
- 'res://prefab/roomTemplates/dungeon/'
tags: null
room_injection_processor_data: ''
- id: 7e3ae680-3d7e-4ae9-b82f-5bfbe1c2f613
title: 房间5
description: ''
room_template_set:
- 'res://prefab/roomTemplates/dungeon/'
tags: null
room_injection_processor_data: ''

View File

@ -147,6 +147,7 @@ hotbar_previous={
[internationalization]
locale/translations=PackedStringArray("res://locals/DeathInfo.en.translation", "res://locals/DeathInfo.ja.translation", "res://locals/DeathInfo.zh.translation", "res://locals/InputMapping.en.translation", "res://locals/InputMapping.ja.translation", "res://locals/InputMapping.zh.translation", "res://locals/Log.en.translation", "res://locals/Log.ja.translation", "res://locals/Log.zh.translation", "res://locals/Slogan.en.translation", "res://locals/Slogan.ja.translation", "res://locals/Slogan.zh.translation", "res://locals/UI.en.translation", "res://locals/UI.ja.translation", "res://locals/UI.zh.translation", "res://locals/Item.en.translation", "res://locals/Item.ja.translation", "res://locals/Item.zh.translation", "res://locals/Action.en.translation", "res://locals/Action.ja.translation", "res://locals/Action.zh.translation", "res://locals/Misc.en.translation", "res://locals/Misc.ja.translation", "res://locals/Misc.zh.translation")
locale/test="ja"
[layer_names]

View File

@ -238,7 +238,7 @@ offset_top = 15.0
offset_right = 21.5
offset_bottom = 40.0
grow_horizontal = 2
text = "save"
text = "ui_save"
[node name="FileNameLineEdit" type="LineEdit" parent="SaveOrLoadPanel"]
layout_mode = 1
@ -263,7 +263,7 @@ offset_top = -38.0
offset_right = 80.0
offset_bottom = -13.0
grow_vertical = 2
text = "filename"
text = "ui_filename"
[node name="HBoxContainer" type="HBoxContainer" parent="SaveOrLoadPanel"]
layout_mode = 1
@ -281,8 +281,8 @@ grow_vertical = 0
[node name="CancelButton" type="Button" parent="SaveOrLoadPanel/HBoxContainer"]
layout_mode = 2
text = "cancel"
text = "ui_cancel"
[node name="ActionButton" type="Button" parent="SaveOrLoadPanel/HBoxContainer"]
layout_mode = 2
text = "save"
text = "ui_save"

View File

@ -54,7 +54,6 @@ texture = ExtResource("2_n1yht")
[node name="OperationTip" type="RichTextLabel" parent="CanvasLayer/Control/VBoxContainer"]
layout_mode = 2
bbcode_enabled = true
text = "OperationTip"
fit_content = true
[node name="FPSLabel" type="Label" parent="CanvasLayer/Control"]
@ -85,6 +84,7 @@ offset_bottom = 25.0
grow_horizontal = 2
[node name="BackpackUIContainer" type="Control" parent="CanvasLayer"]
visible = false
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0

View File

@ -127,6 +127,7 @@ underline = 1
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer2"]
layout_mode = 2
size_flags_horizontal = 8
[node name="GodotLabel" type="Label" parent="VBoxContainer2/HBoxContainer"]
layout_mode = 2

View File

@ -205,7 +205,7 @@ public partial class CharacterTemplate : CharacterBody2D
CharacterName = GetMeta("Name", Name).AsString();
CampId = GetMeta("CampId", Config.CampId.Default).AsString();
MaxHp = GetMeta("MaxHp", Config.DefaultMaxHp).AsInt32();
var lootListId = GetMeta("LootListId", string.Empty).AsString();
// var lootListId = GetMeta("LootListId", string.Empty).AsString();
if (MaxHp <= 0)
{

View File

@ -4,7 +4,6 @@ using System.Threading.Tasks;
using ColdMint.scripts.damage;
using ColdMint.scripts.deathInfo;
using ColdMint.scripts.debug;
using ColdMint.scripts.item;
using ColdMint.scripts.map.events;
using ColdMint.scripts.utils;

View File

@ -1,15 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ColdMint.scripts.debug;
using ColdMint.scripts.serialization;
using ColdMint.scripts.utils;
using Godot;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace ColdMint.scripts.item;
/// <summary>
@ -21,7 +17,9 @@ public static class ItemTypeRegister
/// <para>Register items here</para>
/// <para>在这里注册物品</para>
/// </summary>
public static void StaticRegister() { }
public static void StaticRegister()
{
}
/// <summary>
/// <para>Register items from yaml file</para>
@ -30,29 +28,31 @@ public static class ItemTypeRegister
public static void RegisterFromFile()
{
LogCat.Log("start_item_register_from_file");
// initialize yaml deserializer
var deserializer = new DeserializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance) // convent snake_case
.Build();
//初始化文件目录
//initialize file dir
string itemRegsDirPath = "res://data/itemRegs/";
var itemRegsDirPath = "res://data/itemRegs/";
var itemRegsDir = DirAccess.Open(itemRegsDirPath);
if (DirAccess.GetOpenError() is not Error.Ok)
{
LogCat.LogError("error_when_open_item_regs_dir");
}
//找到文件
//find files
var files = itemRegsDir.GetFiles();
if (files == null)
{
LogCat.LogWithFormat("found_files", 0);
return;
}
LogCat.LogWithFormat("found_files", files.Length);
//将文件解析为项目类型信息
//parse files to item type infos
IEnumerable<ItemTypeInfo> typeInfos =
files.SelectMany(file => ParseFile(deserializer, $"{itemRegsDirPath}/{file}")).ToList();
files.SelectMany(file => ParseFile( $"{itemRegsDirPath}/{file}")).ToList();
LogCat.LogWithFormat("found_item_types", typeInfos.Count());
//遍历类型信息并注册它们。
//traverse type infos and register them.
foreach (var typeInfo in typeInfos)
{
@ -60,13 +60,19 @@ public static class ItemTypeRegister
}
}
private static IList<ItemTypeInfo> ParseFile(IDeserializer deserializer, string filePath)
/// <summary>
/// <para>ParseFile</para>
/// <para>解析文件</para>
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private static IList<ItemTypeInfo> ParseFile(string filePath)
{
var yamlFile = FileAccess.Open(filePath, FileAccess.ModeFlags.Read);
//阅读和反序列化
//Read & deserialize
var yamlString = yamlFile.GetAsText();
var typeInfos = deserializer.Deserialize<IList<ItemTypeInfo>>(yamlString);
var typeInfos = YamlSerialization.Deserialize<IList<ItemTypeInfo>>(yamlString);
yamlFile.Close();
return typeInfos;
}
@ -110,8 +116,13 @@ public static class ItemTypeRegister
//Use for yaml deserialization
private record struct ItemTypeInfo(
string Id, string ScenePath, string IconPath, int MaxStackValue,
IList<CustomArg>? CustomArgs) { }
string Id,
string ScenePath,
string IconPath,
int MaxStackValue,
IList<CustomArg>? CustomArgs)
{
}
private readonly record struct CustomArg(string Name, CustomArgType Type, string Value)
{

View File

@ -2,8 +2,6 @@ using System;
using ColdMint.scripts.character;
using ColdMint.scripts.pickable;
using ColdMint.scripts.damage;
using Godot;
namespace ColdMint.scripts.item.weapon;

View File

@ -583,7 +583,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
}
var filePath = Path.Join(Config.GetLevelGraphExportDirectory(), FileNameToActualName(fileName));
await File.WriteAllTextAsync(filePath, JsonSerialization.Serialize(levelGraphEditorSaveData));
await File.WriteAllTextAsync(filePath, YamlSerialization.Serialize(levelGraphEditorSaveData));
}
/// <summary>
@ -598,13 +598,13 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
private string FileNameToActualName(string fileName)
{
string actualName;
if (fileName.EndsWith(".json"))
if (fileName.EndsWith(".yaml"))
{
actualName = fileName;
}
else
{
actualName = fileName + ".json";
actualName = fileName + ".yaml";
}
return actualName;
@ -627,7 +627,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
}
var levelGraphEditorSaveData =
await JsonSerialization.ReadJsonFileToObj<LevelGraphEditorSaveData>(filePath);
await YamlSerialization.ReadYamlFileToObj<LevelGraphEditorSaveData>(filePath);
if (levelGraphEditorSaveData == null)
{
//Deserialization failed.

View File

@ -6,7 +6,6 @@ using ColdMint.scripts.camp;
using ColdMint.scripts.contribute;
using ColdMint.scripts.deathInfo;
using ColdMint.scripts.debug;
using ColdMint.scripts.inventory;
using ColdMint.scripts.item;
using ColdMint.scripts.loot;
using ColdMint.scripts.map;
@ -132,7 +131,6 @@ public partial class MainMenuLoader : UiLoaderTemplate
{
_startGameButton.Pressed += () =>
{
LogCat.Log("start_game");
if (_gameScene == null)
{
return;

View File

@ -1,5 +1,4 @@
using ColdMint.scripts.debug;
using ColdMint.scripts.inventory;
using ColdMint.scripts.inventory;
using ColdMint.scripts.utils;
using Godot;

View File

@ -1,6 +1,4 @@
using Godot;
namespace ColdMint.scripts.loot;
namespace ColdMint.scripts.loot;
public readonly record struct LootDatum(string ItemId, int Quantity)
{

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using ColdMint.scripts.utils;

View File

@ -4,8 +4,6 @@ using System.Collections.Generic;
using ColdMint.scripts.debug;
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.loot;
/// <summary>

View File

@ -1,9 +1,5 @@
using System.Collections.Generic;
using ColdMint.scripts.utils;
using Godot;
namespace ColdMint.scripts.loot;
/// <summary>

View File

@ -178,7 +178,6 @@ public static class MapGenerator
var roomDictionary = new Dictionary<string, Room>();
var randomNumberGenerator = new RandomNumberGenerator();
randomNumberGenerator.Seed = _seed;
LogCat.LogWithFormat("seed_info", _seed);
var startRoomNodeData = await _layoutParsingStrategy.GetStartRoomNodeData();
if (startRoomNodeData == null || string.IsNullOrEmpty(startRoomNodeData.Id))
{
@ -223,8 +222,8 @@ public static class MapGenerator
if (_roomInjectionProcessorsDictionary != null && !string.IsNullOrEmpty(roomInjectionProcessorData))
{
var roomInjectionProcessorDataArray =
JsonSerialization.Deserialize<RoomInjectionProcessorData[]>(roomInjectionProcessorData);
if (roomInjectionProcessorDataArray != null && roomInjectionProcessorDataArray.Length > 0)
YamlSerialization.Deserialize<RoomInjectionProcessorData[]>(roomInjectionProcessorData);
if (roomInjectionProcessorDataArray.Length > 0)
{
foreach (var injectionProcessorData in roomInjectionProcessorDataArray)
{

View File

@ -24,10 +24,10 @@ public interface IRoomInjectionProcessor
///<para>Random probability generator based on world seed</para>
///<para>根据世界种子确定的随机概率生成器</para>
/// </param>
/// <param name="jsonConfigData">
/// <param name="yamlConfigData">
///<para>Inject data into the processor</para>
///<para>注入处理器的数据</para>
/// </param>
/// <returns></returns>
public Task<bool> CanBePlaced(RandomNumberGenerator randomNumberGenerator, string? jsonConfigData);
public Task<bool> CanBePlaced(RandomNumberGenerator randomNumberGenerator, string? yamlConfigData);
}

View File

@ -11,7 +11,7 @@ namespace ColdMint.scripts.map.layoutStrategy;
/// </summary>
public class TestLayoutStrategy : ILayoutStrategy
{
private const string Path = "res://data/levelGraphs/test.json";
private const string Path = "res://data/levelGraphs/test.yaml";
public Task<LevelGraphEditorSaveData?> GetLayout()
{
@ -27,6 +27,6 @@ public class TestLayoutStrategy : ILayoutStrategy
return Task.FromResult<LevelGraphEditorSaveData?>(null);
}
return Task.FromResult(JsonSerialization.Deserialize<LevelGraphEditorSaveData>(json));
return Task.FromResult(YamlSerialization.Deserialize<LevelGraphEditorSaveData?>(json));
}
}

View File

@ -14,20 +14,15 @@ public abstract class RoomInjectionProcessorTemplate<TConfig> : IRoomInjectionPr
{
public abstract string GetId();
public Task<bool> CanBePlaced(RandomNumberGenerator randomNumberGenerator, string? jsonConfigData)
public Task<bool> CanBePlaced(RandomNumberGenerator randomNumberGenerator, string? yamlConfigData)
{
if (jsonConfigData == null)
if (yamlConfigData == null)
{
return Task.FromResult(false);
}
var configData = JsonSerialization.Deserialize<TConfig>(jsonConfigData);
if (configData == null)
{
return Task.FromResult(false);
}
return OnCreateConfigData(randomNumberGenerator, configData);
var configData = YamlSerialization.Deserialize<TConfig>(yamlConfigData);
return configData == null ? Task.FromResult(false) : OnCreateConfigData(randomNumberGenerator, configData);
}
/// <summary>

View File

@ -5,7 +5,7 @@ using ColdMint.scripts.character;
using ColdMint.scripts.damage;
using ColdMint.scripts.item;
using ColdMint.scripts.item.weapon;
using ColdMint.scripts.pickable;
using Godot;
namespace ColdMint.scripts.projectile;
@ -175,7 +175,7 @@ public partial class ProjectileTemplate : CharacterBody2D
characterTemplate.AddForce(force);
}
}
else if (target is WeaponTemplate weaponTemplate)
else if (target is PickAbleTemplate pickAbleTemplate)
{
if (KnockbackForce != Vector2.Zero)
{
@ -192,7 +192,7 @@ public partial class ProjectileTemplate : CharacterBody2D
force.X = forceX * Config.CellSize;
force.Y = KnockbackForce.Y * Config.CellSize;
weaponTemplate.ApplyImpulse(force);
pickAbleTemplate.ApplyImpulse(force);
}
}
}

View File

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading.Tasks;
@ -9,6 +10,12 @@ namespace ColdMint.scripts.serialization;
/// <para>JsonSerialization</para>
/// <para>Json序列化工具</para>
/// </summary>
/// <remarks>
///<para>This serializer is no longer recommended and Yaml is recommended instead of Json.</para>
///<para>此序列化器已不再推荐使用建议用Yaml代替Json。</para>
/// </remarks>
/// <seealso cref="YamlSerialization"/>
[Obsolete("The Json serializer is out of date, we recommend yaml serialization.\nJson序列化器已过时了我们推荐使用Yaml。", true)]
public static class JsonSerialization
{
private static readonly JsonSerializerOptions Options = new()

View File

@ -0,0 +1,58 @@
using System.IO;
using System.Threading.Tasks;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace ColdMint.scripts.serialization;
public static class YamlSerialization
{
/// <summary>
/// <para>YamlDeserializer</para>
/// <para>Yaml反序列化器</para>
/// </summary>
private static readonly IDeserializer YamlDeserializer = new DeserializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance) // convent snake_case
.Build();
private static readonly ISerializer YamlSerializer = new SerializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance) // convent snake_case
.Build();
/// <summary>
/// <para>Read a Json file to type T</para>
/// <para>读取一个Json文件到T类型</para>
/// </summary>
/// <param name="path"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<T?> ReadYamlFileToObj<T>(string path)
{
await using var openStream = File.OpenRead(path);
var yaml = await new StreamReader(openStream).ReadToEndAsync();
return YamlDeserializer.Deserialize<T>(yaml);
}
/// <summary>
/// <para>Serialize the object to Json</para>
/// <para>将对象序列化为Yaml</para>
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string Serialize(object obj)
{
return YamlSerializer.Serialize(obj);
}
/// <summary>
/// <para>Deserialize Yaml to the object</para>
/// <para>反序列化Yaml到对象</para>
/// </summary>
/// <param name="yaml"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T Deserialize<T>(string yaml)
{
return YamlDeserializer.Deserialize<T>(yaml);
}
}

View File

@ -1,9 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using ColdMint.scripts.debug;
using ColdMint.scripts.inventory;
using ColdMint.scripts.item;
using ColdMint.scripts.item.weapon;