using System; using UnityEngine; namespace Core.Health { /// /// Damageable class for handling health using events /// Could be used on Players or enemies or even destructable world objects /// [Serializable] public class Damageable { /// /// The max health of this instance /// public float maxHealth; public float startingHealth; /// /// The alignment of the damager /// public SerializableIAlignmentProvider alignment; // events public event Action reachedMaxHealth; // 当前的Entity用于响应各种血量相关的事件。 public event Action damaged, healed, died, healthChanged; /// /// 魔法护盾值改变 /// public event Action ShieldWallHealthChanged; /// /// Gets the current health. /// public float currentHealth { protected set; get; } /// /// 设置无敌状态. /// public bool bInvincible { get; set; } /// /// 是否存在魔法护盾 /// public bool IsExistShieldWall { get; set; } /// /// 0 -> 和agent的生命周期一致,直到死亡, > 0 (单位s)*时间内生效 /// public float ShieldWallEffectiveTime { get; set; } /// /// 魔法护盾生效剩余时间,这个只针对 ShieldWallEffectTime > 0 才生效 /// public float ShieldWallRemainTime { get; set; } /// /// 魔法护盾最大生命值,即初始生命值 /// public float ShieldWallMaxHealth { get; set; } /// /// 魔法护盾当前生命值,当 <= 0时直接移除护盾效果 /// public float ShieldWallCurrentHealth { get; set; } /// /// Gets the normalised health. /// public float normalisedHealth { get { if (Math.Abs(maxHealth) <= Mathf.Epsilon) { Debug.LogError("Max Health is 0"); maxHealth = 1f; } return currentHealth / maxHealth; } } /// /// 清理魔法护盾 /// public void ClearShieldWall() { IsExistShieldWall = false; ShieldWallMaxHealth = 0; ShieldWallCurrentHealth = 0; ShieldWallRemainTime = 0; ShieldWallEffectiveTime = 0; } /// /// Gets the of this instance /// public IAlignmentProvider alignmentProvider { get { return alignment != null ? alignment.GetInterface() : null; } } /// /// Gets whether this instance is dead. /// public bool isDead { get { return currentHealth < 0.00001f; } } /// /// Gets whether this instance is at max health. /// public bool isAtMaxHealth { get { return Mathf.Approximately(currentHealth, maxHealth); } } /// /// Init this instance /// public virtual void Init() { currentHealth = startingHealth; } /// /// Sets the max health and starting health to the same value /// public void SetMaxHealth(float health) { if (health <= 0) { return; } maxHealth = startingHealth = health; } /// /// Sets the max health and starting health separately /// public void SetMaxHealth(float health, float startingHealth) { if (health <= 0) { return; } maxHealth = health; this.startingHealth = startingHealth; } /// /// Sets this instance's health directly. /// /// /// The value to set to /// public void SetHealth(float health) { var info = new HealthChangeInfo { damageable = this, newHealth = health, oldHealth = currentHealth }; currentHealth = health; info.oldHealth = health; if (healthChanged != null) { healthChanged(info); } } /// /// 获得一个魔法护盾 /// /// 护盾的最大生命值 /// 生效时间 0 -> 和Agent生命周期一致,> 0 *(单位ms)时间内生效 public void AddShieldWall(float maxHealth, float effectiveTime) { // 如果重复获得护盾,直接全部重置数据,移除之前的护盾效果,重新获得一个护盾 IsExistShieldWall = true; ShieldWallEffectiveTime = effectiveTime; if (effectiveTime > 0) ShieldWallRemainTime = effectiveTime / 1000f; ShieldWallMaxHealth = ShieldWallCurrentHealth = maxHealth; HealthChangeInfo info = new HealthChangeInfo { damageable = this, ShieldWallMaxHealth = maxHealth, ShieldWallCurrentHealth = maxHealth, IsExistShieldWall = true }; if (ShieldWallHealthChanged != null) { ShieldWallHealthChanged(info); } } /// /// Use the alignment to see if taking damage is a valid action /// /// /// The damage to take /// /// /// The alignment of the other combatant /// /// /// The output data if there is damage taken /// /// /// true if this instance took damage /// false if this instance was already dead, or the alignment did not allow the damage /// public bool TakeDamage(float damage, IAlignmentProvider damageAlignment, ref HealthChangeInfo output) { /*output = new HealthChangeInfo { damageAlignment = damageAlignment, damageable = this, newHealth = currentHealth, oldHealth = currentHealth };*/ output.damageAlignment = damageAlignment; output.damageable = this; output.newHealth = currentHealth; output.oldHealth = currentHealth; // 无敌状态下,不可伤害. if (this.bInvincible) return false; bool canDamage = damageAlignment == null || alignmentProvider == null || damageAlignment.CanHarm(alignmentProvider); if (isDead || !canDamage) { return false; } ChangeHealth(-damage, ref output); SafelyDoAction(damaged, output); if (isDead) { SafelyDoAction(died, output); } return true; } /// /// Logic for increasing the health. /// /// Health. public HealthChangeInfo IncreaseHealth(float health) { var info = new HealthChangeInfo { damageable = this }; ChangeHealth(health, ref info); SafelyDoAction(healed, info); if (isAtMaxHealth) { SafelyDoAction(reachedMaxHealth); } return info; } /// /// Changes the health. /// /// Health increment. /// HealthChangeInfo for this change protected void ChangeHealth(float healthIncrement, ref HealthChangeInfo info) { bool changeHealth = false; float increment = healthIncrement; if (IsExistShieldWall) { info.ShieldWallOldHealth = ShieldWallCurrentHealth; ShieldWallCurrentHealth += healthIncrement; if (ShieldWallCurrentHealth == 0) info.IsExistShieldWall = IsExistShieldWall = false; else if (ShieldWallCurrentHealth < 0) { changeHealth = true; info.IsExistShieldWall = IsExistShieldWall = false; // 护盾血量不够减 increment = ShieldWallCurrentHealth; info.ShieldWallCurrentHealth = ShieldWallCurrentHealth = 0; } } else changeHealth = true; if (changeHealth) { info.oldHealth = currentHealth; currentHealth += increment; currentHealth = Mathf.Clamp(currentHealth, 0f, maxHealth); info.newHealth = currentHealth; if (healthChanged != null) { healthChanged(info); } } } /// /// A helper method for null checking actions /// /// Action to be done protected void SafelyDoAction(Action action) { if (action != null) { action(); } } /// /// A helper method for null checking actions /// /// Action to be done /// The HealthChangeInfo to be passed to the Action protected void SafelyDoAction(Action action, HealthChangeInfo info) { if (action != null) { action(info); } } } }