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);
}
}
}
}