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); }