2024-08-02 15:57:11 +00:00
using System ;
using System.Collections.Generic ;
using ColdMint.scripts.camp ;
using ColdMint.scripts.character ;
using ColdMint.scripts.damage ;
2024-10-07 15:29:08 +00:00
using ColdMint.scripts.furniture ;
2024-08-02 15:57:11 +00:00
using ColdMint.scripts.pickable ;
using ColdMint.scripts.projectile.decorator ;
2024-08-19 16:15:12 +00:00
using ColdMint.scripts.utils ;
2024-08-02 15:57:11 +00:00
using Godot ;
2024-04-28 13:55:19 +00:00
namespace ColdMint.scripts.projectile ;
2024-08-02 15:57:11 +00:00
/// <summary>
/// <para>Projectile</para>
/// <para>抛射体</para>
/// </summary>
public partial class Projectile : CharacterBody2D
{
2024-08-05 14:52:20 +00:00
/// <summary>
/// <para>life(ms)</para>
/// <para>子弹的存在时间(毫秒)</para>
/// </summary>
2024-08-15 14:58:28 +00:00
[Export] private long _life ;
2024-08-02 15:57:11 +00:00
//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时, 销毁抛射体
2024-08-15 14:58:28 +00:00
[Export] private double _durability ;
2024-08-02 15:57:11 +00:00
2024-08-15 14:58:28 +00:00
[Export] private int _maxDamage ;
[Export] private int _minDamage ;
[Export] private int _damageType ;
2024-08-02 15:57:11 +00:00
/// <summary>
/// <para>After this time destroy the projectile</para>
/// <para>超过此时刻销毁抛射体</para>
/// </summary>
private DateTime ? _destructionTime ;
2024-08-05 14:52:20 +00:00
[Export] public float Speed ;
2024-08-02 15:57:11 +00:00
2024-08-12 14:30:13 +00:00
/// <summary>
/// <para>Whether it bounces back after hitting an enemy or a wall</para>
/// <para>是否撞到敌人或墙壁后反弹</para>
/// </summary>
2024-08-15 14:58:28 +00:00
[Export] private bool _enableBounce ;
2024-08-12 14:30:13 +00:00
/// <summary>
/// <para>Can it penetrate the wall</para>
/// <para>是否可以穿透墙壁</para>
/// </summary>
2024-08-15 14:58:28 +00:00
[Export] private bool _ignoreWall ;
2024-08-12 14:30:13 +00:00
2024-08-14 15:08:07 +00:00
/// <summary>
/// <para>Enable the tracking of the enemy</para>
/// <para>启用追踪敌人的功能</para>
/// </summary>
2024-08-15 14:58:28 +00:00
[Export] private bool _enableTracking ;
2024-08-14 15:08:07 +00:00
2024-08-18 08:09:31 +00:00
/// <summary>
/// <para>The target dies and destroys the projectile at the same time</para>
/// <para>在目标死亡后销毁抛射体</para>
/// </summary>
2024-08-19 16:15:12 +00:00
[Export] private bool _targetDiesDestroyProjectile ;
2024-08-18 08:09:31 +00:00
2024-08-14 15:08:07 +00:00
/// <summary>
/// <para>The target</para>
/// <para>设置目标</para>
/// </summary>
2024-08-18 08:09:31 +00:00
public Node2D ? TargetNode { get ; set ; }
2024-08-14 15:08:07 +00:00
2024-08-02 15:57:11 +00:00
private List < IProjectileDecorator > ? _projectileDecorators ;
2024-08-19 16:15:12 +00:00
/// <summary>
/// <para>Rays used to detect walls</para>
/// <para>用于检测墙壁的射线</para>
/// </summary>
private RayCast2D ? _wallRayCast ;
2024-08-02 15:57:11 +00:00
2024-08-22 14:03:44 +00:00
/// <summary>
/// <para>Repel strength</para>
/// <para>击退强度</para>
/// </summary>
/// <remarks>
///<para>Must be greater than 0, the unit is in format.</para>
///<para>必须大于0,单位为格式。</para>
/// </remarks>
[Export] private float _repelStrength ;
2024-08-02 15:57:11 +00:00
/// <summary>
/// <para>The master of the weapon</para>
/// <para>武器的主人</para>
/// </summary>
public new Node2D ? Owner { get ; set ; }
public override void _Ready ( )
{
2024-08-19 16:15:12 +00:00
if ( ! _ignoreWall )
{
_wallRayCast = new RayCast2D ( ) ;
_wallRayCast . SetCollisionMaskValue ( Config . LayerNumber . Wall , true ) ;
_wallRayCast . SetCollisionMaskValue ( Config . LayerNumber . Floor , true ) ;
NodeUtils . CallDeferredAddChild ( this , _wallRayCast ) ;
}
2024-08-02 15:57:11 +00:00
//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秒, 禁止无限期存在的抛射体
2024-08-15 14:58:28 +00:00
if ( _life < = 0 )
2024-08-02 15:57:11 +00:00
{
2024-08-15 14:58:28 +00:00
_life = 10000 ;
2024-08-02 15:57:11 +00:00
}
2024-08-15 14:58:28 +00:00
_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 ) ;
2024-08-22 14:03:44 +00:00
SetCollisionMaskValue ( Config . LayerNumber . PickAbleItem , true ) ;
2024-10-07 15:29:08 +00:00
SetCollisionMaskValue ( Config . LayerNumber . Barrier , true ) ;
2024-08-12 14:30:13 +00:00
//Platform collision layer is not allowed to collide
//平台碰撞层不可碰撞
SetCollisionMaskValue ( Config . LayerNumber . Platform , false ) ;
2024-08-18 08:09:31 +00:00
if ( TargetNode ! = null )
{
TargetNode . TreeExiting + = ( ) = >
{
//Clear the trace when the target is destroyed.
//在目标被销毁的时候清空跟踪。
TargetNode = null ;
if ( _targetDiesDestroyProjectile )
{
OnTimeOut ( ) ;
}
} ;
}
2024-08-02 15:57:11 +00:00
}
/// <summary>
/// <para>Add decorator</para>
/// <para>添加装饰器</para>
/// </summary>
/// <param name="decorator">
///<para>decorator</para>
///<para>装饰器</para>
/// </param>
public void AddProjectileDecorator ( IProjectileDecorator decorator )
{
_projectileDecorators ? ? = [ ] ;
_projectileDecorators . Add ( decorator ) ;
}
/// <summary>
/// <para>Remove decorator</para>
/// <para>移除装饰器</para>
/// </summary>
/// <param name="decorator">
///<para>decorator</para>
///<para>装饰器</para>
/// </param>
/// <returns></returns>
public bool RemoveProjectileDecorator ( IProjectileDecorator decorator )
{
return _projectileDecorators ? . Remove ( decorator ) ? ? false ;
}
/// <summary>
/// <para>Detect whether harm is allowed</para>
/// <para>检测是否允许造成伤害</para>
/// </summary>
/// <param name="owner"></param>
/// <param name="target"></param>
/// <returns></returns>
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 ;
}
2024-08-12 14:30:13 +00:00
2024-10-07 15:29:08 +00:00
if ( target is Furniture )
{
return true ;
}
2024-08-11 15:29:23 +00:00
if ( target is PickAbleTemplate pickAbleTemplate )
2024-08-02 15:57:11 +00:00
{
2024-08-11 15:29:23 +00:00
//The picked-up item cannot resist the bullet.
//被拾起的物品无法抵挡子弹。
return ! pickAbleTemplate . Picked ;
2024-08-02 15:57:11 +00:00
}
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 ;
}
/// <summary>
/// <para>Executive injury treatment</para>
/// <para>执行伤害处理</para>
/// </summary>
/// <param name="owner"></param>
/// <param name="target"></param>
private void DoDamage ( Node2D ? owner , Node2D target )
{
if ( target is CharacterTemplate characterTemplate )
{
//Allow damage to be caused
//允许造成伤害
var damage = new Damage
{
Attacker = owner ,
2024-08-15 14:58:28 +00:00
MaxDamage = _maxDamage ,
MinDamage = _minDamage
2024-08-02 15:57:11 +00:00
} ;
damage . CreateDamage ( ) ;
damage . MoveLeft = Velocity . X < 0 ;
2024-08-15 14:58:28 +00:00
damage . Type = _damageType ;
2024-08-02 15:57:11 +00:00
var dead = characterTemplate . Damage ( damage ) ;
if ( dead )
{
//If the character is dead, then call OnKillCharacter
//如果角色死亡了, 那么调用OnKillCharacter
2024-08-03 16:32:49 +00:00
InvokeDecorators ( decorator = > { decorator . OnKillCharacter ( owner , characterTemplate ) ; } ) ;
2024-08-02 15:57:11 +00:00
}
2024-08-22 14:03:44 +00:00
if ( _repelStrength > 0 )
2024-08-02 15:57:11 +00:00
{
//If we set the attack force, then apply the force to the object
//如果我们设置了攻退力,那么将力应用到对象上
2024-08-22 14:03:44 +00:00
var normalized = Velocity . Normalized ( ) ;
characterTemplate . AddForce ( new Vector2 ( normalized . X * _repelStrength * Config . CellSize ,
normalized . Y * _repelStrength * Config . CellSize ) ) ;
2024-08-02 15:57:11 +00:00
}
}
else if ( target is PickAbleTemplate pickAbleTemplate )
{
2024-08-22 14:03:44 +00:00
if ( _repelStrength > 0 )
2024-08-02 15:57:11 +00:00
{
//If we set the attack force, then apply the force to the object
//如果我们设置了攻退力,那么将力应用到对象上
2024-08-22 14:03:44 +00:00
var normalized = Velocity . Normalized ( ) ;
pickAbleTemplate . ApplyImpulse ( new Vector2 ( normalized . X * _repelStrength * Config . CellSize ,
normalized . Y * _repelStrength * Config . CellSize ) ) ;
2024-08-02 15:57:11 +00:00
}
2024-10-07 15:29:08 +00:00
} else if ( target is Furniture furniture )
{
var damage = new Damage
{
Attacker = owner ,
MaxDamage = _maxDamage ,
MinDamage = _minDamage
} ;
damage . CreateDamage ( ) ;
damage . MoveLeft = Velocity . X < 0 ;
damage . Type = _damageType ;
furniture . Damage ( damage ) ;
2024-08-02 15:57:11 +00:00
}
}
/// <summary>
/// <para>Call the method of the decorator</para>
/// <para>调用装饰器的方法</para>
/// </summary>
/// <param name="projectileDecoratorAction"></param>
private void InvokeDecorators ( Action < IProjectileDecorator > projectileDecoratorAction )
{
if ( _projectileDecorators = = null )
{
return ;
}
foreach ( var decorator in _projectileDecorators )
{
projectileDecoratorAction ( decorator ) ;
}
}
/// <summary>
/// <para>When beyond the time of existence</para>
/// <para>当超过存在时间</para>
/// </summary>
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 )
{
2024-08-12 14:30:13 +00:00
var collisionInfo = MoveAndCollide ( Velocity * ( float ) delta ) ;
2024-08-14 15:08:07 +00:00
if ( collisionInfo = = null )
{
//No collision.
//没有撞到任何东西。
2024-08-18 08:09:31 +00:00
if ( _enableTracking & & TargetNode ! = null )
2024-08-14 15:08:07 +00:00
{
//Track the target
//追踪目标
//Gets a vector of the projectile pointing at the enemy's position.
//得到抛射体指向敌人位置的向量。
2024-08-19 16:15:12 +00:00
var desiredVelocity = TargetNode . GlobalPosition - GlobalPosition ;
if ( ! _ignoreWall & & _wallRayCast ! = null )
{
_wallRayCast ! . TargetPosition = desiredVelocity ;
if ( _wallRayCast . IsColliding ( ) )
{
return ;
}
}
var actualDesiredVelocity = desiredVelocity . Normalized ( ) * Speed ;
2024-08-14 15:08:07 +00:00
//The weight is smaller, the circle is larger.
//weight越小, 子弹绕的圈越大。
2024-08-19 16:15:12 +00:00
Velocity = Velocity . Lerp ( actualDesiredVelocity , 0.1f ) ;
2024-08-14 15:08:07 +00:00
}
}
else
2024-08-12 14:30:13 +00:00
{
//Bump into other objects.
//撞到其他对象。
2024-08-15 14:58:28 +00:00
if ( _enableBounce )
2024-08-12 14:30:13 +00:00
{
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
//子弹撞击到物体时,耐久度减少
2024-08-15 14:58:28 +00:00
_durability - - ;
if ( _durability < = 0 )
2024-08-12 14:30:13 +00:00
{
//When the durability is less than or equal to 0, destroy the bullet
//当耐久度小于等于0时, 销毁子弹
QueueFree ( ) ;
}
}
2024-08-02 15:57:11 +00:00
}
}