using System; using System.Collections.Generic; using ColdMint.scripts.camp; using ColdMint.scripts.character; using ColdMint.scripts.damage; using ColdMint.scripts.pickable; using ColdMint.scripts.projectile.decorator; using ColdMint.scripts.utils; using Godot; namespace ColdMint.scripts.projectile; /// /// Projectile /// 抛射体 /// public partial class Projectile : CharacterBody2D { /// /// life(ms) /// 子弹的存在时间(毫秒) /// [Export] 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时,销毁抛射体 [Export] private double _durability; [Export] private int _maxDamage; [Export] private int _minDamage; [Export] private int _damageType; /// /// After this time destroy the projectile /// 超过此时刻销毁抛射体 /// private DateTime? _destructionTime; [Export] public float Speed; /// /// Whether it bounces back after hitting an enemy or a wall /// 是否撞到敌人或墙壁后反弹 /// [Export] private bool _enableBounce; /// /// Can it penetrate the wall /// 是否可以穿透墙壁 /// [Export] private bool _ignoreWall; /// /// Enable the tracking of the enemy /// 启用追踪敌人的功能 /// [Export] private bool _enableTracking; /// /// The target dies and destroys the projectile at the same time /// 在目标死亡后销毁抛射体 /// [Export] private bool _targetDiesDestroyProjectile; /// /// The target /// 设置目标 /// public Node2D? TargetNode { get; set; } private List? _projectileDecorators; /// /// Rays used to detect walls /// 用于检测墙壁的射线 /// private RayCast2D? _wallRayCast; /// /// Repel strength /// 击退强度 /// /// ///Must be greater than 0, the unit is in format. ///必须大于0,单位为格式。 /// [Export] private float _repelStrength; /// /// The master of the weapon /// 武器的主人 /// public new Node2D? Owner { get; set; } public override void _Ready() { if (!_ignoreWall) { _wallRayCast = new RayCast2D(); _wallRayCast.SetCollisionMaskValue(Config.LayerNumber.Wall, true); _wallRayCast.SetCollisionMaskValue(Config.LayerNumber.Floor, true); NodeUtils.CallDeferredAddChild(this, _wallRayCast); } //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); SetCollisionMaskValue(Config.LayerNumber.Wall, !_ignoreWall); SetCollisionMaskValue(Config.LayerNumber.Floor, !_ignoreWall); SetCollisionMaskValue(Config.LayerNumber.Player, true); SetCollisionMaskValue(Config.LayerNumber.Mob, true); SetCollisionMaskValue(Config.LayerNumber.PickAbleItem, true); //Platform collision layer is not allowed to collide //平台碰撞层不可碰撞 SetCollisionMaskValue(Config.LayerNumber.Platform, false); if (TargetNode != null) { TargetNode.TreeExiting += () => { //Clear the trace when the target is destroyed. //在目标被销毁的时候清空跟踪。 TargetNode = null; if (_targetDiesDestroyProjectile) { OnTimeOut(); } }; } } /// /// 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; } if (target is PickAbleTemplate pickAbleTemplate) { //The picked-up item cannot resist the bullet. //被拾起的物品无法抵挡子弹。 return !pickAbleTemplate.Picked; } 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(owner, characterTemplate); }); } if (_repelStrength > 0) { //If we set the attack force, then apply the force to the object //如果我们设置了攻退力,那么将力应用到对象上 var normalized = Velocity.Normalized(); characterTemplate.AddForce(new Vector2(normalized.X * _repelStrength * Config.CellSize, normalized.Y * _repelStrength * Config.CellSize)); } } else if (target is PickAbleTemplate pickAbleTemplate) { if (_repelStrength > 0) { //If we set the attack force, then apply the force to the object //如果我们设置了攻退力,那么将力应用到对象上 var normalized = Velocity.Normalized(); pickAbleTemplate.ApplyImpulse(new Vector2(normalized.X * _repelStrength * Config.CellSize, normalized.Y * _repelStrength * Config.CellSize)); } } } /// /// Call the method of the decorator /// 调用装饰器的方法 /// /// private void InvokeDecorators(Action projectileDecoratorAction) { if (_projectileDecorators == null) { return; } foreach (var decorator in _projectileDecorators) { projectileDecoratorAction(decorator); } } /// /// 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) { var collisionInfo = MoveAndCollide(Velocity * (float)delta); if (collisionInfo == null) { //No collision. //没有撞到任何东西。 if (_enableTracking && TargetNode != null) { //Track the target //追踪目标 //Gets a vector of the projectile pointing at the enemy's position. //得到抛射体指向敌人位置的向量。 var desiredVelocity = TargetNode.GlobalPosition - GlobalPosition; if (!_ignoreWall && _wallRayCast != null) { _wallRayCast!.TargetPosition = desiredVelocity; if (_wallRayCast.IsColliding()) { return; } } var actualDesiredVelocity = desiredVelocity.Normalized() * Speed; //The weight is smaller, the circle is larger. //weight越小,子弹绕的圈越大。 Velocity = Velocity.Lerp(actualDesiredVelocity, 0.1f); } } else { //Bump into other objects. //撞到其他对象。 if (_enableBounce) { Velocity = Velocity.Bounce(collisionInfo.GetNormal()); } //Here we test whether harm is allowed, notice that for TileMap, we directly allow harm. //这里我们检测是否允许造成伤害,注意对于TileMap,我们直接允许造成伤害。 var godotObject = collisionInfo.GetCollider(); var node = (Node2D)godotObject; 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(); } } } }