using System;
using ColdMint.scripts.camp;
using ColdMint.scripts.character;
using ColdMint.scripts.damage;
using Godot;
namespace ColdMint.scripts.item.weapon;
///
/// WeaponTemplate
/// 武器模板
///
public abstract partial class WeaponTemplate : RigidBody2D, IItem
{
private float _gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
//Implements IItem
[Export] public virtual string Id { get; private set; } = "ID";
protected Texture2D? UniqueIcon { get; set; }
public Texture2D Icon => UniqueIcon ?? ItemTypeManager.DefaultIconOf(Id);
protected string? UniqueName { get; set; }
public new string Name => UniqueName ?? ItemTypeManager.DefaultNameOf(Id);
protected string? UniqueDescription { get; set; }
public string? Description => UniqueDescription ?? ItemTypeManager.DefaultDescriptionOf(Id);
public void Use(Node2D? owner, Vector2 targetGlobalPosition)
{
Fire(owner, targetGlobalPosition);
}
public virtual void Destroy()
{
QueueFree();
}
public bool CanStackWith(IItem item) => false;
///
/// Whether the weapon is currently picked up
/// 当前武器是否被捡起了
///
public bool Picked { get; set; }
///
/// Owner
/// 主人
///
public new Node2D? Owner { get; set; }
///
/// Enabled contact injury
/// 启用接触伤害
///
public bool EnableContactInjury;
[Export] private int _minContactInjury = 1;
[Export] private int _maxContactInjury = 2;
private DateTime? _lastFiringTime;
///
/// Firing interval
/// 开火间隔
///
private TimeSpan _firingInterval;
private long _firingIntervalAsMillisecond = 100;
[Export] protected long FiringIntervalAsMillisecond
{
get => _firingIntervalAsMillisecond;
set
{
_firingIntervalAsMillisecond = value;
_firingInterval = TimeSpan.FromMilliseconds(_firingIntervalAsMillisecond);
}
}
///
/// The recoil of the weapon
/// 武器的后坐力
///
///
///When the weapon is fired, how much recoil is applied to the user, in units: the number of cells, and the X direction of the force is automatically inferred.
///武器开火,要对使用者施加多大的后坐力,单位:格数,力的X方向是自动推断的。
///
[Export] private Vector2 _recoil;
///
/// This area represents the collision range of the weapon, and when other nodes enter this area, they will deal damage.
/// 这个区域表示武器的碰撞范围,当其他节点进入此区域时,会造成伤害。
///
private Area2D? _damageArea2D;
///
/// The number of tile maps in contact with this weapon
/// 与此武器接触的瓦片地图数量
///
private int _tileMapNumber;
public override void _Ready()
{
_damageArea2D = GetNode("DamageArea2D");
_damageArea2D.BodyEntered += OnBodyEnter;
_damageArea2D.BodyExited += OnBodyExited;
}
private void OnBodyExited(Node node)
{
if (Picked)
{
return;
}
//If it leaves the ground or walls.
//如果离开了地面或墙壁。
if (node is TileMap)
{
_tileMapNumber--;
if (_tileMapNumber == 0)
{
//No longer in contact with any shingles can cause injury
//不再与任何瓦片接触后,可以造成伤害
EnableContactInjury = true;
SetCollisionMaskValue(Config.LayerNumber.Player, false);
}
}
}
///
/// Use weapons against the enemy
/// 使用武器砸敌人
///
///
private void OnBodyEnter(Node node)
{
if (Picked)
{
return;
}
if (node is TileMap)
{
_tileMapNumber++;
EnableContactInjury = false;
//Items can be pushed by the player when they are on the ground
//当物品在地面上时,可被玩家推动
SetCollisionMaskValue(Config.LayerNumber.Player, true);
}
else if (node is CharacterTemplate characterTemplate)
{
if (!EnableContactInjury)
{
return;
}
if (Owner is not CharacterTemplate ownerCharacterTemplate)
{
return;
}
//Determine if your side can cause damage
//判断所属的阵营是否可以造成伤害
var canCauseHarm = CampManager.CanCauseHarm(CampManager.GetCamp(ownerCharacterTemplate.CampId),
CampManager.GetCamp(characterTemplate.CampId));
if (!canCauseHarm)
{
return;
}
//If allowed to cause harm
//如果允许造成伤害
var damage = new Damage
{
MaxDamage = Math.Abs(_maxContactInjury),
MinDamage = Math.Abs(_minContactInjury),
Attacker = ownerCharacterTemplate
};
damage.CreateDamage();
damage.MoveLeft = LinearVelocity.X < 0;
damage.Type = Config.DamageType.Physical;
characterTemplate.Damage(damage);
//Reduce speed after hitting enemies.
//击中敌人后减少速度。
LinearVelocity *= 1 - Config.ThrownItemsHitEnemiesReduceSpeedByPercentage;
}
}
///
/// 翻转武器
///
///
public void Flip(bool facingLeft) { }
///
/// Discharge of the weapon
/// 武器开火
///
///
///
///owner
///武器所有者
///
///
///enemyGlobalPosition
///敌人所在位置
///
///
public void Fire(Node2D? owner, Vector2 enemyGlobalPosition)
{
var nowTime = DateTime.Now;
//If the present time minus the time of the last fire is less than the interval between fires, it means that the fire cannot be fired yet.
//如果现在时间减去上次开火时间小于开火间隔,说明还不能开火。
if (_lastFiringTime != null && nowTime - _lastFiringTime < _firingInterval)
{
return;
}
if (owner is CharacterTemplate characterTemplate)
{
//We check the recoil of the weapon before each firing.
//我们在每次开火之前,检查武器的后坐力。
if (_recoil != Vector2.Zero)
{
var force = new Vector2();
var forceX = Math.Abs(_recoil.X);
if (Math.Abs(RotationDegrees) < 90)
{
//The weapon goes to the right and we apply a recoil to the left
//武器朝向右边我们向左施加后坐力
forceX = -forceX;
}
force.X = forceX * Config.CellSize;
force.Y = _recoil.Y * Config.CellSize;
characterTemplate.AddForce(force);
}
}
DoFire(owner, enemyGlobalPosition);
_lastFiringTime = nowTime;
}
///
/// Execute fire
/// 执行开火
///
protected abstract void DoFire(Node2D? owner, Vector2 enemyGlobalPosition);
}