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;