using System;
|
using UnityEngine;
|
|
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>
|
/// The alignment of the damager
|
/// </summary>
|
public SerializableIAlignmentProvider alignment;
|
|
// 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 < 0.00001f; }
|
}
|
|
/// <summary>
|
/// Gets whether this instance is at max health.
|
/// </summary>
|
public bool isAtMaxHealth
|
{
|
get { return Mathf.Approximately(currentHealth, maxHealth); }
|
}
|
|
|
/// <summary>
|
/// Init this instance
|
/// </summary>
|
public virtual void Init()
|
{
|
currentHealth = startingHealth;
|
}
|
|
/// <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>
|
/// 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>
|
/// 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 (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;
|
|
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);
|
}
|
}
|
|
/// <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
|
{
|
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;
|
}
|
|
/// <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);
|
}
|
|
return info;
|
}
|
|
/// <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;
|
|
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);
|
}
|
}
|
}
|
}
|