From b7f72c0456d2ca77c2d1f200e668c887c67e2c1b Mon Sep 17 00:00:00 2001 From: Cold-Mint Date: Sat, 22 Jun 2024 23:29:24 +0800 Subject: [PATCH] =?UTF-8?q?Add=20item=20generator.=20=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E7=89=A9=E5=93=81=E7=94=9F=E6=88=90=E5=99=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locals/slogan.csv.import | 8 +- prefab/roomTemplates/dungeon/initialRoom.tscn | 17 +++- scripts/character/CharacterTemplate.cs | 4 +- scripts/inventory/ItemSlotNode.cs | 42 ++++++++ scripts/inventory/ItemType.cs | 11 ++- scripts/inventory/ItemTypeManager.cs | 97 +++++++++---------- scripts/inventory/ItemTypeRegister.cs | 32 +++--- scripts/loot/LootDatum.cs | 6 ++ scripts/map/ItemSpawn.cs | 44 +++++++++ scripts/utils/NodeUtils.cs | 2 + 10 files changed, 181 insertions(+), 82 deletions(-) create mode 100644 scripts/map/ItemSpawn.cs diff --git a/locals/slogan.csv.import b/locals/slogan.csv.import index a8a05f0..b4b32ef 100644 --- a/locals/slogan.csv.import +++ b/locals/slogan.csv.import @@ -2,14 +2,14 @@ importer="csv_translation" type="Translation" -uid="uid://cc0k86apkvut7" +uid="uid://dl2r1rydlm7pa" [deps] -files=["res://locals/slogan.zh.translation", "res://locals/slogan.en.translation", "res://locals/slogan.ja.translation"] +files=["res://locals/Slogan.zh.translation", "res://locals/Slogan.en.translation", "res://locals/Slogan.ja.translation"] -source_file="res://locals/slogan.csv" -dest_files=["res://locals/slogan.zh.translation", "res://locals/slogan.en.translation", "res://locals/slogan.ja.translation"] +source_file="res://locals/Slogan.csv" +dest_files=["res://locals/Slogan.zh.translation", "res://locals/Slogan.en.translation", "res://locals/Slogan.ja.translation"] [params] diff --git a/prefab/roomTemplates/dungeon/initialRoom.tscn b/prefab/roomTemplates/dungeon/initialRoom.tscn index ec59b74..1d0de93 100644 --- a/prefab/roomTemplates/dungeon/initialRoom.tscn +++ b/prefab/roomTemplates/dungeon/initialRoom.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=6 format=3 uid="uid://du5ldsp613fei"] +[gd_scene load_steps=7 format=3 uid="uid://du5ldsp613fei"] [ext_resource type="TileSet" uid="uid://c4wpp12rr44hi" path="res://tileSets/dungeon.tres" id="1_rn2om"] [ext_resource type="Script" path="res://scripts/map/PlayerSpawn.cs" id="2_6p8mv"] -[ext_resource type="PackedScene" uid="uid://dnnn2xyayiehk" path="res://prefab/weapons/staffOfTheUndead.tscn" id="3_ud0w8"] +[ext_resource type="Script" path="res://scripts/map/ItemSpawn.cs" id="3_v1tlc"] +[ext_resource type="Texture2D" uid="uid://b2blj0yf4ohx3" path="res://icon.svg" id="4_psvpu"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"] size = Vector2(450, 191) @@ -40,8 +41,14 @@ shape = SubResource("RectangleShape2D_jxmys") debug_color = Color(0, 0.6, 0.701961, 0.419608) [node name="Marker2D" type="Marker2D" parent="."] -position = Vector2(216, 113) +position = Vector2(221, 134) script = ExtResource("2_6p8mv") -[node name="StaffOfTheUndead6" parent="." instance=ExtResource("3_ud0w8")] -position = Vector2(290, 167) +[node name="ItemMarker2D" type="Marker2D" parent="."] +position = Vector2(142, 84) +script = ExtResource("3_v1tlc") +ItemId = "staff_of_the_undead" + +[node name="Icon" type="Sprite2D" parent="ItemMarker2D"] +scale = Vector2(0.3, 0.3) +texture = ExtResource("4_psvpu") diff --git a/scripts/character/CharacterTemplate.cs b/scripts/character/CharacterTemplate.cs index 42d4e35..f552894 100644 --- a/scripts/character/CharacterTemplate.cs +++ b/scripts/character/CharacterTemplate.cs @@ -492,7 +492,7 @@ public partial class CharacterTemplate : CharacterBody2D foreach (var lootDatum in lootData) { var (id, amount) = lootDatum.Value; - ItemTypeManager.CreateItems(id, amount, parentNode, position); + ItemTypeManager.CreateItems(id, amount, position, parentNode); } } @@ -644,7 +644,7 @@ public partial class CharacterTemplate : CharacterBody2D } else { - for (int i = 0; i < number && !itemSlotNode.IsEmpty(); i++) + for (var i = 0; i < number && !itemSlotNode.IsEmpty(); i++) { ThrowOneItem(itemSlotNode, velocity); } diff --git a/scripts/inventory/ItemSlotNode.cs b/scripts/inventory/ItemSlotNode.cs index a422073..7f50852 100644 --- a/scripts/inventory/ItemSlotNode.cs +++ b/scripts/inventory/ItemSlotNode.cs @@ -101,6 +101,43 @@ public partial class ItemSlotNode : MarginContainer return _item; } + /// + /// CreateItemInstance + /// 创建物品槽内的物品实例 + /// + /// + ///number + ///数量 + /// + /// + ///Newly created item + ///新创建的物品 + /// + public IItem? CreateItemInstance(int number) + { + if (_item is not Node2D node2D) + { + return null; + } + + var duplicate = node2D.Duplicate(); + if (duplicate is not IItem newItem) + { + return null; + } + + if (number > _item.Quantity) + { + //The number of item instances created exceeds the current number of items + //创建的物品实例数量,超过了当前物品的数量 + duplicate.QueueFree(); + return null; + } + + newItem.Quantity = number; + return newItem; + } + public override void _DropData(Vector2 atPosition, Variant data) { if (_iconTextureRect == null) @@ -311,6 +348,11 @@ public partial class ItemSlotNode : MarginContainer var newQuantity = item.Quantity + _item.Quantity; _item.Quantity = Math.Min(newQuantity, _item.MaxQuantity); + if (item is Node2D node2D) + { + node2D.QueueFree(); + } + UpdateQuantityLabel(); return true; } } \ No newline at end of file diff --git a/scripts/inventory/ItemType.cs b/scripts/inventory/ItemType.cs index c1581a8..d2a09cf 100644 --- a/scripts/inventory/ItemType.cs +++ b/scripts/inventory/ItemType.cs @@ -3,23 +3,30 @@ using Godot; namespace ColdMint.scripts.inventory; -public readonly struct ItemType(string id, Func newItemFunc, Texture2D? icon, int maxStackQuantity) +public readonly struct ItemType( + string id, + Func createItemFunc, + Texture2D? icon, + int maxStackQuantity) { /// /// Item id of this type /// 该类型物品的id /// public string Id { get; init; } = id; + /// /// A function returns a new item instance of this type /// 用于创建该类型的物品实例的函数 /// - public Func NewItemFunc { get; init; } = newItemFunc; + public Func CreateItemFunc { get; init; } = createItemFunc; + /// /// Default icon of items of this type /// 该类型物品的默认图标 /// public Texture2D? Icon { get; init; } = icon; + /// /// Max number in item stack of this type /// 该类型物品的最大堆叠数量 diff --git a/scripts/inventory/ItemTypeManager.cs b/scripts/inventory/ItemTypeManager.cs index 9da7404..eaf15b7 100644 --- a/scripts/inventory/ItemTypeManager.cs +++ b/scripts/inventory/ItemTypeManager.cs @@ -33,69 +33,62 @@ public static class ItemTypeManager /// Returns null when the id is not registered. /// 当物品id没有注册时返回null /// - /// - public static IItem? NewItem(string id) => - Registry.TryGetValue(id, out var itemType) ? itemType.NewItemFunc() : null; + /// + ///Item Id + ///物品Id + /// + /// + ///Default parent + ///父节点 + /// + /// + ///Enabled by default, whether to place a node into a container node that matches the type of the root node after it is instantiated. If the assignment fails by type, it is placed under the default parent node. + ///默认启用,实例化节点后,是否将其放置到与根节点类型相匹配的容器节点内。如果按照类型分配失败,则放置在默认父节点下。 + /// + /// + public static IItem? CreateItem(string id, Node? defaultParentNode = null, bool assignedByRootNodeType = true) => + Registry.TryGetValue(id, out var itemType) + ? itemType.CreateItemFunc(defaultParentNode, assignedByRootNodeType) + : null; + /// - /// Creates new instances in given amount of the item registered to the given id. - /// 创建给定数量的注册到给定 id 的物品的新实例。 + /// Create multiple new item instances for the given item Id + /// 创建多个给定物品Id的新物品实例 /// + /// + /// + /// + /// + /// /// - /// - public static IList NewItems(string id, int amount) + /// + public static IItem?[]? CreateItems(string id, int number, Vector2 globalPosition, Node? defaultParentNode = null, + bool assignedByRootNodeType = true) { - IList result = []; - for (int i = 0; i < amount; i++) + if (number <= 0) { - if (NewItem(id) is { } item) result.Add(item); + return null; } - return result; - } - - /// - /// Creates new instance of the item registered to the given id, and put it into given position in both node tree and 2D space - /// 创建以给定 id 注册的物品的新实例,并将其放到节点树和二维空间中的给定位置 - /// - /// - /// - /// - /// Position in global coordinate - /// 全局坐标中的位置 - /// - /// - public static IItem? CreateItem(string id, Node? parent = null, Vector2? position = null) - { - var item = NewItem(id); - parent?.CallDeferred(GodotStringNameUtils.AddChild, (item as Node)!); - if (item is not Node2D node) return item; - if (position is { } pos) node.GlobalPosition = pos; - return item; - } - - /// - /// Creates new instances in given amount of the item registered to the given id, and put them into given position in both node tree and 2D space - /// 创建以给定 id 注册的物品的给定数量的新实例,并将其放到节点树和二维空间中的给定位置 - /// - /// - /// - /// - /// - /// Position in global coordinate - /// 全局坐标中的位置 - /// - /// - public static IList CreateItems(string id, int amount, Node? parent = null, Vector2? position = null) - { - IList result = []; - for (int i = 0; i < amount; i++) + var items = new List(); + for (var i = 0; i < number; i++) { - if (CreateItem(id, parent, position) is { } item) - result.Add(item); + var singleItem = CreateItem(id, defaultParentNode, assignedByRootNodeType); + if (singleItem == null) + { + continue; + } + + if (singleItem is Node2D node2D) + { + node2D.GlobalPosition = globalPosition; + } + + items.Add(singleItem); } - return result; + return items.ToArray(); } /// diff --git a/scripts/inventory/ItemTypeRegister.cs b/scripts/inventory/ItemTypeRegister.cs index c0fb919..dba3027 100644 --- a/scripts/inventory/ItemTypeRegister.cs +++ b/scripts/inventory/ItemTypeRegister.cs @@ -50,7 +50,7 @@ public static class ItemTypeRegister //将文件解析为项目类型信息 //parse files to item type infos IEnumerable typeInfos = - files.SelectMany(file => ParseFile( $"{itemRegsDirPath}/{file}")).ToList(); + files.SelectMany(file => ParseFile($"{itemRegsDirPath}/{file}")).ToList(); LogCat.LogWithFormat("found_item_types", typeInfos.Count()); //遍历类型信息并注册它们。 @@ -70,8 +70,9 @@ public static class ItemTypeRegister private static IList ParseFile(string filePath) { var yamlFile = FileAccess.Open(filePath, FileAccess.ModeFlags.Read); - //阅读和反序列化 + //Read & deserialize + //阅读和反序列化 var yamlString = yamlFile.GetAsText(); var typeInfos = YamlSerialization.Deserialize>(yamlString); yamlFile.Close(); @@ -81,35 +82,32 @@ public static class ItemTypeRegister private static void RegisterTypeInfo(ItemTypeInfo typeInfo) { //Load scene and icon + //加载场景和图标 var scene = ResourceLoader.Load(typeInfo.ScenePath); var icon = ResourceLoader.Load(typeInfo.IconPath); + //Create init delegate - Func newItemFunc; - if (typeInfo.CustomArgs is null or []) + //创建初始化委托 + Action? setArgs = null; + if (typeInfo.CustomArgs != null && typeInfo.CustomArgs.Count > 0) { - newItemFunc = () => NodeUtils.InstantiatePackedScene(scene); - } - else - { - Action? setArgs = null; foreach (var arg in typeInfo.CustomArgs) { setArgs += node => node?.SetDeferred(arg.Name, arg.ParseValue()); } - - newItemFunc = () => - { - var newItem = NodeUtils.InstantiatePackedScene(scene); - setArgs?.Invoke(newItem as Node); - return newItem; - }; } + //构造项目类型,寄存器 //construct item type, register var itemType = new ItemType(typeInfo.Id, - newItemFunc, + (defaultParentNode, assignedByRootNodeType) => + { + var newItem = NodeUtils.InstantiatePackedScene(scene, defaultParentNode, assignedByRootNodeType); + setArgs?.Invoke(newItem as Node); + return newItem; + }, icon, typeInfo.MaxStackValue); var succeed = ItemTypeManager.Register(itemType); LogCat.LogWithFormat("register_item", itemType.Id, succeed); diff --git a/scripts/loot/LootDatum.cs b/scripts/loot/LootDatum.cs index 457b90d..eda0bea 100644 --- a/scripts/loot/LootDatum.cs +++ b/scripts/loot/LootDatum.cs @@ -1,5 +1,11 @@ namespace ColdMint.scripts.loot; +/// +/// LootDatum +/// 战利品数据 +/// +/// +/// public readonly record struct LootDatum(string ItemId, int Quantity) { public (string id, int quantity) Value => (ItemId, Quantity); diff --git a/scripts/map/ItemSpawn.cs b/scripts/map/ItemSpawn.cs new file mode 100644 index 0000000..2598cbc --- /dev/null +++ b/scripts/map/ItemSpawn.cs @@ -0,0 +1,44 @@ +using ColdMint.scripts.debug; +using ColdMint.scripts.inventory; +using ColdMint.scripts.map.events; +using Godot; + +namespace ColdMint.scripts.map; + +/// +/// ItemSpawn +/// 物品出生点 +/// +public partial class ItemSpawn : Marker2D +{ + [Export] public string? ItemId { get; private set; } + + public override void _Ready() + { + base._Ready(); + + EventManager.MapGenerationCompleteEvent += MapGenerationCompleteEvent; + } + + private void MapGenerationCompleteEvent(MapGenerationCompleteEvent mapGenerationCompleteEvent) + { + //After the map is generated, create the item instance. + //当地图生成完成后,创建物品实例。 + if (ItemId == null) + { + return; + } + + var item = ItemTypeManager.CreateItem(ItemId,this); + if (item is Node2D node2D) + { + node2D.GlobalPosition = GlobalPosition; + } + } + + public override void _ExitTree() + { + base._ExitTree(); + EventManager.MapGenerationCompleteEvent -= MapGenerationCompleteEvent; + } +} \ No newline at end of file diff --git a/scripts/utils/NodeUtils.cs b/scripts/utils/NodeUtils.cs index e6558e9..cd00764 100644 --- a/scripts/utils/NodeUtils.cs +++ b/scripts/utils/NodeUtils.cs @@ -250,8 +250,10 @@ public static class NodeUtils where T : class { var node = InstantiatePackedScene(packedScene, defaultParentNode, assignedByRootNodeType); + // Check the type conversion and return the result successfully // 检查类型转化,成功返回结果 if (node is T result) return result; + // If the transformation fails, release the created node //如果转型失败,释放所创建的节点 LogCat.LogWarningWithFormat("warning_node_cannot_cast_to", node, nameof(T)); node.QueueFree();