diff --git a/scripts/character/CharacterTemplate.cs b/scripts/character/CharacterTemplate.cs
index b5547d7..3e76c5d 100644
--- a/scripts/character/CharacterTemplate.cs
+++ b/scripts/character/CharacterTemplate.cs
@@ -112,6 +112,11 @@ public partial class CharacterTemplate : CharacterBody2D
public string CampId = null!;
private DamageNumberNodeSpawn? _damageNumber;
+ ///
+ /// Character referenced loot table
+ /// 角色引用的战利品表
+ ///
+ private LootList? _lootList;
private HealthBar? _healthBar;
private DateTime _lastDamageTime;
@@ -207,6 +212,13 @@ public partial class CharacterTemplate : CharacterBody2D
CharacterName = GetMeta("Name", Name).AsString();
CampId = GetMeta("CampId", Config.CampId.Default).AsString();
MaxHp = GetMeta("MaxHp", Config.DefaultMaxHp).AsInt32();
+ string 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)
{
//If Max blood volume is 0 or less, set Max blood volume to 10
diff --git a/scripts/inventory/LootData.cs b/scripts/inventory/LootData.cs
new file mode 100644
index 0000000..6a92357
--- /dev/null
+++ b/scripts/inventory/LootData.cs
@@ -0,0 +1,7 @@
+namespace ColdMint.scripts.inventory;
+
+public class LootData
+{
+ 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
new file mode 100644
index 0000000..0c4b2fa
--- /dev/null
+++ b/scripts/inventory/LootEntry.cs
@@ -0,0 +1,32 @@
+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
new file mode 100644
index 0000000..28fa1c9
--- /dev/null
+++ b/scripts/inventory/LootList.cs
@@ -0,0 +1,88 @@
+using System.Collections.Generic;
+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 List GenerateLootData()
+ {
+ var lootDataList = new List();
+ if (_lootEntrieList == null)
+ {
+ return lootDataList;
+ }
+
+ 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.
+ //如果随机数大于生成概率,则跳过当前循环。
+ 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);
+ }
+
+ return lootDataList;
+ }
+
+
+ ///
+ /// 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
new file mode 100644
index 0000000..dce16f7
--- /dev/null
+++ b/scripts/inventory/LootListManager.cs
@@ -0,0 +1,107 @@
+using System.Collections.Generic;
+using Godot;
+
+namespace ColdMint.scripts.inventory;
+
+///
+/// LootListManager
+/// 战利品表管理器
+///
+public static class LootListManager
+{
+ private static Dictionary? _lootListDictionary;
+
+
+ ///
+ /// Register loot table
+ /// 注册战利品表
+ ///
+ ///
+ ///
+ public static bool RegisterLootList(string id, LootList lootList)
+ {
+ 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
+ /// 生成战利品对象
+ ///
+ ///
+ ///lootDataArray
+ ///战利品数组
+ ///
+ ///
+ ///parentNode
+ ///父节点
+ ///
+ public static void GenerateLootObjects(LootData[] lootDataArray, Node parentNode)
+ {
+ if (lootDataArray.Length == 0)
+ {
+ return;
+ }
+
+ Dictionary packedSceneDictionary = new();
+ foreach (var lootData in lootDataArray)
+ {
+ if (string.IsNullOrEmpty(lootData.ResPath))
+ {
+ continue;
+ }
+
+ if (!packedSceneDictionary.TryGetValue(lootData.ResPath, out var packedScene))
+ {
+ packedScene = GD.Load(lootData.ResPath);
+ packedSceneDictionary.TryAdd(lootData.ResPath, packedScene);
+ }
+
+ CreateLootObject(packedScene, parentNode);
+ }
+ }
+
+ private static void CreateLootObject(PackedScene? packedScene, Node parent)
+ {
+ if (packedScene == null)
+ {
+ return;
+ }
+
+ var lootObject = packedScene.Instantiate();
+ if (lootObject is not Node2D node2D)
+ {
+ return;
+ }
+
+ parent.AddChild(node2D);
+ }
+
+ ///
+ /// Remove loot list
+ /// 移除战利品表
+ ///
+ ///
+ ///
+ public static bool UnregisterLootList(string id)
+ {
+ if (_lootListDictionary == null)
+ {
+ return false;
+ }
+
+ return _lootListDictionary.Remove(id);
+ }
+}
\ No newline at end of file