diff --git a/.gitignore b/.gitignore
index 267c058..1ebcbdd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,3 @@ export_presets.cfg
.vs/
*.translation
*.user
-*.DotSettings
diff --git a/ColdMint.Traveler.sln.DotSettings b/ColdMint.Traveler.sln.DotSettings
new file mode 100644
index 0000000..1f777d6
--- /dev/null
+++ b/ColdMint.Traveler.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/data/itemRegs/weapons.yaml b/data/itemRegs/weapons.yaml
index a6a62f4..3700f97 100644
--- a/data/itemRegs/weapons.yaml
+++ b/data/itemRegs/weapons.yaml
@@ -2,3 +2,15 @@
scene_path: res://prefab/weapons/staffOfTheUndead.tscn
icon_path: res://sprites/weapon/staffOfTheUndead_icon.png
max_stack_value: 1
+
+- id: degraded_staff_of_the_undead
+ scene_path: res://prefab/weapons/staffOfTheUndead.tscn
+ icon_path: res://sprites/weapon/staffOfTheUndead_icon.png
+ max_stack_value: 1
+ custom_args:
+ - name: FiringIntervalAsMillisecond
+ type: int
+ value: 1000
+ - name: UniqueName
+ type: string
+ value: 劣化的死灵法杖
diff --git a/locals/Log.csv b/locals/Log.csv
index 63924a5..60dffeb 100644
--- a/locals/Log.csv
+++ b/locals/Log.csv
@@ -23,10 +23,17 @@ log_player_packed_scene_not_exist,玩家预制场景不存在。,Player packed s
log_exit_the_room_debug,节点{0}退出房间{1}。,"Node {0} exits room {1}.",ノード{0}が部屋{1}を退出します。
log_enter_the_room_debug,节点{0}进入房间{1}。,"Node {0} enters room {1}.",ノード{0}が部屋{1}に入ります。
log_death_info,生物{0}被{1}击败。,"Creature {0} was defeated by {1}.",生物{0}が{1}によって打ち負かされました。
+
log_loot_list_has_no_entries,ID为{0}的战利品表,没有指定条目。,"Loot list with ID {0}, no entry specified.",ID{0}の戦利品テーブルは、エントリ指定されていません。
log_not_within_the_loot_spawn_range,给定的数值{0}没有在战利品{1}的生成范围{2}内。,The given value {0} is not within the spawn range {2} of loot {1}.,与えられた数値{0}は戦利品{1}の生成範囲{2}内にありません。
log_loot_data_quantity,有{0}个战利品数据被返回。,{0} loot data was returned.,{0}個の戦利品データが返されます。
+log_loot_data_add,生成战利品{0},Add loot {0},戦利品{0}を生成する
+
+log_warning_node_cannot_cast_to,创建的物品{0}无法被转型为类型{1},Created items {0} cannot be cast into type {1},作成されたアイテム {0} をタイプ {1} にキャストすることはできません。
+
log_start_item_register_from_file,开始从文件注册物品信息,Start registering item information from files,アイテム情報をファイルから登録開始
-log_item_register_from_file,从文件{0}中注册物品信息,Registering item information from file {0},ファイル{0}からアイテム情報を登録する
-log_item_register_find_item_in_file,注册发现的物品{0},Register discovered item {0},見つかったアイテム{0}を登録
-log_error_when_open_item_regs_dir,尝试打开物品信息目录时发生错误,Error when opening itemRegs dir,アイテム情報カタログを開こうとしてエラーが発生しました。
\ No newline at end of file
+log_found_files,找到{0}个文件,Found {0} files,{0}ファイルが見つかりました
+log_found_item_types,从文件中找到{0}个物品类型,Found {0} item types in files,ファイルから{0}個のアイテム・タイプが見つかった
+log_register_item,注册物品类型{0}结果为{1},Registered item type {0}; results in {1},登録されたアイテム・タイプ {0} の結果は {1} です。
+log_error_when_open_item_regs_dir,尝试打开物品信息目录时发生错误,Error when opening itemRegs dir,アイテム情報カタログを開こうとしてエラーが発生しました。
+log_wrong_custom_arg,不匹配的参数:类型为{0}而值为{1},Mismatched parameter: type {0} and value {1},パラメータの不一致:型{0}と値{1}。
\ No newline at end of file
diff --git a/prefab/entitys/DelivererOfDarkMagic.tscn b/prefab/entitys/DelivererOfDarkMagic.tscn
index f665553..a7535a0 100644
--- a/prefab/entitys/DelivererOfDarkMagic.tscn
+++ b/prefab/entitys/DelivererOfDarkMagic.tscn
@@ -31,10 +31,10 @@ radius = 129.027
collision_layer = 64
collision_mask = 38
script = ExtResource("1_ubaid")
+LootListId = "test"
metadata/CampId = "Mazoku"
metadata/MaxHp = 50
metadata/Name = "死灵法师"
-metadata/LootListId = "Test"
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(0, 4)
diff --git a/prefab/weapons/staffOfTheUndead.tscn b/prefab/weapons/staffOfTheUndead.tscn
index 63d73e1..01d4dc3 100644
--- a/prefab/weapons/staffOfTheUndead.tscn
+++ b/prefab/weapons/staffOfTheUndead.tscn
@@ -15,12 +15,8 @@ collision_layer = 8
collision_mask = 34
script = ExtResource("1_w8hhv")
ProjectileScenes = [ExtResource("2_34250")]
-_firingIntervalAsMillisecond = null
-_recoil = null
+FiringIntervalAsMillisecond = 300
Id = "staff_of_the_undead"
-_minContactInjury = null
-_maxContactInjury = null
-metadata/Projectiles = PackedStringArray("res://prefab/projectile/curseOfTheUndead.tscn")
[node name="DamageArea2D" type="Area2D" parent="."]
collision_layer = 8
diff --git a/scenes/LevelGraphEditor.tscn b/scenes/LevelGraphEditor.tscn
index 101d53e..9e9f07d 100644
--- a/scenes/LevelGraphEditor.tscn
+++ b/scenes/LevelGraphEditor.tscn
@@ -53,7 +53,7 @@ offset_left = 10.0
offset_top = 13.0
offset_right = 97.0
offset_bottom = 38.0
-text = "create_room"
+text = "ui_create_room"
[node name="Label3" type="Label" parent="CreateOrEditorPanel"]
layout_mode = 1
@@ -61,7 +61,7 @@ offset_left = 12.0
offset_top = 106.0
offset_right = 72.0
offset_bottom = 131.0
-text = "describe"
+text = "ui_describe"
[node name="Label4" type="Label" parent="CreateOrEditorPanel"]
layout_mode = 1
@@ -69,7 +69,7 @@ offset_left = 15.0
offset_top = 152.0
offset_right = 75.0
offset_bottom = 177.0
-text = "room_template_collection_prompt"
+text = "ui_room_template_collection_prompt"
[node name="RoomTemplateTipsLabel" type="Label" parent="CreateOrEditorPanel"]
layout_mode = 1
@@ -109,7 +109,7 @@ offset_top = 6.0
offset_right = -11.0
offset_bottom = 39.0
grow_horizontal = 0
-text = "close"
+text = "ui_close"
[node name="Label2" type="Label" parent="CreateOrEditorPanel"]
layout_mode = 1
@@ -117,7 +117,7 @@ offset_left = 13.0
offset_top = 60.0
offset_right = 48.0
offset_bottom = 85.0
-text = "name"
+text = "ui_name"
[node name="CreateRoomButton" type="Button" parent="CreateOrEditorPanel"]
layout_mode = 1
@@ -132,7 +132,7 @@ offset_right = -13.0
offset_bottom = -13.0
grow_horizontal = 0
grow_vertical = 0
-text = "creation"
+text = "ui_creation"
[node name="RoomTemplateCollectionTextEdit" type="TextEdit" parent="CreateOrEditorPanel"]
layout_mode = 1
@@ -150,7 +150,7 @@ offset_left = 16.0
offset_top = 289.0
offset_right = 56.0
offset_bottom = 314.0
-text = "tags"
+text = "ui_tags"
[node name="TagLineEdit" type="LineEdit" parent="CreateOrEditorPanel"]
layout_mode = 1
@@ -168,7 +168,7 @@ offset_left = 14.0
offset_top = 368.0
offset_right = 283.0
offset_bottom = 393.0
-text = "room_injection_processor"
+text = "ui_room_injection_processor"
[node name="RoomInjectionProcessorDataTextEdit" type="TextEdit" parent="CreateOrEditorPanel"]
layout_mode = 1
diff --git a/scripts/Config.cs b/scripts/Config.cs
index e159156..98e4d42 100644
--- a/scripts/Config.cs
+++ b/scripts/Config.cs
@@ -31,7 +31,7 @@ public static class Config
/// A trophy table for testing
/// 测试用的战利品表
///
- public const string Test = "Test";
+ public const string Test = "test";
}
///
diff --git a/scripts/character/CharacterTemplate.cs b/scripts/character/CharacterTemplate.cs
index 1391747..0c7c82f 100644
--- a/scripts/character/CharacterTemplate.cs
+++ b/scripts/character/CharacterTemplate.cs
@@ -10,7 +10,9 @@ using ColdMint.scripts.inventory;
using ColdMint.scripts.item;
using ColdMint.scripts.utils;
using ColdMint.scripts.item.weapon;
+using ColdMint.scripts.loot;
using ColdMint.scripts.pickable;
+
using Godot;
namespace ColdMint.scripts.character;
@@ -107,11 +109,7 @@ public partial class CharacterTemplate : CharacterBody2D
private DamageNumberNodeSpawn? _damageNumber;
- ///
- /// Character referenced loot table
- /// 角色引用的战利品表
- ///
- private LootList? _lootList;
+ [Export] public string LootListId { get; private set; } = "";
private HealthBar? _healthBar;
private DateTime _lastDamageTime;
@@ -208,12 +206,6 @@ public partial class CharacterTemplate : CharacterBody2D
CampId = GetMeta("CampId", Config.CampId.Default).AsString();
MaxHp = GetMeta("MaxHp", Config.DefaultMaxHp).AsInt32();
var lootListId = GetMeta("LootListId", string.Empty).AsString();
- if (!string.IsNullOrEmpty(lootListId))
- {
- //If a loot table is specified, get the loot table.
- //如果指定了战利品表,那么获取战利品表。
- _lootList = LootListManager.GetLootList(lootListId);
- }
if (MaxHp <= 0)
{
@@ -461,20 +453,9 @@ public partial class CharacterTemplate : CharacterBody2D
///
protected void CreateLootObject()
{
- if (_lootList == null)
- {
- return;
- }
-
- var lootDataArray = _lootList.GenerateLootData();
- if (lootDataArray.Length == 0)
- {
- return;
- }
-
-
+ var lootData = LootListManager.GenerateLootData(LootListId);
var finalGlobalPosition = GlobalPosition;
- CallDeferred("GenerateLootObjects", this, lootDataArray, finalGlobalPosition);
+ GenerateLootObjects(GetParent(), lootData, finalGlobalPosition);
}
///
@@ -482,13 +463,17 @@ public partial class CharacterTemplate : CharacterBody2D
/// 生成战利品对象
///
///
- ///
+ ///
///
public void GenerateLootObjects(Node parentNode,
- LootData[] lootDataArray,
+ IEnumerable lootData,
Vector2 position)
{
- LootListManager.GenerateLootObjects(parentNode, lootDataArray, position);
+ foreach (var lootDatum in lootData)
+ {
+ var (id, amount) = lootDatum.Value;
+ ItemTypeManager.CreateItems(id, amount, parentNode, position);
+ }
}
///
@@ -601,14 +586,8 @@ public partial class CharacterTemplate : CharacterBody2D
//Generates a random number that controls the horizontal velocity of thrown items (range: 0.01 to 1)
//生成一个随机数,用于控制抛出物品的水平方向速度(范围为:0.01到1)
var percent = GD.Randf() + 0.01f;
- if (GD.Randf() > 0.5)
- {
- ThrowItem(i, -1, new Vector2(horizontalDirection * percent, height));
- }
- else
- {
- ThrowItem(i, -1, new Vector2(-horizontalDirection * percent, height));
- }
+ var horizontalVelocity = horizontalDirection * percent * GD.Randf() > 0.5 ? 1f : -1f;
+ ThrowItem(i, -1, new Vector2(horizontalVelocity, height));
}
}
@@ -671,7 +650,7 @@ public partial class CharacterTemplate : CharacterBody2D
return;
}
- CallDeferred("NodeReparent", node2D);
+ CallDeferred(nameof(NodeReparent), node2D);
switch (item)
{
case PickAbleTemplate pickAbleTemplate:
diff --git a/scripts/damage/DamageNumberNodeSpawn.cs b/scripts/damage/DamageNumberNodeSpawn.cs
index 3e79491..0bc00fc 100644
--- a/scripts/damage/DamageNumberNodeSpawn.cs
+++ b/scripts/damage/DamageNumberNodeSpawn.cs
@@ -106,7 +106,7 @@ public partial class DamageNumberNodeSpawn : Marker2D
return;
}
- CallDeferred("AddDamageNumberNode", damageNumber);
+ CallDeferred(nameof(AddDamageNumberNode), damageNumber);
damageNumber.Position = GlobalPosition;
if (damageTemplate.MoveLeft)
{
diff --git a/scripts/inventory/LootData.cs b/scripts/inventory/LootData.cs
deleted file mode 100644
index 0a6d863..0000000
--- a/scripts/inventory/LootData.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using Godot;
-
-namespace ColdMint.scripts.inventory;
-
-public partial class LootData : GodotObject
-{
- public string? ResPath { get; set; }
- public int Quantity { get; set; }
-}
\ No newline at end of file
diff --git a/scripts/inventory/LootEntry.cs b/scripts/inventory/LootEntry.cs
deleted file mode 100644
index 0c4b2fa..0000000
--- a/scripts/inventory/LootEntry.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace ColdMint.scripts.inventory;
-
-///
-/// Loot entry
-/// 战利品条目
-///
-public class LootEntry
-{
- ///
- /// generation probability
- /// 生成概率
- ///
- public double? Chance { get; set; }
-
- ///
- /// Minimum number of generated
- /// 最小生成多少个
- ///
- public int MinQuantity { get; set; }
-
- ///
- /// The maximum number of files to be generated
- /// 最多生成多少个
- ///
- public int MaxQuantity { get; set; }
-
- ///
- /// resources path
- /// 资源路径
- ///
- public string? ResPath { get; set; }
-}
\ No newline at end of file
diff --git a/scripts/inventory/LootList.cs b/scripts/inventory/LootList.cs
deleted file mode 100644
index fe4a924..0000000
--- a/scripts/inventory/LootList.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System.Collections.Generic;
-using ColdMint.scripts.debug;
-using Godot;
-
-namespace ColdMint.scripts.inventory;
-
-///
-/// Loot list
-/// 战利品表
-///
-public class LootList
-{
- ///
- /// Id
- /// 战利品表的Id
- ///
- public string? Id { get; set; }
-
- private List? _lootEntrieList;
-
- ///
- /// Add loot entry
- /// 添加战利品条目
- ///
- ///
- public void AddLootEntry(LootEntry lootEntry)
- {
- if (_lootEntrieList == null)
- {
- _lootEntrieList = new List();
- }
-
- _lootEntrieList.Add(lootEntry);
- }
-
-
- ///
- /// GenerateLootData
- /// 生成战利品数据
- ///
- ///
- public LootData[] GenerateLootData()
- {
- var lootDataList = new List();
- if (_lootEntrieList == null)
- {
- LogCat.LogWithFormat("loot_list_has_no_entries", Id);
- return lootDataList.ToArray();
- }
-
- foreach (var lootEntry in _lootEntrieList)
- {
- var chance = GD.Randf();
- if (chance > lootEntry.Chance)
- {
- //If the random number is greater than the generation probability, skip the current loop.
- //如果随机数大于生成概率,则跳过当前循环。
- LogCat.LogWithFormat("not_within_the_loot_spawn_range", chance, lootEntry.ResPath, lootEntry.Chance);
- continue;
- }
-
- //We generate a loot data for each loot entry.
- //我们为每个战利品条目生成一个战利品数据。
- var quantity = GD.RandRange(lootEntry.MinQuantity, lootEntry.MaxQuantity);
- var lootData = new LootData
- {
- ResPath = lootEntry.ResPath,
- Quantity = quantity
- };
- lootDataList.Add(lootData);
- }
-
- LogCat.LogWithFormat("loot_data_quantity", lootDataList.Count);
- return lootDataList.ToArray();
- }
-
-
- ///
- /// Remove loot entry
- /// 移除战利品条目
- ///
- ///
- ///
- public bool RemoveLootEntry(LootEntry lootEntry)
- {
- if (_lootEntrieList == null)
- {
- return false;
- }
-
- return _lootEntrieList.Remove(lootEntry);
- }
-}
\ No newline at end of file
diff --git a/scripts/inventory/LootListManager.cs b/scripts/inventory/LootListManager.cs
deleted file mode 100644
index 0b60dcf..0000000
--- a/scripts/inventory/LootListManager.cs
+++ /dev/null
@@ -1,117 +0,0 @@
-using System.Collections.Generic;
-using ColdMint.scripts.utils;
-using Godot;
-
-namespace ColdMint.scripts.inventory;
-
-///
-/// LootListManager
-/// 战利品表管理器
-///
-public static class LootListManager
-{
- private static Dictionary? _lootListDictionary;
-
-
- ///
- /// Register loot table
- /// 注册战利品表
- ///
- ///
- public static bool RegisterLootList(LootList lootList)
- {
- var id = lootList.Id;
- if (id == null)
- {
- return false;
- }
-
- if (_lootListDictionary != null) return _lootListDictionary.TryAdd(id, lootList);
- _lootListDictionary = new Dictionary { { id, lootList } };
- return true;
- }
-
- ///
- /// Get Loot List
- /// 获取战利品表
- ///
- ///
- ///
- public static LootList? GetLootList(string id)
- {
- return _lootListDictionary?.GetValueOrDefault(id);
- }
-
- ///
- /// Generate loot objects
- /// 生成战利品对象
- ///
- public static void GenerateLootObjects(Node parentNode, LootData[] lootDataArray, Vector2 position)
- {
- if (lootDataArray.Length == 0)
- {
- return;
- }
-
- //Cache the loaded PackedScene object.
- //缓存已加载的PackedScene对象。
- Dictionary packedSceneDictionary = new();
- foreach (var lootData in lootDataArray)
- {
- if (string.IsNullOrEmpty(lootData.ResPath) || lootData.Quantity == 0)
- {
- continue;
- }
-
- if (!packedSceneDictionary.TryGetValue(lootData.ResPath, out var packedScene))
- {
- packedScene = GD.Load(lootData.ResPath);
- packedSceneDictionary.TryAdd(lootData.ResPath, packedScene);
- }
-
- for (var i = 0; i < lootData.Quantity; i++)
- {
- //Generate as many loot instance objects as there are loot.
- //有多少个战利品就生成多少个战利品实例对象。
- CreateLootInstanceObject(parentNode, packedScene, position);
- }
- }
- }
-
-
- ///
- /// Create a loot instance object
- /// 创建战利品实例对象
- ///
- private static void CreateLootInstanceObject(Node parent, PackedScene? packedScene, Vector2 position)
- {
- if (packedScene == null)
- {
- return;
- }
-
- var lootObject = NodeUtils.InstantiatePackedScene(packedScene, parent);
- if (lootObject == null)
- {
- return;
- }
-
- lootObject.Position = position;
- }
-
- ///
- /// Remove loot list
- /// 移除战利品表
- ///
- ///
- ///
- public static bool UnregisterLootList(string id)
- {
- if (_lootListDictionary == null)
- {
- return false;
- }
-
- return _lootListDictionary.Remove(id);
- }
-}
\ No newline at end of file
diff --git a/scripts/inventory/UniversalItemContainer.cs b/scripts/inventory/UniversalItemContainer.cs
index a863001..87b1d64 100644
--- a/scripts/inventory/UniversalItemContainer.cs
+++ b/scripts/inventory/UniversalItemContainer.cs
@@ -67,16 +67,16 @@ public class UniversalItemContainer : IItemContainer
public bool AddItemStack(IItemStack itemStack)
{
- while (true)
+ ItemSlotNode? itemSlotNode = Match(itemStack);
+ while (itemSlotNode is not null)
{
- var itemSlotNode = Match(itemStack);
-
- if (itemSlotNode == null)
- return false;
-
if (itemSlotNode.AddItemStack(itemStack))
return true;
+
+ itemSlotNode = Match(itemStack);
}
+
+ return false;
}
public int GetSelectIndex()
@@ -235,7 +235,7 @@ public class UniversalItemContainer : IItemContainer
return;
}
- itemSlotNode.IsSelect = (_itemSlotNodes.Count ) == _selectIndex;
+ itemSlotNode.IsSelect = (_itemSlotNodes.Count) == _selectIndex;
_itemSlotNodes.Add(itemSlotNode);
}
diff --git a/scripts/item/ICommonItem.cs b/scripts/item/ICommonItem.cs
index b223a92..a41a60e 100644
--- a/scripts/item/ICommonItem.cs
+++ b/scripts/item/ICommonItem.cs
@@ -16,9 +16,9 @@ namespace ColdMint.scripts.item;
public interface ICommonItem : IItem
{
///
- /// Method to copy an instance same with self. Will be used to pick out item instance from a
+ /// Method to clone an instance same with self. Will be used to pick out item instance from a
/// 复制与自身相同的实例的方法。将用于从 中拿取新的物品实例。
///
///
- ICommonItem CopyInstance();
+ ICommonItem CloneInstance();
}
\ No newline at end of file
diff --git a/scripts/item/IItem.cs b/scripts/item/IItem.cs
index d155aa0..6ed7bbb 100644
--- a/scripts/item/IItem.cs
+++ b/scripts/item/IItem.cs
@@ -26,6 +26,14 @@ public interface IItem
/// 当前项目的描述
///
string? Description { get; }
+ ///
+ ///
+ /// Whether the current item can be put into item containers like packsack.
+ /// This attribute is usually set to false for items of the backpack class to avoid pack nesting.
+ ///
+ /// 当前物品是否可以放入背包类的容器中。一般将背包类物品的该属性设为false来避免背包嵌套。
+ ///
+ bool CanPutInPack { get; }
///
/// Execute when current item is used
e.g. when player clicks left mouse button with current item in hand
diff --git a/scripts/item/ItemTypeManager.cs b/scripts/item/ItemTypeManager.cs
index 964e3ca..1e29cca 100644
--- a/scripts/item/ItemTypeManager.cs
+++ b/scripts/item/ItemTypeManager.cs
@@ -1,13 +1,9 @@
using System.Collections.Generic;
-using ColdMint.scripts.debug;
using ColdMint.scripts.utils;
using Godot;
-using YamlDotNet.Serialization;
-using YamlDotNet.Serialization.NamingConventions;
-
namespace ColdMint.scripts.item;
///
@@ -16,68 +12,6 @@ namespace ColdMint.scripts.item;
///
public static class ItemTypeManager
{
- //用于yaml反序列化
- //Use for yaml deserialization
- private record struct ItemTypeInfo(string Id, string ScenePath, string IconPath, int MaxStackValue) { }
-
- ///
- /// Register items from yaml file
- /// 从文件注册物品
- ///
- public static void RegisterFromFile()
- {
- LogCat.Log("start_item_register_from_file");
-
- // 初始化yaml反序列化器
- // initialize yaml deserializer
- var deserializer = new DeserializerBuilder()
- .WithNamingConvention(UnderscoredNamingConvention.Instance) // convent snake_case
- .Build();
-
- //初始化文件目录
- //initialize file dir
- 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");
- }
-
- //遍历目录,找到要注册的文件
- //traverse the dir, find files to register
- foreach (var file in itemRegsDir.GetFiles())
- {
- if (file is null) continue;
- LogCat.LogWithFormat("item_register_from_file", file);
-
- //读取文件,解析为类型为info的IEnumerable
- //read file, parse to an IEnumerable of type infos
- var yamlFile = FileAccess.Open($"{itemRegsDirPath}/{file}", FileAccess.ModeFlags.Read);
- var yamlString = yamlFile.GetAsText();
- var typeInfos = deserializer.Deserialize>(yamlString);
- yamlFile.Close();
-
- //遍历类型信息并注册它们。
- //traverse type infos and register them.
- foreach (var typeInfo in typeInfos)
- {
- LogCat.LogWithFormat("item_register_find_item_in_file", typeInfo.Id);
- var scene = ResourceLoader.Load(typeInfo.ScenePath);
- var icon = ResourceLoader.Load(typeInfo.IconPath);
- var itemType = new ItemType(typeInfo.Id,
- () => NodeUtils.InstantiatePackedScene(scene),
- icon, typeInfo.MaxStackValue);
- Register(itemType);
- }
- }
- }
-
- ///
- /// Register items here
- /// 在这里注册物品
- ///
- public static void StaticRegister() { }
-
private static Dictionary Registry { get; } = [];
private static Texture2D DefaultTexture { get; } = new PlaceholderTexture2D();
@@ -101,9 +35,71 @@ 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;
+ ///
+ /// Creates new instances in given amount of the item registered to the given id.
+ /// 创建给定数量的注册到给定 id 的物品的新实例。
+ ///
+ ///
+ ///
+ public static IList NewItems(string id, int amount)
+ {
+ IList result = [];
+ for (int i = 0; i < amount; i++)
+ {
+ if (NewItem(id) is { } item) result.Add(item);
+ }
+
+ 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++)
+ {
+ if (CreateItem(id, parent, position) is { } item)
+ result.Add(item);
+ }
+
+ return result;
+ }
+
///
/// Get the translated default name of the item type for the given id
/// 获取指定物品id翻译后的物品名
diff --git a/scripts/item/ItemTypeRegister.cs b/scripts/item/ItemTypeRegister.cs
new file mode 100644
index 0000000..dcad2c2
--- /dev/null
+++ b/scripts/item/ItemTypeRegister.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using ColdMint.scripts.debug;
+using ColdMint.scripts.utils;
+
+using Godot;
+
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
+
+namespace ColdMint.scripts.item;
+
+///
+/// 负责从文件注册物品
+///
+public static class ItemTypeRegister
+{
+ ///
+ /// Register items here
+ /// 在这里注册物品
+ ///
+ public static void StaticRegister() { }
+
+ ///
+ /// Register items from yaml file
+ /// 从文件注册物品
+ ///
+ 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 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();
+ LogCat.LogWithFormat("found_files", files.Length);
+
+ // parse files to item type infos
+ IEnumerable typeInfos =
+ files.SelectMany(file => ParseFile(deserializer, $"{itemRegsDirPath}/{file}")).ToList();
+ LogCat.LogWithFormat("found_item_types", typeInfos.Count());
+
+ // traverse type infos and register them.
+ foreach (var typeInfo in typeInfos)
+ {
+ RegisterTypeInfo(typeInfo);
+ }
+ }
+
+ private static IList ParseFile(IDeserializer deserializer, string filePath)
+ {
+ var yamlFile = FileAccess.Open(filePath, FileAccess.ModeFlags.Read);
+ //Read & deserialize
+ var yamlString = yamlFile.GetAsText();
+ var typeInfos = deserializer.Deserialize>(yamlString);
+
+ yamlFile.Close();
+ return typeInfos;
+ }
+
+ 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 [])
+ {
+ 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,
+ icon, typeInfo.MaxStackValue);
+ var succeed = ItemTypeManager.Register(itemType);
+ LogCat.LogWithFormat("register_item", itemType.Id, succeed);
+ }
+
+ //Use for yaml deserialization
+ private record struct ItemTypeInfo(
+ string Id, string ScenePath, string IconPath, int MaxStackValue,
+ IList? CustomArgs) { }
+
+ private readonly record struct CustomArg(string Name, CustomArgType Type, string Value)
+ {
+ public Variant ParseValue() =>
+ Type switch
+ {
+ CustomArgType.String => Value,
+ CustomArgType.Int => int.Parse(Value),
+ CustomArgType.Float => double.Parse(Value),
+ CustomArgType.Vector2 => ParseVector2FromString(Value),
+ CustomArgType.Texture => ResourceLoader.Load("res://sprites/" + Value),
+ _ => throw new ArgumentOutOfRangeException($"Unknown Arg Type {Type}")
+ };
+
+ private Vector2 ParseVector2FromString(string s)
+ {
+ var ss = s.Split(',');
+ if (ss.Length != 2)
+ {
+ LogCat.LogErrorWithFormat("wrong_custom_arg", "Vector2", s);
+ return Vector2.Zero;
+ }
+
+ return new Vector2(float.Parse(ss[0]), float.Parse(ss[1]));
+ }
+ }
+
+ private enum CustomArgType
+ {
+ String,
+ Int,
+ Float,
+ Vector2,
+ Texture,
+ }
+}
\ No newline at end of file
diff --git a/scripts/item/Packsack.cs b/scripts/item/Packsack.cs
index fd8581e..6c334e9 100644
--- a/scripts/item/Packsack.cs
+++ b/scripts/item/Packsack.cs
@@ -1,6 +1,7 @@
using ColdMint.scripts.inventory;
using ColdMint.scripts.pickable;
using ColdMint.scripts.utils;
+
using Godot;
using PacksackUi = ColdMint.scripts.loader.uiLoader.PacksackUi;
@@ -15,6 +16,8 @@ public partial class Packsack : PickAbleTemplate
private PackedScene? _packedScene;
private PacksackUi? _packsackUi;
+ public override bool CanPutInPack => false;
+
public override void Destroy()
{
if (ItemContainer == null) return;
@@ -32,6 +35,7 @@ public partial class Packsack : PickAbleTemplate
{
return;
}
+
if (_packsackUi == null)
{
_packsackUi = NodeUtils.InstantiatePackedScene(_packedScene,this);
@@ -41,6 +45,7 @@ public partial class Packsack : PickAbleTemplate
_packsackUi.ItemContainer = ItemContainer;
}
}
+
_packsackUi?.Show();
}
@@ -51,6 +56,5 @@ public partial class Packsack : PickAbleTemplate
base._Ready();
ItemContainer = new UniversalItemContainer();
_packedScene = GD.Load("res://prefab/ui/packsackUI.tscn");
-
}
}
\ No newline at end of file
diff --git a/scripts/item/itemStacks/CommonItemStack.cs b/scripts/item/itemStacks/CommonItemStack.cs
index 1887718..0018e04 100644
--- a/scripts/item/itemStacks/CommonItemStack.cs
+++ b/scripts/item/itemStacks/CommonItemStack.cs
@@ -67,7 +67,7 @@ public class CommonItemStack(ICommonItem innerItem) : IItemStack
{
if(Empty) return null;
Quantity--;
- var result = innerItem.CopyInstance();
+ var result = innerItem.CloneInstance();
if(Empty) innerItem.Destroy();
return result;
}
@@ -75,7 +75,7 @@ public class CommonItemStack(ICommonItem innerItem) : IItemStack
public IItemStack? PickItems(int value)
{
if (Empty) return null;
- var result = new CommonItemStack(innerItem.CopyInstance());
+ var result = new CommonItemStack(innerItem.CloneInstance());
var n = Math.Min(Quantity, value);
if (n < 0)
{
diff --git a/scripts/item/itemStacks/PacksackStack.cs b/scripts/item/itemStacks/PacksackStack.cs
index a70a22e..3e96266 100644
--- a/scripts/item/itemStacks/PacksackStack.cs
+++ b/scripts/item/itemStacks/PacksackStack.cs
@@ -17,17 +17,16 @@ public class PacksackStack(Packsack packsack) : IItemStack
public Texture2D Icon => packsack.Icon;
public string Name => packsack.Name;
public string? Description => packsack.Description;
-
- //todo: 只拒绝是背包的物品是权宜之计,应该为物品加入一个“是否可以放入背包”的属性来实现这个判断。
+
public bool CanAddItem(IItem item)
{
- if (item is Packsack) return false;
+ if (!item.CanPutInPack) return false;
return packsack.ItemContainer?.CanAddItem(item) ?? false;
}
public bool AddItem(IItem item)
{
- if (item is Packsack) return false;
+ if (!item.CanPutInPack) return false;
return packsack.ItemContainer?.AddItem(item) ?? false;
}
@@ -45,7 +44,7 @@ public class PacksackStack(Packsack packsack) : IItemStack
public IItem? GetItem()
{
- return Empty ? packsack : null;
+ return Empty ? null : packsack;
}
public IItem? PickItem()
diff --git a/scripts/item/weapon/WeaponTemplate.cs b/scripts/item/weapon/WeaponTemplate.cs
index ff08f75..d323f0e 100644
--- a/scripts/item/weapon/WeaponTemplate.cs
+++ b/scripts/item/weapon/WeaponTemplate.cs
@@ -1,6 +1,9 @@
using System;
+
using ColdMint.scripts.character;
using ColdMint.scripts.pickable;
+using ColdMint.scripts.damage;
+
using Godot;
namespace ColdMint.scripts.item.weapon;
@@ -12,6 +15,7 @@ namespace ColdMint.scripts.item.weapon;
public abstract partial class WeaponTemplate : PickAbleTemplate
{
private float _gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
+
public override void Use(Node2D? owner, Vector2 targetGlobalPosition)
{
Fire(owner, targetGlobalPosition);
@@ -25,7 +29,16 @@ public abstract partial class WeaponTemplate : PickAbleTemplate
/// 开火间隔
///
private TimeSpan _firingInterval;
- [Export] private long _firingIntervalAsMillisecond = 100;
+ private long _firingIntervalAsMillisecond = 100;
+ [Export] protected long FiringIntervalAsMillisecond
+ {
+ get => _firingIntervalAsMillisecond;
+ set
+ {
+ _firingIntervalAsMillisecond = value;
+ _firingInterval = TimeSpan.FromMilliseconds(_firingIntervalAsMillisecond);
+ }
+ }
///
@@ -38,16 +51,7 @@ public abstract partial class WeaponTemplate : PickAbleTemplate
///
[Export] private Vector2 _recoil;
- public override void _Ready()
- {
- _firingInterval = TimeSpan.FromMilliseconds(_firingIntervalAsMillisecond);
- }
-
-
-
-
-
-
+ public override void _Ready() { }
///
/// Discharge of the weapon
diff --git a/scripts/loader/uiLoader/MainMenuLoader.cs b/scripts/loader/uiLoader/MainMenuLoader.cs
index 5baa3a7..925a1a9 100644
--- a/scripts/loader/uiLoader/MainMenuLoader.cs
+++ b/scripts/loader/uiLoader/MainMenuLoader.cs
@@ -8,9 +8,11 @@ using ColdMint.scripts.deathInfo;
using ColdMint.scripts.debug;
using ColdMint.scripts.inventory;
using ColdMint.scripts.item;
+using ColdMint.scripts.loot;
using ColdMint.scripts.map;
using ColdMint.scripts.map.roomInjectionProcessor;
using ColdMint.scripts.utils;
+
using Godot;
namespace ColdMint.scripts.loader.uiLoader;
@@ -47,31 +49,6 @@ public partial class MainMenuLoader : UiLoaderTemplate
LogCat.MinLogLevel = LogCat.DisableAllLogLevel;
}
- //注册测试使用的战利品表
- if (Config.IsDebug())
- {
- var testLootList = new LootList
- {
- Id = Config.LootListId.Test
- };
- var staffOfTheUndead = new LootEntry
- {
- Chance = 0.05f,
- MaxQuantity = 5,
- MinQuantity = 1,
- ResPath = "res://prefab/weapons/staffOfTheUndead.tscn"
- };
- testLootList.AddLootEntry(staffOfTheUndead);
- var packsack = new LootEntry
- {
- Chance = 1f,
- MaxQuantity = 1,
- MinQuantity = 1,
- ResPath = "res://prefab/packsacks/packsack.tscn"
- };
- testLootList.AddLootEntry(packsack);
- LootListManager.RegisterLootList(testLootList);
- }
ContributorDataManager.RegisterAllContributorData();
DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler());
@@ -87,6 +64,7 @@ public partial class MainMenuLoader : UiLoaderTemplate
Directory.CreateDirectory(dataPath);
}
+
//Registered camp
//注册阵营
var defaultCamp = new Camp(Config.CampId.Default)
@@ -101,13 +79,16 @@ public partial class MainMenuLoader : UiLoaderTemplate
_gameScene = GD.Load("res://scenes/game.tscn");
_contributor = GD.Load("res://scenes/contributor.tscn");
_levelGraphEditor = GD.Load("res://scenes/levelGraphEditor.tscn");
-
+
//Register ItemTypes from file
//从文件注册物品类型
- ItemTypeManager.RegisterFromFile();
+ ItemTypeRegister.RegisterFromFile();
//Hardcoded ItemTypes Register
//硬编码注册物品类型
- ItemTypeManager.StaticRegister();
+ ItemTypeRegister.StaticRegister();
+
+ //静态注册掉落表
+ LootRegister.StaticRegister();
}
public override void InitializeUi()
diff --git a/scripts/loot/LootDatum.cs b/scripts/loot/LootDatum.cs
new file mode 100644
index 0000000..4053484
--- /dev/null
+++ b/scripts/loot/LootDatum.cs
@@ -0,0 +1,8 @@
+using Godot;
+
+namespace ColdMint.scripts.loot;
+
+public readonly record struct LootDatum(string ItemId, int Quantity)
+{
+ public (string id, int quantity) Value => (ItemId, Quantity);
+}
\ No newline at end of file
diff --git a/scripts/loot/LootEntry.cs b/scripts/loot/LootEntry.cs
new file mode 100644
index 0000000..c4d9ea4
--- /dev/null
+++ b/scripts/loot/LootEntry.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using ColdMint.scripts.utils;
+
+namespace ColdMint.scripts.loot;
+
+///
+/// Loot entry
+/// 战利品条目
+///
+public readonly struct LootEntry(string itemId,int minQuantity=1,int maxQuantity = 1,int weight = 1)
+{
+ ///
+ /// ID of item
+ /// 物品ID
+ ///
+ public string ItemId { get; init; } = itemId;
+
+ ///
+ /// Minimum number of generated
+ /// 最小生成多少个
+ ///
+ public int MinQuantity { get; init; } = minQuantity;
+
+ ///
+ /// The maximum number of files to be generated
+ /// 最多生成多少个
+ ///
+ public int MaxQuantity { get; init; } = maxQuantity;
+
+ ///
+ /// Weight of probability within the drop group
+ /// 在掉落组内的生成权重
+ ///
+ public int Weight { get; init; } = weight;
+}
+
+///
+/// Loot Group
+/// 战利品分组
+///
+///
+///
+public readonly record struct LootGroup(double Chance, IEnumerable Entries)
+{
+ private int WeightSum { get; } = Entries.Sum(entry => entry.Weight);
+
+ public LootDatum GenerateLootData()
+ {
+ var random = RandomUtils.Instance;
+ var w = random.Next(WeightSum);
+ LootEntry entry = default;
+ foreach (var e in Entries)
+ {
+ w -= e.Weight;
+ if (w >= 0) continue;
+ entry = e;
+ break;
+ }
+
+ var quantity = random.Next(entry.MinQuantity, entry.MaxQuantity + 1);
+
+ return new(entry.ItemId, quantity);
+ }
+}
\ No newline at end of file
diff --git a/scripts/loot/LootList.cs b/scripts/loot/LootList.cs
new file mode 100644
index 0000000..ffba1f3
--- /dev/null
+++ b/scripts/loot/LootList.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+
+using ColdMint.scripts.debug;
+using ColdMint.scripts.utils;
+
+using Godot;
+
+namespace ColdMint.scripts.loot;
+
+///
+/// Loot list
+/// 战利品表
+///
+public readonly struct LootList(string id, IList groups)
+{
+ public string Id { get; } = id;
+ private IList Groups { get; } = groups;
+
+ private static Random Random => RandomUtils.Instance;
+
+ ///
+ /// GenerateLootData
+ /// 生成战利品数据
+ ///
+ ///
+ public LootDatum[] GenerateLootData()
+ {
+ if (Groups is [])
+ {
+ LogCat.LogWithFormat("loot_list_has_no_entries", Id);
+ return [];
+ }
+
+ var lootDataList = new List();
+ foreach (var group in Groups)
+ {
+ //If the random number is greater than the generation probability, skip the current loop.
+ //如果随机数大于生成概率,则跳过当前循环。
+ var rd = Random.NextDouble();
+ if (rd > group.Chance) continue;
+
+ //We generate a loot data for each loot entry.
+ //我们为每个战利品条目生成一个战利品数据。
+ var datum = group.GenerateLootData();
+ lootDataList.Add(datum);
+ LogCat.LogWithFormat("loot_data_add", datum);
+ }
+
+ LogCat.LogWithFormat("loot_data_quantity", lootDataList.Count);
+ return lootDataList.ToArray();
+ }
+}
\ No newline at end of file
diff --git a/scripts/loot/LootListManager.cs b/scripts/loot/LootListManager.cs
new file mode 100644
index 0000000..09e7709
--- /dev/null
+++ b/scripts/loot/LootListManager.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+
+using ColdMint.scripts.utils;
+
+using Godot;
+
+namespace ColdMint.scripts.loot;
+
+///
+/// LootListManager
+/// 战利品表管理器
+///
+public static class LootListManager
+{
+ private static Dictionary LootListDictionary { get; } = [];
+
+ ///
+ /// Register loot table
+ /// 注册战利品表
+ ///
+ ///
+ public static bool RegisterLootList(LootList lootList)
+ {
+ var id = lootList.Id;
+ if (id is "") return false;
+ return LootListDictionary.TryAdd(id, lootList);
+ }
+
+ ///
+ /// Remove loot list
+ /// 移除战利品表
+ ///
+ ///
+ ///
+ public static bool UnregisterLootList(string id)
+ {
+ return LootListDictionary.Remove(id);
+ }
+
+ ///
+ /// Generate an loot data.
+ /// 获取掉落物品
+ ///
+ ///
+ ///
+ public static IEnumerable GenerateLootData(string id)
+ {
+ if (!LootListDictionary.TryGetValue(id, out var list)) return [];
+ return list.GenerateLootData();
+ }
+}
\ No newline at end of file
diff --git a/scripts/loot/LootRegister.cs b/scripts/loot/LootRegister.cs
new file mode 100644
index 0000000..208b8ec
--- /dev/null
+++ b/scripts/loot/LootRegister.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+
+namespace ColdMint.scripts.loot;
+
+public static class LootRegister
+{
+ ///
+ /// Register loots hardcoded here
+ /// 在这里硬编码地注册掉落表
+ ///
+ public static void StaticRegister()
+ {
+ //注册测试使用的战利品表
+ if (Config.IsDebug())
+ {
+ IList lootGroups = [];
+ lootGroups.Add(new LootGroup(0.8,
+ [
+ new LootEntry("degraded_staff_of_the_undead", weight: 2), new LootEntry("staff_of_the_undead")
+ ]));
+ lootGroups.Add(new LootGroup(1,
+ [
+ new LootEntry("packsack", minQuantity: 2, maxQuantity: 4)
+ ]));
+
+ var testLootList = new LootList(Config.LootListId.Test, lootGroups);
+ LootListManager.RegisterLootList(testLootList);
+ }
+ }
+}
\ No newline at end of file
diff --git a/scripts/pickable/PickAbleTemplate.cs b/scripts/pickable/PickAbleTemplate.cs
index 36e2da3..5d0dd9d 100644
--- a/scripts/pickable/PickAbleTemplate.cs
+++ b/scripts/pickable/PickAbleTemplate.cs
@@ -1,8 +1,10 @@
using System;
+
using ColdMint.scripts.camp;
using ColdMint.scripts.character;
using ColdMint.scripts.damage;
using ColdMint.scripts.item;
+
using Godot;
namespace ColdMint.scripts.pickable;
@@ -14,12 +16,13 @@ namespace ColdMint.scripts.pickable;
public partial class PickAbleTemplate : RigidBody2D, IItem
{
[Export] public virtual string Id { get; set; } = "ID";
- protected Texture2D? UniqueIcon { get; set; }
+ [Export] protected Texture2D? UniqueIcon { get; set; }
public Texture2D Icon => UniqueIcon ?? ItemTypeManager.DefaultIconOf(Id);
- protected string? UniqueName { get; set; }
+ [Export] protected string? UniqueName { get; set; }
public new string Name => UniqueName ?? ItemTypeManager.DefaultNameOf(Id);
- protected string? UniqueDescription { get; set; }
-
+ [Export] protected string? UniqueDescription { get; set; }
+ public virtual bool CanPutInPack => true;
+
///
/// Owner
/// 主人
@@ -55,9 +58,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
///
public bool Picked { get; set; }
- public virtual void Use(Node2D? owner, Vector2 targetGlobalPosition)
- {
- }
+ public virtual void Use(Node2D? owner, Vector2 targetGlobalPosition) { }
public override void _Ready()
{
@@ -124,7 +125,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
//Determine if your side can cause damage
//判断所属的阵营是否可以造成伤害
var canCauseHarm = CampManager.CanCauseHarm(CampManager.GetCamp(ownerCharacterTemplate.CampId),
- CampManager.GetCamp(characterTemplate.CampId));
+ CampManager.GetCamp(characterTemplate.CampId));
if (!canCauseHarm)
{
return;
@@ -147,8 +148,8 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
LinearVelocity *= 1 - Config.ThrownItemsHitEnemiesReduceSpeedByPercentage;
}
}
-
-
+
+
///
/// Flip item
/// 翻转物品
diff --git a/scripts/utils/GodotStringNameUtils.cs b/scripts/utils/GodotStringNameUtils.cs
new file mode 100644
index 0000000..ff4bb5f
--- /dev/null
+++ b/scripts/utils/GodotStringNameUtils.cs
@@ -0,0 +1,8 @@
+using Godot;
+
+namespace ColdMint.scripts.utils;
+
+public static class GodotStringNameUtils
+{
+ public static StringName AddChild { get; } = new("add_child");
+}
\ No newline at end of file
diff --git a/scripts/utils/NodeUtils.cs b/scripts/utils/NodeUtils.cs
index 5bd5e63..3d183e2 100644
--- a/scripts/utils/NodeUtils.cs
+++ b/scripts/utils/NodeUtils.cs
@@ -1,10 +1,12 @@
using System;
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;
+
using Godot;
using PacksackUi = ColdMint.scripts.loader.uiLoader.PacksackUi;
@@ -71,7 +73,7 @@ public static class NodeUtils
///
///
public static Node2D? GetTheNearestNode(Node2D origin, Node[] array, HashSet? exclude = null,
- bool excludeInvisibleNodes = true)
+ bool excludeInvisibleNodes = true)
{
var closestDistance = float.MaxValue;
Node2D? closestNode = null;
@@ -127,7 +129,7 @@ public static class NodeUtils
return GameSceneNodeHolder.WeaponContainer;
}
- if (GameSceneNodeHolder.PacksackContainer!= null && childNode is Packsack)
+ if (GameSceneNodeHolder.PacksackContainer != null && childNode is Packsack)
{
return GameSceneNodeHolder.PacksackContainer;
}
@@ -160,37 +162,68 @@ public static class NodeUtils
/// 默认父节点,如果传入null,则不会将实例化后的节点放入父节点内。您需要手动放置,这对于延迟设置父节点相当有用。
///
///
- public static T? InstantiatePackedScene(PackedScene packedScene, Node? defaultParentNode = null,
- bool assignedByRootNodeType = true) where T : Node
+ ///
+ public static Node InstantiatePackedScene(PackedScene packedScene, Node? defaultParentNode = null,
+ bool assignedByRootNodeType = true)
{
- try
+ var instantiateNode = packedScene.Instantiate();
+ //An attempt is made to place an instantiated node under a parent node only after the default parent node is set.
+ //只有设定了默认父节点后才会尝试将实例化的节点放置到父节点下。
+ if (defaultParentNode != null)
{
- var instantiateNode = packedScene.Instantiate();
- //An attempt is made to place an instantiated node under a husband node only after the default parent node is set.
- //只有设定了默认父节点后才会尝试将实例化的节点放置到夫节点下。
- if (defaultParentNode != null)
+ if (assignedByRootNodeType)
{
- if (assignedByRootNodeType)
- {
- var containerNode = FindContainerNode(instantiateNode, defaultParentNode);
- containerNode.AddChild(instantiateNode);
- }
- else
- {
- //If you do not need to assign by type, place it under the default parent node.
- //如果不需要按照类型分配,那么将其放到默认父节点下。
- defaultParentNode.AddChild(instantiateNode);
- }
+ var containerNode = FindContainerNode(instantiateNode, defaultParentNode);
+ containerNode.AddChild(instantiateNode);
}
+ else
+ {
+ //If you do not need to assign by type, place it under the default parent node.
+ //如果不需要按照类型分配,那么将其放到默认父节点下。
+ defaultParentNode.AddChild(instantiateNode);
+ }
+ }
- return instantiateNode;
- }
- catch (InvalidCastException e)
- {
- //null is returned if the type conversion fails.
- //在类型转换失败时,返回null。
- LogCat.WhenCaughtException(e);
- return null;
- }
+ return instantiateNode;
+ }
+
+ ///
+ ///
+ /// Generic version of that transforms the created node to type,
+ /// if the created node can't be transformed to the specified type, return null and release it.
+ ///
+ /// 的泛型版本,转换创建的节点至类型,如果创建的节点无法转型至指定的类型,返回null并释放它
+ ///
+ ///
+ ///This method is recommended in place of all packedScene.Instantiate<T>() calls within a project, using it to instantiate a scene, optionally assigned to a container that matches the type of the root node.
+ ///推荐使用此方法代替项目内所有的packedScene.Instantiate<T>()调用,使用此方法实例化场景,可选择将其分配到与根节点类型相匹配的容器内。
+ ///
+ ///
+ ///packedScene
+ ///打包的场景
+ ///
+ ///
+ ///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.
+ ///默认启用,实例化节点后,是否将其放置到与根节点类型相匹配的容器节点内。如果按照类型分配失败,则放置在默认父节点下。
+ ///
+ ///
+ /// Default parent. If null is passed, the instantiated node is not put into the parent. You need to place it manually, which is quite useful for delaying setting the parent node.
+ /// 默认父节点,如果传入null,则不会将实例化后的节点放入父节点内。您需要手动放置,这对于延迟设置父节点相当有用。
+ ///
+ ///
+ /// If new node cannot cast to given type, return null
+ /// 如果创建的节点无法转型至指定的类型,返回null
+ ///
+ public static T? InstantiatePackedScene(PackedScene packedScene, Node? defaultParentNode = null,
+ bool assignedByRootNodeType = true)
+ where T : class
+ {
+ var node = InstantiatePackedScene(packedScene, defaultParentNode, assignedByRootNodeType);
+ // 检查类型转化,成功返回结果
+ if (node is T result) return result;
+ //如果转型失败,释放所创建的节点
+ LogCat.LogWarningWithFormat("warning_node_cannot_cast_to",node,nameof(T));
+ node.QueueFree();
+ return null;
}
}
\ No newline at end of file
diff --git a/scripts/utils/RandomUtils.cs b/scripts/utils/RandomUtils.cs
new file mode 100644
index 0000000..f41a5ba
--- /dev/null
+++ b/scripts/utils/RandomUtils.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace ColdMint.scripts.utils;
+
+public static class RandomUtils
+{
+ public static Random Instance { get; } = new();
+}
\ No newline at end of file