Add item generator.
加入物品生成器。
This commit is contained in:
parent
18d9ccb8ab
commit
b7f72c0456
|
@ -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]
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -101,6 +101,43 @@ public partial class ItemSlotNode : MarginContainer
|
|||
return _item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>CreateItemInstance</para>
|
||||
/// <para>创建物品槽内的物品实例</para>
|
||||
/// </summary>
|
||||
/// <param name="number">
|
||||
///<para>number</para>
|
||||
///<para>数量</para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
///<para>Newly created item</para>
|
||||
///<para>新创建的物品</para>
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -3,23 +3,30 @@ using Godot;
|
|||
|
||||
namespace ColdMint.scripts.inventory;
|
||||
|
||||
public readonly struct ItemType(string id, Func<IItem?> newItemFunc, Texture2D? icon, int maxStackQuantity)
|
||||
public readonly struct ItemType(
|
||||
string id,
|
||||
Func<Node?, bool, IItem?> createItemFunc,
|
||||
Texture2D? icon,
|
||||
int maxStackQuantity)
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Item id of this type</para>
|
||||
/// <para>该类型物品的id</para>
|
||||
/// </summary>
|
||||
public string Id { get; init; } = id;
|
||||
|
||||
/// <summary>
|
||||
/// <para>A function returns a new item instance of this type</para>
|
||||
/// <para>用于创建该类型的物品实例的函数</para>
|
||||
/// </summary>
|
||||
public Func<IItem?> NewItemFunc { get; init; } = newItemFunc;
|
||||
public Func<Node?, bool, IItem?> CreateItemFunc { get; init; } = createItemFunc;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Default icon of items of this type</para>
|
||||
/// <para>该类型物品的默认图标</para>
|
||||
/// </summary>
|
||||
public Texture2D? Icon { get; init; } = icon;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Max number in item stack of this type</para>
|
||||
/// <para>该类型物品的最大堆叠数量</para>
|
||||
|
|
|
@ -33,69 +33,62 @@ public static class ItemTypeManager
|
|||
/// <para>Returns null when the id is not registered.</para>
|
||||
/// <para>当物品id没有注册时返回null</para>
|
||||
/// </returns>
|
||||
/// <seealso cref="NewItems"/><seealso cref="CreateItem"/>
|
||||
public static IItem? NewItem(string id) =>
|
||||
Registry.TryGetValue(id, out var itemType) ? itemType.NewItemFunc() : null;
|
||||
/// <param name="id">
|
||||
///<para>Item Id</para>
|
||||
///<para>物品Id</para>
|
||||
/// </param>
|
||||
/// <param name="defaultParentNode">
|
||||
///<para>Default parent</para>
|
||||
///<para>父节点</para>
|
||||
/// </param>
|
||||
/// <param name="assignedByRootNodeType">
|
||||
///<para>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.</para>
|
||||
///<para>默认启用,实例化节点后,是否将其放置到与根节点类型相匹配的容器节点内。如果按照类型分配失败,则放置在默认父节点下。</para>
|
||||
/// </param>
|
||||
/// <seealso cref="CreateItems"/>
|
||||
public static IItem? CreateItem(string id, Node? defaultParentNode = null, bool assignedByRootNodeType = true) =>
|
||||
Registry.TryGetValue(id, out var itemType)
|
||||
? itemType.CreateItemFunc(defaultParentNode, assignedByRootNodeType)
|
||||
: null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates new instances in given amount of the item registered to the given id.</para>
|
||||
/// <para>创建给定数量的注册到给定 id 的物品的新实例。</para>
|
||||
/// <para>Create multiple new item instances for the given item Id</para>
|
||||
/// <para>创建多个给定物品Id的新物品实例</para>
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="number"></param>
|
||||
/// <param name="defaultParentNode"></param>
|
||||
/// <param name="assignedByRootNodeType"></param>
|
||||
/// <param name="globalPosition"></param>
|
||||
/// <returns></returns>
|
||||
/// <seealso cref="NewItem"/><seealso cref="CreateItems"/>
|
||||
public static IList<IItem> NewItems(string id, int amount)
|
||||
/// <seealso cref="CreateItem"/>
|
||||
public static IItem?[]? CreateItems(string id, int number, Vector2 globalPosition, Node? defaultParentNode = null,
|
||||
bool assignedByRootNodeType = true)
|
||||
{
|
||||
IList<IItem> result = [];
|
||||
for (int i = 0; i < amount; i++)
|
||||
if (number <= 0)
|
||||
{
|
||||
if (NewItem(id) is { } item) result.Add(item);
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Creates new instance of the item registered to the given id, and put it into given position in both node tree and 2D space</para>
|
||||
/// <para>创建以给定 id 注册的物品的新实例,并将其放到节点树和二维空间中的给定位置</para>
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="position">
|
||||
/// <para>Position in global coordinate</para>
|
||||
/// <para>全局坐标中的位置</para>
|
||||
/// </param>
|
||||
/// <seealso cref="NewItem"/><seealso cref="CreateItems"/>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>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</para>
|
||||
/// <para>创建以给定 id 注册的物品的给定数量的新实例,并将其放到节点树和二维空间中的给定位置</para>
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="amount"></param>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="position">
|
||||
/// <para>Position in global coordinate</para>
|
||||
/// <para>全局坐标中的位置</para>
|
||||
/// </param>
|
||||
/// <seealso cref="NewItems"/><seealso cref="CreateItem"/>
|
||||
public static IList<IItem> CreateItems(string id, int amount, Node? parent = null, Vector2? position = null)
|
||||
{
|
||||
IList<IItem> result = [];
|
||||
for (int i = 0; i < amount; i++)
|
||||
var items = new List<IItem?>();
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -50,7 +50,7 @@ public static class ItemTypeRegister
|
|||
//将文件解析为项目类型信息
|
||||
//parse files to item type infos
|
||||
IEnumerable<ItemTypeInfo> 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<ItemTypeInfo> ParseFile(string filePath)
|
||||
{
|
||||
var yamlFile = FileAccess.Open(filePath, FileAccess.ModeFlags.Read);
|
||||
//阅读和反序列化
|
||||
|
||||
//Read & deserialize
|
||||
//阅读和反序列化
|
||||
var yamlString = yamlFile.GetAsText();
|
||||
var typeInfos = YamlSerialization.Deserialize<IList<ItemTypeInfo>>(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<PackedScene>(typeInfo.ScenePath);
|
||||
var icon = ResourceLoader.Load<Texture2D>(typeInfo.IconPath);
|
||||
|
||||
|
||||
//Create init delegate
|
||||
Func<IItem?> newItemFunc;
|
||||
if (typeInfo.CustomArgs is null or [])
|
||||
//创建初始化委托
|
||||
Action<Node?>? setArgs = null;
|
||||
if (typeInfo.CustomArgs != null && typeInfo.CustomArgs.Count > 0)
|
||||
{
|
||||
newItemFunc = () => NodeUtils.InstantiatePackedScene<IItem>(scene);
|
||||
}
|
||||
else
|
||||
{
|
||||
Action<Node?>? setArgs = null;
|
||||
foreach (var arg in typeInfo.CustomArgs)
|
||||
{
|
||||
setArgs +=
|
||||
node => node?.SetDeferred(arg.Name, arg.ParseValue());
|
||||
}
|
||||
|
||||
newItemFunc = () =>
|
||||
{
|
||||
var newItem = NodeUtils.InstantiatePackedScene<IItem>(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<IItem>(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);
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
namespace ColdMint.scripts.loot;
|
||||
|
||||
/// <summary>
|
||||
/// <para>LootDatum</para>
|
||||
/// <para>战利品数据</para>
|
||||
/// </summary>
|
||||
/// <param name="ItemId"></param>
|
||||
/// <param name="Quantity"></param>
|
||||
public readonly record struct LootDatum(string ItemId, int Quantity)
|
||||
{
|
||||
public (string id, int quantity) Value => (ItemId, Quantity);
|
||||
|
|
44
scripts/map/ItemSpawn.cs
Normal file
44
scripts/map/ItemSpawn.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.inventory;
|
||||
using ColdMint.scripts.map.events;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.map;
|
||||
|
||||
/// <summary>
|
||||
/// <para>ItemSpawn</para>
|
||||
/// <para>物品出生点</para>
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue
Block a user