using System.Linq; using Cirno.Scripts.Resources; using Godot; using Godot.Collections; namespace Cirno.Scripts.Components.Actors; public partial class DamageReceiverActorModule : ActorModule, IHittable { protected Actor _actor; [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 Array DamageResistances { get; set; } = []; public override void Init(Actor actor) { _actor = actor; this.HealthProvider.MaxResource = actor.Health; HealthProvider.FillResource(); HealthProvider.ResourceDepleted += OnDeath; } public override void Update(double delta) { } public override void PhysicsUpdate(double delta) { } private void _on_damage_hitbox_area_entered(Area2D area) { if (_actor.IsDestroyed) return; if (Invulnerable) return; if (area is not Bullet bullet) return; if (BulletGroup is BulletOwner.None) { this.Hit(bullet.Damage, bullet.DamageType); // If this hit killed the actor, attribute XP to the source weapon if present if (HealthProvider.CurrentResource <= 0 && bullet.BulletInfo?.SourceWeapon != null) { // Award XP equal to actor's MotivationReward rounded to int, or a fixed value. var xp = (int)System.Math.Round(_actor.EnemyData?.MotivationReward ?? 0f); bullet.BulletInfo.SourceWeapon.GainExperience(xp); } bullet.RequestCollisionDestruction(); return; } if (bullet.BulletInfo.Owner == BulletGroup) return; this.Hit(bullet.Damage, bullet.DamageType); // Attribute XP on lethal hit if (HealthProvider.CurrentResource <= 0 && bullet.BulletInfo?.SourceWeapon != null) { var xp = (int)System.Math.Round(_actor.EnemyData?.MotivationReward ?? 0f); bullet.BulletInfo.SourceWeapon.GainExperience(xp); } bullet.RequestCollisionDestruction(); } public void Hit(float damage, DamageType damageType = DamageType.Neutral) { if (_actor.IsDestroyed) return; if (Invulnerable) return; var dmg = DamageResistances.Aggregate(damage, (current, resistance) => current * resistance.CalculateDamage(current, damageType)); HealthProvider.CurrentResource -= dmg; } protected void OnDeath() { _actor.IsDestroyed = true; _actor.TriggerDeath(); } }