diff --git a/data/itemRegs/weapons.yaml b/data/itemRegs/weapons.yaml index 171c341..0d8abb7 100644 --- a/data/itemRegs/weapons.yaml +++ b/data/itemRegs/weapons.yaml @@ -1,4 +1,8 @@ - id: staff_of_the_undead scene_path: res://prefab/weapons/staffOfTheUndead.tscn icon_path: res://sprites/weapon/staffOfTheUndead.png - max_stack_value: 1 \ No newline at end of file + max_stack_value: 1 + custom_args: + - name: FiringIntervalAsMillisecond + type: int + value: 1000 diff --git a/locals/Log.csv b/locals/Log.csv index 63ca9e8..4a39692 100644 --- a/locals/Log.csv +++ b/locals/Log.csv @@ -23,9 +23,11 @@ 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} にキャストすることはできません。 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/scripts/Config.cs b/scripts/Config.cs index 5263cc5..119f521 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 dac5540..a5eec70 100644 --- a/scripts/character/CharacterTemplate.cs +++ b/scripts/character/CharacterTemplate.cs @@ -10,6 +10,7 @@ using ColdMint.scripts.inventory; using ColdMint.scripts.item; using ColdMint.scripts.utils; using ColdMint.scripts.item.weapon; +using ColdMint.scripts.loot; using Godot; @@ -107,11 +108,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 +205,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 +452,11 @@ 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); + //Todo : change name str to nameof(), like this + // CallDeferred(nameof(GenerateLootObjects), this, lootData, finalGlobalPosition); + GenerateLootObjects(GetParent(), lootData, finalGlobalPosition); } /// @@ -482,13 +464,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 +587,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)); } } 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/item/ItemTypeManager.cs b/scripts/item/ItemTypeManager.cs index 9416d07..cf7261d 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,12 +12,6 @@ namespace ColdMint.scripts.item; /// public static class ItemTypeManager { - /// - /// Register items here - /// 在这里注册物品 - /// - public static void StaticRegister() { } - private static Dictionary Registry { get; } = []; private static Texture2D DefaultTexture { get; } = new PlaceholderTexture2D(); @@ -45,9 +35,66 @@ 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 void CreateItem(string id, Node? parent = null, Vector2? position = null) + { + var item = NewItem(id); + parent?.AddChild(item as Node); + if (item is not Node2D node) return; + if (position is { } pos) node.GlobalPosition = pos; + } + + /// + /// 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 void CreateItems(string id, int amount, Node? parent = null, Vector2? position = null) + { + for (int i = 0; i < amount; i++) + { + CreateItem(id, parent, position); + } + } + /// /// 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 index 9b22422..00b20f2 100644 --- a/scripts/item/ItemTypeRegister.cs +++ b/scripts/item/ItemTypeRegister.cs @@ -17,6 +17,12 @@ namespace ColdMint.scripts.item; /// public static class ItemTypeRegister { + /// + /// Register items here + /// 在这里注册物品 + /// + public static void StaticRegister() { } + /// /// Register items from yaml file /// 从文件注册物品 diff --git a/scripts/loader/uiLoader/MainMenuLoader.cs b/scripts/loader/uiLoader/MainMenuLoader.cs index 3659e79..4e7676c 100644 --- a/scripts/loader/uiLoader/MainMenuLoader.cs +++ b/scripts/loader/uiLoader/MainMenuLoader.cs @@ -7,6 +7,7 @@ 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; @@ -43,31 +44,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); - } DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler()); MapGenerator.RegisterRoomInjectionProcessor(new ChanceRoomInjectionProcessor()); @@ -82,6 +58,7 @@ public partial class MainMenuLoader : UiLoaderTemplate Directory.CreateDirectory(dataPath); } + //Registered camp //注册阵营 var defaultCamp = new Camp(Config.CampId.Default) @@ -94,13 +71,16 @@ public partial class MainMenuLoader : UiLoaderTemplate var aborigines = new Camp(Config.CampId.Aborigines); CampManager.AddCamp(aborigines); _gameScene = (PackedScene)GD.Load("res://scenes/game.tscn"); - + //Register ItemTypes from file //从文件注册物品类型 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..77a834b --- /dev/null +++ b/scripts/loot/LootEntry.cs @@ -0,0 +1,68 @@ +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) + { + entry = e; + } + } + + 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..4ccd9da --- /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(1, + [ + new LootEntry("packsack", weight: 2), new LootEntry("staff_of_the_undead", minQuantity: 2, maxQuantity: 4) + ])); + lootGroups.Add(new LootGroup(0.3, + [ + new LootEntry("packsack") + ])); + + var testLootList = new LootList(Config.LootListId.Test, lootGroups); + LootListManager.RegisterLootList(testLootList); + } + } +} \ 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