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