using System.Linq; using Cirno.Scripts.Enums; using Cirno.Scripts.Resources; using Godot; using Godot.Collections; namespace Cirno.Scripts.Components.Actors; public partial class GenericDamageReceiver : Area2D, IHittable { [Export] public ActorResourceProvider HealthProvider { get; private set; } [Export] public bool Invulnerable { get; private set; } = false; [Export] public BulletOwner BulletGroup { get; set; } = BulletOwner.None; [Export] public PackedScene Debris { get; set; } [Export] public Array DamageResistances { get; set; } = []; [Export] public bool DeleteParentOnDeath { get; private set; } = true; [Signal] public delegate void ShieldHitEventHandler(); [Signal] public delegate void BulletHitEventHandler(Bullet bullet, Vector2 position, Vector2 direction); //[Signal] public delegate void DeathEventHandler(); private Node2D _parent; public bool Enabled { get; private set; } = true; public override void _Ready() { _parent = GetParent(); HealthProvider.FillResource(); HealthProvider.ResourceDepleted += OnDeath; } public void ChangeState(bool enabled) { Enabled = enabled; } private void _on_damage_hitbox_area_entered(Area2D area) { if (!Enabled) return; if (area is not Bullet bullet) return; if (Invulnerable) { EmitSignalShieldHit(); return; }; if (BulletGroup is BulletOwner.None) { this.Hit(bullet.Damage, bullet.DamageType); EmitSignalBulletHit(bullet, area.GlobalPosition, (this.GlobalPosition - area.GlobalPosition).Normalized()); bullet.RequestCollisionDestruction(); return; } if (bullet.BulletInfo.Owner == BulletGroup) return; this.Hit(bullet.Damage, bullet.DamageType); EmitSignalBulletHit(bullet, area.GlobalPosition, (this.GlobalPosition - area.GlobalPosition).Normalized()); bullet.RequestCollisionDestruction(); } public void Hit(float damage, DamageType damageType = DamageType.Neutral) { if (!Enabled) return; if (Invulnerable) return; // Change value based on difficulty float difficultyReducedDmg = damage * GlobalState.Instance.SessionSettings.DifficultyDamageMultiplier; var dmg = DamageResistances.Aggregate(difficultyReducedDmg, (current, resistance) => current * resistance.CalculateDamage(current, damageType)); HealthProvider.CurrentResource -= dmg; } private void OnDeath() { if (Debris is not null) { _parent.CreateSibling(Debris); } // Not needed because the health provider is accessible //EmitSignal(SignalName.Death); if (DeleteParentOnDeath) { _parent.QueueFree(); } } }