diff --git a/scripts/character/CharacterTemplate.cs b/scripts/character/CharacterTemplate.cs index 3c2e32b..225aea6 100644 --- a/scripts/character/CharacterTemplate.cs +++ b/scripts/character/CharacterTemplate.cs @@ -502,8 +502,8 @@ public partial class CharacterTemplate : CharacterBody2D ///伤害模板 /// /// - ///Return whether the damage was done successfully - ///返回是否成功造成了伤害 + ///Return whether the character is dead + ///返回本次伤害是否导致角色死亡。 /// public bool Damage(DamageTemplate damageTemplate) { @@ -517,12 +517,10 @@ public partial class CharacterTemplate : CharacterBody2D //角色死亡 OnDie(damageTemplate); ThrowAllItemOnDie(); - return true; } - UpDataHealthBar(); - return true; + return false; } /// diff --git a/scripts/loot/LootEntry.cs b/scripts/loot/LootEntry.cs index d73e08e..d85ab7f 100644 --- a/scripts/loot/LootEntry.cs +++ b/scripts/loot/LootEntry.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; - using ColdMint.scripts.utils; namespace ColdMint.scripts.loot; @@ -9,7 +8,7 @@ namespace ColdMint.scripts.loot; /// Loot entry /// 战利品条目 /// -public readonly struct LootEntry(string itemId,int minQuantity=1,int maxQuantity = 1,int weight = 1) +public readonly struct LootEntry(string itemId, int minQuantity = 1, int maxQuantity = 1, int weight = 1) { /// /// ID of item @@ -40,8 +39,14 @@ public readonly struct LootEntry(string itemId,int minQuantity=1,int maxQuantity /// Loot Group /// 战利品分组 /// -/// -/// +/// +///Chance +///概率 +/// +/// +///Entries +///条目列表 +/// public readonly record struct LootGroup(double Chance, IEnumerable Entries) { private int WeightSum { get; } = Entries.Sum(entry => entry.Weight); @@ -61,6 +66,6 @@ public readonly record struct LootGroup(double Chance, IEnumerable En var quantity = random.Next(entry.MinQuantity, entry.MaxQuantity + 1); - return new(entry.ItemId, quantity); + return new LootDatum(entry.ItemId, quantity); } } \ No newline at end of file diff --git a/scripts/loot/LootRegister.cs b/scripts/loot/LootRegister.cs index 208b8ec..9372640 100644 --- a/scripts/loot/LootRegister.cs +++ b/scripts/loot/LootRegister.cs @@ -10,19 +10,17 @@ public static class LootRegister /// public static void StaticRegister() { + //Register the test using the loot table //注册测试使用的战利品表 if (Config.IsDebug()) { - IList lootGroups = []; - lootGroups.Add(new LootGroup(0.8, + List lootGroups = [ - 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) - ])); - + new LootGroup(0.8, + [ + new LootEntry("staff_necromancy"), + ]) + ]; var testLootList = new LootList(Config.LootListId.Test, lootGroups); LootListManager.RegisterLootList(testLootList); } diff --git a/scripts/projectile/Projectile.cs b/scripts/projectile/Projectile.cs index ec36955..7d93f6c 100644 --- a/scripts/projectile/Projectile.cs +++ b/scripts/projectile/Projectile.cs @@ -1,3 +1,317 @@ +using System; +using System.Collections.Generic; +using ColdMint.scripts.camp; +using ColdMint.scripts.character; +using ColdMint.scripts.damage; +using ColdMint.scripts.inventory; +using ColdMint.scripts.pickable; +using ColdMint.scripts.projectile.decorator; +using Godot; + namespace ColdMint.scripts.projectile; -public partial class Projectile : ProjectileTemplate; +/// +/// Projectile +/// 抛射体 +/// +public partial class Projectile : CharacterBody2D +{ + private long _life; + + //The durability of the projectile + //抛射体的耐久度 + //When the projectile hits the object, the durability will be reduced, and when the durability is less than or equal to 0, the projectile will be destroyed + //当抛射体撞击到物体时,会减少耐久度,当耐久度小于等于0时,销毁抛射体 + private double _durability; + + private int _maxDamage; + private int _minDamage; + private int _damageType; + + /// + /// After this time destroy the projectile + /// 超过此时刻销毁抛射体 + /// + private DateTime? _destructionTime; + + + /// + /// The impact area of the bullet + /// 子弹的碰撞区域 + /// + private Area2D? _area2D; + + /// + /// knockback + /// 击退 + /// + /// + ///How much force does it have when hitting the character? Unit: Number of cells,The X direction of the force is inferred automatically. + ///当击中角色时带有多大的力?单位:格数,力的X方向是自动推断的。 + /// + private Vector2 _knockbackForce; + + public float Speed => GetMeta("Speed", "15").AsSingle(); + + private List? _projectileDecorators; + + + /// + /// The master of the weapon + /// 武器的主人 + /// + public new Node2D? Owner { get; set; } + + public override void _Ready() + { + //The bullet's impact detection area + //子弹的碰撞检测区域 + _area2D = GetNode("CollisionDetectionArea"); + _area2D.Monitoring = true; + _area2D.BodyEntered += OnBodyEnter; + _area2D.BodyExited += OnBodyExited; + _durability = GetMeta("Durability", "1").AsDouble(); + _maxDamage = GetMeta("MaxDamage", "7").AsInt32(); + _minDamage = GetMeta("MinDamage", "5").AsInt32(); + _damageType = GetMeta("DamageType", Config.DamageType.Physical).AsInt32(); + _knockbackForce = GetMeta("Knockback", Vector2.Zero).AsVector2(); + //life(ms) + //子弹的存在时间(毫秒) + _life = GetMeta("Life", "10000").AsInt64(); + //If the existence time is less than or equal to 0, then it is set to exist for 10 seconds, and projectiles that exist indefinitely are prohibited + //如果存在时间小于等于0,那么设置为存在10秒,禁止无限期存在的抛射体 + if (_life <= 0) + { + _life = 10000; + } + + _destructionTime = DateTime.Now.AddMilliseconds(_life); + } + + /// + /// Add decorator + /// 添加装饰器 + /// + /// + ///decorator + ///装饰器 + /// + public void AddProjectileDecorator(IProjectileDecorator decorator) + { + _projectileDecorators ??= []; + _projectileDecorators.Add(decorator); + } + + /// + /// Remove decorator + /// 移除装饰器 + /// + /// + ///decorator + ///装饰器 + /// + /// + public bool RemoveProjectileDecorator(IProjectileDecorator decorator) + { + return _projectileDecorators?.Remove(decorator) ?? false; + } + + /// + /// Detect whether harm is allowed + /// 检测是否允许造成伤害 + /// + /// + /// + /// + private bool CanCauseHarm(Node2D? owner, Node2D target) + { + //We must know who the owner of the bullet is in order to determine whether it should cause damage or not + //我们必须知道子弹的主人是谁,才能判断是否应该造成伤害 + if (owner == null) + { + return false; + } + + if (owner is not CharacterTemplate ownerCharacterTemplate) + { + return false; + } + + if (target is TileMapLayer) + { + //When we hit the tile, we return true to prevent the bullet from penetrating the tile. + //撞击到瓦片时,我们返回true,是为了防止子弹穿透瓦片。 + return true; + } + + //Match any item now + //现在使它识别任何物品 + if (target is IItem) + { + //Bullets are allowed to strike objects. + //允许子弹撞击物品。 + return true; + } + + if (target is not CharacterTemplate characterTemplate) + { + return false; + } + + //First get the owner's camp and compare it with the target camp + //先获取主人的阵营与目标阵营进行比较 + var canCauseHarm = CampManager.CanCauseHarm(CampManager.GetCamp(ownerCharacterTemplate.CampId), + CampManager.GetCamp(characterTemplate.CampId)); + return canCauseHarm; + } + + /// + /// Executive injury treatment + /// 执行伤害处理 + /// + /// + /// + private void DoDamage(Node2D? owner, Node2D target) + { + if (target is CharacterTemplate characterTemplate) + { + //Allow damage to be caused + //允许造成伤害 + var damage = new Damage + { + Attacker = owner, + MaxDamage = _maxDamage, + MinDamage = _minDamage + }; + damage.CreateDamage(); + damage.MoveLeft = Velocity.X < 0; + damage.Type = _damageType; + var dead = characterTemplate.Damage(damage); + if (dead) + { + //If the character is dead, then call OnKillCharacter + //如果角色死亡了,那么调用OnKillCharacter + InvokeDecorators(decorator => { decorator.OnKillCharacter(); }); + } + + if (_knockbackForce != Vector2.Zero) + { + //If we set the attack force, then apply the force to the object + //如果我们设置了攻退力,那么将力应用到对象上 + var force = new Vector2(); + var forceX = Math.Abs(_knockbackForce.X); + if (Velocity.X < 0) + { + //Beat back to port + //向左击退 + forceX = -forceX; + } + + force.X = forceX * Config.CellSize; + force.Y = _knockbackForce.Y * Config.CellSize; + characterTemplate.AddForce(force); + } + } + else if (target is PickAbleTemplate pickAbleTemplate) + { + if (_knockbackForce != Vector2.Zero) + { + //If we set the attack force, then apply the force to the object + //如果我们设置了攻退力,那么将力应用到对象上 + var force = new Vector2(); + var forceX = Math.Abs(_knockbackForce.X); + if (Velocity.X < 0) + { + //Beat back to port + //向左击退 + forceX = -forceX; + } + + force.X = forceX * Config.CellSize; + force.Y = _knockbackForce.Y * Config.CellSize; + pickAbleTemplate.ApplyImpulse(force); + } + } + } + + /// + /// Call the method of the decorator + /// 调用装饰器的方法 + /// + /// + private void InvokeDecorators(Action projectileDecoratorAction) + { + if (_projectileDecorators == null) + { + return; + } + + foreach (var decorator in _projectileDecorators) + { + projectileDecoratorAction(decorator); + } + } + + /// + /// When the bullet is in contact with the node + /// 当子弹与节点接触时 + /// + /// + protected virtual void OnBodyEnter(Node2D node) + { + //Here we test whether harm is allowed, notice that for TileMap, we directly allow harm. + //这里我们检测是否允许造成伤害,注意对于TileMap,我们直接允许造成伤害。 + var canCauseHarm = CanCauseHarm(Owner, node); + if (!canCauseHarm) + { + return; + } + + DoDamage(Owner, node); + //Please specify in the Mask who the bullet will collide with + //请在Mask内配置子弹会和谁碰撞 + //When a bullet hits an object, its durability decreases + //子弹撞击到物体时,耐久度减少 + _durability--; + if (_durability <= 0) + { + //When the durability is less than or equal to 0, destroy the bullet + //当耐久度小于等于0时,销毁子弹 + QueueFree(); + } + } + + /// + /// When the bullet leaves the node + /// 当子弹离开节点时 + /// + /// + protected virtual void OnBodyExited(Node2D node) + { + } + + + /// + /// When beyond the time of existence + /// 当超过存在时间 + /// + private void OnTimeOut() + { + QueueFree(); + } + + public override void _Process(double delta) + { + //If the existence time is exceeded, the projectile is destroyed + //如果超过了存在时间,那么销毁抛射体 + if (DateTime.Now >= _destructionTime) + { + OnTimeOut(); + } + } + + public override void _PhysicsProcess(double delta) + { + MoveAndSlide(); + } +} \ No newline at end of file diff --git a/scripts/projectile/ProjectileTemplate.cs b/scripts/projectile/ProjectileTemplate.cs deleted file mode 100644 index e254b26..0000000 --- a/scripts/projectile/ProjectileTemplate.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System; -using ColdMint.scripts.camp; -using ColdMint.scripts.character; -using ColdMint.scripts.damage; -using ColdMint.scripts.inventory; -using ColdMint.scripts.pickable; -using Godot; - -namespace ColdMint.scripts.projectile; - -/// -/// Projectile template -/// 抛射体模板 -/// -public partial class ProjectileTemplate : CharacterBody2D -{ - private long _life; - - //The durability of the projectile - //抛射体的耐久度 - //When the projectile hits the object, the durability will be reduced, and when the durability is less than or equal to 0, the projectile will be destroyed - //当抛射体撞击到物体时,会减少耐久度,当耐久度小于等于0时,销毁抛射体 - private double _durability; - - private int _maxDamage; - private int _minDamage; - private int _damageType; - - /// - /// After this time destroy the projectile - /// 超过此时刻销毁抛射体 - /// - private DateTime? _destructionTime; - - - /// - /// The impact area of the bullet - /// 子弹的碰撞区域 - /// - private Area2D? _area2D; - - /// - /// knockback - /// 击退 - /// - /// - ///How much force does it have when hitting the character? Unit: Number of cells,The X direction of the force is inferred automatically. - ///当击中角色时带有多大的力?单位:格数,力的X方向是自动推断的。 - /// - private Vector2 _knockbackForce; - - public float Speed => GetMeta("Speed", "15").AsSingle(); - - /// - /// The master of the weapon - /// 武器的主人 - /// - public new Node2D? Owner { get; set; } - - public override void _Ready() - { - //The bullet's impact detection area - //子弹的碰撞检测区域 - _area2D = GetNode("CollisionDetectionArea"); - _area2D.Monitoring = true; - _area2D.BodyEntered += OnBodyEnter; - _area2D.BodyExited += OnBodyExited; - _durability = GetMeta("Durability", "1").AsDouble(); - _maxDamage = GetMeta("MaxDamage", "7").AsInt32(); - _minDamage = GetMeta("MinDamage", "5").AsInt32(); - _damageType = GetMeta("DamageType", Config.DamageType.Physical).AsInt32(); - _knockbackForce = GetMeta("Knockback", Vector2.Zero).AsVector2(); - //life(ms) - //子弹的存在时间(毫秒) - _life = GetMeta("Life", "10000").AsInt64(); - //If the existence time is less than or equal to 0, then it is set to exist for 10 seconds, and projectiles that exist indefinitely are prohibited - //如果存在时间小于等于0,那么设置为存在10秒,禁止无限期存在的抛射体 - if (_life <= 0) - { - _life = 10000; - } - - _destructionTime = DateTime.Now.AddMilliseconds(_life); - } - - - /// - /// Detect whether harm is allowed - /// 检测是否允许造成伤害 - /// - /// - /// - /// - private bool CanCauseHarm(Node2D? owner, Node2D target) - { - //We must know who the owner of the bullet is in order to determine whether it should cause damage or not - //我们必须知道子弹的主人是谁,才能判断是否应该造成伤害 - if (owner == null) - { - return false; - } - - if (owner is not CharacterTemplate ownerCharacterTemplate) - { - return false; - } - - if (target is TileMapLayer) - { - //When we hit the tile, we return true to prevent the bullet from penetrating the tile. - //撞击到瓦片时,我们返回true,是为了防止子弹穿透瓦片。 - return true; - } - - //Match any item now - //现在使它识别任何物品 - if (target is IItem) - { - //Bullets are allowed to strike objects. - //允许子弹撞击物品。 - return true; - } - - if (target is not CharacterTemplate characterTemplate) - { - return false; - } - - //First get the owner's camp and compare it with the target camp - //先获取主人的阵营与目标阵营进行比较 - var canCauseHarm = CampManager.CanCauseHarm(CampManager.GetCamp(ownerCharacterTemplate.CampId), - CampManager.GetCamp(characterTemplate.CampId)); - return canCauseHarm; - } - - /// - /// Executive injury treatment - /// 执行伤害处理 - /// - /// - /// - private void DoDamage(Node2D? owner, Node2D target) - { - if (target is CharacterTemplate characterTemplate) - { - //Allow damage to be caused - //允许造成伤害 - var damage = new Damage - { - Attacker = owner, - MaxDamage = _maxDamage, - MinDamage = _minDamage - }; - damage.CreateDamage(); - damage.MoveLeft = Velocity.X < 0; - damage.Type = _damageType; - characterTemplate.Damage(damage); - if (_knockbackForce != Vector2.Zero) - { - //If we set the attack force, then apply the force to the object - //如果我们设置了攻退力,那么将力应用到对象上 - var force = new Vector2(); - var forceX = Math.Abs(_knockbackForce.X); - if (Velocity.X < 0) - { - //Beat back to port - //向左击退 - forceX = -forceX; - } - - force.X = forceX * Config.CellSize; - force.Y = _knockbackForce.Y * Config.CellSize; - characterTemplate.AddForce(force); - } - } - else if (target is PickAbleTemplate pickAbleTemplate) - { - if (_knockbackForce != Vector2.Zero) - { - //If we set the attack force, then apply the force to the object - //如果我们设置了攻退力,那么将力应用到对象上 - var force = new Vector2(); - var forceX = Math.Abs(_knockbackForce.X); - if (Velocity.X < 0) - { - //Beat back to port - //向左击退 - forceX = -forceX; - } - - force.X = forceX * Config.CellSize; - force.Y = _knockbackForce.Y * Config.CellSize; - pickAbleTemplate.ApplyImpulse(force); - } - } - } - - /// - /// When the bullet is in contact with the node - /// 当子弹与节点接触时 - /// - /// - protected virtual void OnBodyEnter(Node2D node) - { - //Here we test whether harm is allowed, notice that for TileMap, we directly allow harm. - //这里我们检测是否允许造成伤害,注意对于TileMap,我们直接允许造成伤害。 - var canCauseHarm = CanCauseHarm(Owner, node); - if (!canCauseHarm) - { - return; - } - - DoDamage(Owner, node); - //Please specify in the Mask who the bullet will collide with - //请在Mask内配置子弹会和谁碰撞 - //When a bullet hits an object, its durability decreases - //子弹撞击到物体时,耐久度减少 - _durability--; - if (_durability <= 0) - { - //When the durability is less than or equal to 0, destroy the bullet - //当耐久度小于等于0时,销毁子弹 - QueueFree(); - } - } - - /// - /// When the bullet leaves the node - /// 当子弹离开节点时 - /// - /// - protected virtual void OnBodyExited(Node2D node) - { - } - - - /// - /// When beyond the time of existence - /// 当超过存在时间 - /// - private void OnTimeOut() - { - QueueFree(); - } - - public override void _Process(double delta) - { - //If the existence time is exceeded, the projectile is destroyed - //如果超过了存在时间,那么销毁抛射体 - if (DateTime.Now >= _destructionTime) - { - OnTimeOut(); - } - } - - public override void _PhysicsProcess(double delta) - { - MoveAndSlide(); - } -} \ No newline at end of file diff --git a/scripts/projectile/decorator/IProjectileDecorator.cs b/scripts/projectile/decorator/IProjectileDecorator.cs new file mode 100644 index 0000000..e7de1b8 --- /dev/null +++ b/scripts/projectile/decorator/IProjectileDecorator.cs @@ -0,0 +1,18 @@ +namespace ColdMint.scripts.projectile.decorator; + +/// +/// Projectile decorator +/// 抛射体装饰 +/// +/// +///Decorator mode is a structural mode, which can add special features to the projectile. +///装饰模式是一种结构性模式,可以将一种抛射体的特殊功能添加到抛射体上。 +/// +public interface IProjectileDecorator +{ + /// + /// When the character is killed by this projectile + /// 当角色被此抛射体击杀时 + /// + void OnKillCharacter(); +} \ No newline at end of file diff --git a/scripts/weapon/ProjectileWeapon.cs b/scripts/weapon/ProjectileWeapon.cs index d3b0c8b..d07e5fa 100644 --- a/scripts/weapon/ProjectileWeapon.cs +++ b/scripts/weapon/ProjectileWeapon.cs @@ -61,7 +61,7 @@ public partial class ProjectileWeapon : WeaponTemplate //获取第一个抛射体 var projectileScene = ProjectileScenes[0]; // var projectileScene = _projectileCache[_projectiles[0]]; - var projectile = NodeUtils.InstantiatePackedScene(projectileScene); + var projectile = NodeUtils.InstantiatePackedScene(projectileScene); if (projectile == null) return; NodeUtils.CallDeferredAddChild(GameSceneNodeHolder.ProjectileContainer, projectile); projectile.Owner = owner;